diff options
Diffstat (limited to 'Radio/HW/BladeRF/src/board')
21 files changed, 13555 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], ×tamp, sizeof(timestamp)); + i += sizeof(timestamp); + + memcpy(&buf[i], &img->serial, BLADERF_SERIAL_LENGTH); + i += BLADERF_SERIAL_LENGTH; + + memset(&buf[i], 0, BLADERF_IMAGE_RESERVED_LEN); + i += BLADERF_IMAGE_RESERVED_LEN; + + type = HOST_TO_BE32((uint32_t)img->type); + memcpy(&buf[i], &type, sizeof(type)); + i += sizeof(type); + + addr = HOST_TO_BE32(img->address); + memcpy(&buf[i], &addr, sizeof(addr)); + i += sizeof(addr); + + len = HOST_TO_BE32(img->length); + memcpy(&buf[i], &len, sizeof(len)); + i += sizeof(len); + + memcpy(&buf[i], img->data, img->length); + i += img->length; + + sha256_buffer((const char *)buf, i, checksum); + memcpy(&buf[BLADERF_IMAGE_MAGIC_LEN], checksum, BLADERF_IMAGE_CHECKSUM_LEN); + + return i; +} + +/* Unpack flash image from file and validate fields */ +static int unpack_image(struct bladerf_image *img, uint8_t *buf, size_t len) +{ + size_t i = 0; + uint32_t type; + + /* Ensure we have at least a full set of metadata */ + if (len < CALC_IMAGE_SIZE(0)) { + return BLADERF_ERR_INVAL; + } + + memcpy(img->magic, &buf[i], BLADERF_IMAGE_MAGIC_LEN); + img->magic[BLADERF_IMAGE_MAGIC_LEN] = '\0'; + if (strncmp(img->magic, image_magic, BLADERF_IMAGE_MAGIC_LEN)) { + return BLADERF_ERR_INVAL; + } + i += BLADERF_IMAGE_MAGIC_LEN; + + memcpy(img->checksum, &buf[i], BLADERF_IMAGE_CHECKSUM_LEN); + i += BLADERF_IMAGE_CHECKSUM_LEN; + + memcpy(&img->version.major, &buf[i], sizeof(img->version.major)); + i += sizeof(img->version.major); + img->version.major = BE16_TO_HOST(img->version.major); + + memcpy(&img->version.minor, &buf[i], sizeof(img->version.minor)); + i += sizeof(img->version.minor); + img->version.minor = BE16_TO_HOST(img->version.minor); + + memcpy(&img->version.patch, &buf[i], sizeof(img->version.patch)); + i += sizeof(img->version.patch); + img->version.patch = BE16_TO_HOST(img->version.patch); + + memcpy(&img->timestamp, &buf[i], sizeof(img->timestamp)); + i += sizeof(img->timestamp); + img->timestamp = BE64_TO_HOST(img->timestamp); + + memcpy(img->serial, &buf[i], BLADERF_SERIAL_LENGTH); + img->serial[BLADERF_SERIAL_LENGTH] = '\0'; + i += BLADERF_SERIAL_LENGTH; + + memcpy(img->reserved, &buf[i], BLADERF_IMAGE_RESERVED_LEN); + i += BLADERF_IMAGE_RESERVED_LEN; + + memcpy(&type, &buf[i], sizeof(type)); + i += sizeof(type); + type = BE32_TO_HOST(type); + + if (!image_type_is_valid((bladerf_image_type)type)) { + log_debug("Invalid type value in image: %d\n", (int)type); + return BLADERF_ERR_INVAL; + } else { + img->type = (bladerf_image_type)type; + } + + memcpy(&img->address, &buf[i], sizeof(img->address)); + i += sizeof(img->address); + img->address = BE32_TO_HOST(img->address); + + memcpy(&img->length, &buf[i], sizeof(img->length)); + i += sizeof(img->length); + img->length = BE32_TO_HOST(img->length); + + if (len != CALC_IMAGE_SIZE(img->length)) { + log_debug("Image contains more or less data than expected\n"); + return BLADERF_ERR_INVAL; + } + + /* Just slide the data over */ + memmove(&buf[0], &buf[i], img->length); + img->data = buf; + + return 0; +} + +int bladerf_image_print_metadata(const struct bladerf_image *image) { + if (!image) { + return BLADERF_ERR_MEM; + } + + printf("Magic: %s\n", image->magic); + printf("Type: %s\n", bladerf_image_type_to_string(image->type)); + printf("Version: %d.%d.%d\n", + image->version.major, image->version.minor, image->version.patch); + printf("Timestamp: %" PRIx64 "\n", image->timestamp); + printf("Serial: %s\n", image->serial); + printf("Address: %x\n", image->address); + printf("Length: %u\n", image->length); + fflush(stdout); + + return 0; +} + +const char* bladerf_image_type_to_string(bladerf_image_type type) { + switch (type) { + case BLADERF_IMAGE_TYPE_INVALID: + return "Invalid"; + case BLADERF_IMAGE_TYPE_RAW: + return "Raw Data"; + case BLADERF_IMAGE_TYPE_FIRMWARE: + return "Firmware"; + case BLADERF_IMAGE_TYPE_FPGA_40KLE: + return "FPGA 40 KLE Bitstream"; + case BLADERF_IMAGE_TYPE_FPGA_115KLE: + return "FPGA 115 KLE Bitstream"; + case BLADERF_IMAGE_TYPE_FPGA_A4: + return "FPGA A4 Bitstream"; + case BLADERF_IMAGE_TYPE_FPGA_A9: + return "FPGA A9 Bitstream"; + case BLADERF_IMAGE_TYPE_CALIBRATION: + return "Board Calibration"; + case BLADERF_IMAGE_TYPE_RX_DC_CAL: + return "RX DC Offset Calibration Table"; + case BLADERF_IMAGE_TYPE_TX_DC_CAL: + return "TX DC Offset Calibration Table"; + case BLADERF_IMAGE_TYPE_RX_IQ_CAL: + return "RX IQ Balance Calibration Table"; + case BLADERF_IMAGE_TYPE_TX_IQ_CAL: + return "TX IQ Balance Calibration Table"; + case BLADERF_IMAGE_TYPE_FPGA_A5: + return "FPGA A5 Bitstream"; + case BLADERF_IMAGE_TYPE_GAIN_CAL: + return "Gain Calibration"; + default: + return "Unknown Type"; + } +} + +int bladerf_image_write(struct bladerf *dev, + struct bladerf_image *img, const char *file) +{ + int rv; + FILE *f = NULL; + uint8_t *buf = NULL; + size_t buf_len; + + /* Ensure the format identifier is correct */ + if (memcmp(img->magic, image_magic, BLADERF_IMAGE_MAGIC_LEN) != 0) { +#ifdef LOGGING_ENABLED + char badmagic[BLADERF_IMAGE_MAGIC_LEN + 1]; + memset(badmagic, 0, sizeof(badmagic)); + memcpy(&badmagic, &img->magic, BLADERF_IMAGE_MAGIC_LEN); + log_debug("Invalid file format magic value: %s\n", badmagic); +#endif + return BLADERF_ERR_INVAL; + } + + /* Check for a valid image type */ + if (!image_type_is_valid(img->type)) { + log_debug("Invalid image type: %d\n", img->type); + return BLADERF_ERR_INVAL; + } + + /* Just to be tiny bit paranoid... */ + if (!img->data) { + log_debug("Image data pointer is NULL\n"); + return BLADERF_ERR_INVAL; + } + + buf_len = CALC_IMAGE_SIZE(img->length); + buf = (uint8_t *)calloc(1, buf_len); + if (!buf) { + log_verbose("calloc failed: %s\n", strerror(errno)); + return BLADERF_ERR_MEM; + } + + /* If the type is RAW, we should only allow erase-block aligned + * addresses and lengths */ + if (img->type == BLADERF_IMAGE_TYPE_RAW && img->address != 0xffffffff) { + if (img->address % dev->flash_arch->ebsize_bytes != 0) { + log_debug("Image address must be erase block-aligned for RAW.\n"); + rv = BLADERF_ERR_INVAL; + goto error; + } else if (img->length % dev->flash_arch->ebsize_bytes != 0) { + log_debug("Image length must be erase block-aligned for RAW.\n"); + rv = BLADERF_ERR_INVAL; + goto error; + } + } + + pack_image(img, buf); + + f = fopen(file, "wb"); + if (!f) { + if (errno == EACCES) { + rv = BLADERF_ERR_PERMISSION; + } else { + rv = BLADERF_ERR_IO; + } + + log_debug("Failed to open \"%s\": %s\n", file, strerror(errno)); + + goto error; + } + + rv = file_write(f, buf, buf_len); + +error: + if (f) { + fclose(f); + } + free(buf); + return rv; +} + +int bladerf_image_read(struct bladerf_image *img, const char *file) +{ + int rv = -1; + uint8_t *buf = NULL; + size_t buf_len; + + rv = file_read_buffer(file, &buf, &buf_len); + if (rv < 0) { + goto bladerf_image_read_out; + } + + rv = verify_checksum(buf, buf_len); + if (rv < 0) { + goto bladerf_image_read_out; + } + + /* Note: On success, buf->data = buf, with the data memmove'd over. + * Static analysis tools might indicate a false postive leak when + * buf goes out of scope with rv == 0 */ + rv = unpack_image(img, buf, buf_len); + +bladerf_image_read_out: + if (rv != 0) { + free(buf); + } + + return rv; +} + +static inline bool is_page_aligned(struct bladerf *dev, uint32_t val) +{ + return val % dev->flash_arch->psize_bytes == 0; +} + +static inline bool is_valid_addr_len(struct bladerf *dev, + uint32_t addr, uint32_t len) +{ + if (addr >= dev->flash_arch->tsize_bytes) { + return false; + } else if (len > dev->flash_arch->tsize_bytes) { + return false; + } else if ((addr + len) > dev->flash_arch->tsize_bytes) { + return false; + } else { + return true; + } +} + +struct bladerf_image * bladerf_alloc_image(struct bladerf *dev, + bladerf_image_type type, + uint32_t address, + uint32_t length) +{ + struct bladerf_image *image; + + assert(BLADERF_IMAGE_MAGIC_LEN == (sizeof(image_magic) - 1)); + + /* 0xffffffff is a placeholder for images that use the format but don't + * currently have an address in flash to live in */ + if (address != 0xffffffff) { + if (!is_page_aligned(dev, address)) { + log_debug("Address is not page-aligned: 0x%08x\n", address); + return NULL; + } else if (!is_page_aligned(dev, length)) { + log_debug("Length is not page-aligned: 0x%08x\n", length); + return NULL; + } else if (!is_valid_addr_len(dev, address, length)) { + log_debug("Invalid address=0x%08x or length=0x%08x\n", address, length); + return NULL; + } + } + + image = (struct bladerf_image *)calloc(1, sizeof(*image)); + + if (!image) { + return NULL; + } + + if (length) { + image->data = (uint8_t *)calloc(1, length); + if (!image->data) { + free(image); + return NULL; + } + } + + memcpy(image->magic, &image_magic, BLADERF_IMAGE_MAGIC_LEN); + + image->version.major = 0; + image->version.minor = 1; + image->version.patch = 0; + image->timestamp = get_timestamp(); + image->address = address; + image->length = length; + image->type = type; + + return image; +} + +static int make_cal_region(bladerf_fpga_size size, uint16_t vctcxo_trim, + uint8_t *buf, size_t len) +{ + int rv; + static const char fpga_size_40[] = "40"; + static const char fpga_size_115[] = "115"; + static const char fpga_size_A4[] = "A4"; + static const char fpga_size_A5[] = "A5"; + static const char fpga_size_A9[] = "A9"; + const char *fpga_size; + char dac[7] = {0}; + + if (size == BLADERF_FPGA_40KLE) { + fpga_size = fpga_size_40; + } else if (size == BLADERF_FPGA_115KLE) { + fpga_size = fpga_size_115; + } else if (size == BLADERF_FPGA_A4) { + fpga_size = fpga_size_A4; + } else if (size == BLADERF_FPGA_A5) { + fpga_size = fpga_size_A5; + } else if (size == BLADERF_FPGA_A9) { + fpga_size = fpga_size_A9; + } else { + assert(0); /* Bug catcher */ + return BLADERF_ERR_INVAL; + } + + memset(buf, 0xff, len); + + assert(len < INT_MAX); + rv = binkv_add_field((char*)buf, (int)len, "B", fpga_size); + + if (rv < 0) { + return rv; + } + + sprintf(dac, "%u", vctcxo_trim); + + rv = binkv_add_field((char*)buf, (int)len, "DAC", dac); + if (rv < 0) { + return rv; + } + + return 0; +} + +struct bladerf_image * bladerf_alloc_cal_image(struct bladerf *dev, + bladerf_fpga_size fpga_size, + uint16_t vctcxo_trim) +{ + struct bladerf_image *image; + int status; + + image = bladerf_alloc_image(dev, + BLADERF_IMAGE_TYPE_CALIBRATION, + BLADERF_FLASH_ADDR_CAL, + BLADERF_FLASH_BYTE_LEN_CAL); + + if (!image) { + return NULL; + } + + status = make_cal_region(fpga_size, vctcxo_trim, + image->data, image->length); + + if (status != 0) { + bladerf_free_image(image); + image = NULL; + } + + return image; +} + +void bladerf_free_image(struct bladerf_image *image) +{ + if (image) { + free(image->data); + free(image); + } +} diff --git a/Radio/HW/BladeRF/src/board/bladerf2/bladerf2.c b/Radio/HW/BladeRF/src/board/bladerf2/bladerf2.c new file mode 100644 index 0000000..8d56149 --- /dev/null +++ b/Radio/HW/BladeRF/src/board/bladerf2/bladerf2.c @@ -0,0 +1,3744 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2017-2018 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> + +#include "libbladeRF.h" + +#include "bladeRF.h" +#include "host_config.h" + +#include "log.h" +#define LOGGER_ID_STRING +#include "logger_id.h" +#include "rel_assert.h" + +#include "../bladerf1/flash.h" +#include "board/board.h" +#include "capabilities.h" +#include "compatibility.h" + +#include "ad936x.h" +#include "ad936x_helpers.h" + +#include "driver/fpga_trigger.h" +#include "driver/fx3_fw.h" +#include "driver/ina219.h" +#include "driver/spi_flash.h" + +#include "backend/backend_config.h" +#include "backend/usb/usb.h" + +#include "streaming/async.h" +#include "streaming/sync.h" + +#include "conversions.h" +#include "devinfo.h" +#include "helpers/file.h" +#include "helpers/version.h" +#include "helpers/wallclock.h" +#include "iterators.h" +#include "version.h" + +#include "bladerf2_common.h" +#include "common.h" + + +/******************************************************************************/ +/* Forward declarations */ +/******************************************************************************/ + +static int bladerf2_read_flash_vctcxo_trim(struct bladerf *dev, uint16_t *trim); + + +/******************************************************************************/ +/* Constants */ +/******************************************************************************/ + +// clang-format off + +/* REFIN frequency range */ +static struct bladerf_range const bladerf2_pll_refclk_range = { + FIELD_INIT(.min, 5000000), + FIELD_INIT(.max, 300000000), + FIELD_INIT(.step, 1), + FIELD_INIT(.scale, 1), +}; + +/* Loopback modes */ +static struct bladerf_loopback_modes const bladerf2_loopback_modes[] = { + { + FIELD_INIT(.name, "none"), + FIELD_INIT(.mode, BLADERF_LB_NONE) + }, + { + FIELD_INIT(.name, "firmware"), + FIELD_INIT(.mode, BLADERF_LB_FIRMWARE) + }, + { + FIELD_INIT(.name, "rf_bist"), + FIELD_INIT(.mode, BLADERF_LB_RFIC_BIST) + }, +}; +// clang-format on + + +/******************************************************************************/ +/* Low-level Initialization */ +/******************************************************************************/ + +static int _bladerf2_initialize(struct bladerf *dev) +{ + struct bladerf2_board_data *board_data; + struct bladerf_version required_fw_version, required_fpga_version; + int status; + + /* Test for uninitialized dev struct */ + NULL_CHECK(dev); + NULL_CHECK(dev->board_data); + + /* Initialize board_data struct and members */ + board_data = dev->board_data; + + /* Read FPGA version */ + CHECK_STATUS( + dev->backend->get_fpga_version(dev, &board_data->fpga_version)); + + log_verbose("Read FPGA version: %s\n", board_data->fpga_version.describe); + + /* Determine FPGA capabilities */ + board_data->capabilities |= + bladerf2_get_fpga_capabilities(&board_data->fpga_version); + + log_verbose("Capability mask after FPGA load: 0x%016" PRIx64 "\n", + board_data->capabilities); + + /* If the FPGA version check fails, just warn, but don't error out. + * + * If an error code caused this function to bail out, it would prevent a + * user from being able to unload and reflash a bitstream being + * "autoloaded" from SPI flash. */ + status = + version_check(&bladerf2_fw_compat_table, &bladerf2_fpga_compat_table, + &board_data->fw_version, &board_data->fpga_version, + &required_fw_version, &required_fpga_version); + if (status < 0) { +#if LOGGING_ENABLED + if (BLADERF_ERR_UPDATE_FPGA == status) { + log_warning( + "FPGA v%u.%u.%u was detected. Firmware v%u.%u.%u " + "requires FPGA v%u.%u.%u or later. Please load a " + "different FPGA version before continuing.\n\n", + board_data->fpga_version.major, board_data->fpga_version.minor, + board_data->fpga_version.patch, board_data->fw_version.major, + board_data->fw_version.minor, board_data->fw_version.patch, + required_fpga_version.major, required_fpga_version.minor, + required_fpga_version.patch); + } else if (BLADERF_ERR_UPDATE_FW == status) { + log_warning( + "FPGA v%u.%u.%u was detected, which requires firmware " + "v%u.%u.%u or later. The device firmware is currently " + "v%u.%u.%u. Please upgrade the device firmware before " + "continuing.\n\n", + board_data->fpga_version.major, board_data->fpga_version.minor, + board_data->fpga_version.patch, required_fw_version.major, + required_fw_version.minor, required_fw_version.patch, + board_data->fw_version.major, board_data->fw_version.minor, + board_data->fw_version.patch); + } +#endif + } + + /* Set FPGA packet protocol */ + CHECK_STATUS( + dev->backend->set_fpga_protocol(dev, BACKEND_FPGA_PROTOCOL_NIOSII)); + + /* Initialize INA219 */ + CHECK_STATUS(ina219_init(dev, ina219_r_shunt)); + + /* Set tuning mode. This will trigger initialization of the RFIC. + * + * RFIC initialization consists of: + * - RFFE register initialization + * - RFIC initialization + * - Setting initial frequency + * - Setting up FIR filters + * - Disabling RX and TX on the RFIC + * - Muting the TX + */ + CHECK_STATUS(dev->board->set_tuning_mode(dev, default_tuning_mode(dev))); + + /* Update device state */ + board_data->state = STATE_INITIALIZED; + + /* Initialize VCTCXO trim DAC to stored value */ + uint16_t *trimval = &(board_data->trimdac_stored_value); + + CHECK_STATUS(bladerf2_read_flash_vctcxo_trim(dev, trimval)); + CHECK_STATUS(dev->backend->ad56x1_vctcxo_trim_dac_write(dev, *trimval)); + + board_data->trim_source = TRIM_SOURCE_TRIM_DAC; + + /* Configure PLL */ + CHECK_STATUS(bladerf_set_pll_refclk(dev, BLADERF_REFIN_DEFAULT)); + + /* Reset current quick tune profile number */ + board_data->quick_tune_rx_profile = 0; + board_data->quick_tune_tx_profile = 0; + + log_debug("%s: complete\n", __FUNCTION__); + + return 0; +} + + +/****************************************************************************** + * Generic Board Functions * + ******************************************************************************/ + +/******************************************************************************/ +/* Matches */ +/******************************************************************************/ + +static bool bladerf2_matches(struct bladerf *dev) +{ + NULL_CHECK(dev); + NULL_CHECK(dev->backend); + + uint16_t vid, pid; + int status; + + status = dev->backend->get_vid_pid(dev, &vid, &pid); + if (status < 0) { + log_error("%s: get_vid_pid returned status %s\n", __FUNCTION__, + bladerf_strerror(status)); + return false; + } + + if (USB_NUAND_VENDOR_ID == vid && USB_NUAND_BLADERF2_PRODUCT_ID == pid) { + return true; + } + + return false; +} + + +/******************************************************************************/ +/* Open/close */ +/******************************************************************************/ + +static int bladerf2_open(struct bladerf *dev, struct bladerf_devinfo *devinfo) +{ + NULL_CHECK(dev); + NULL_CHECK(dev->backend); + + struct bladerf2_board_data *board_data; + struct bladerf_version required_fw_version; + char *full_path; + bladerf_dev_speed usb_speed; + size_t i; + int ready, status; + + size_t const MAX_RETRIES = 30; + + /* Allocate board data */ + board_data = calloc(1, sizeof(struct bladerf2_board_data)); + if (NULL == board_data) { + RETURN_ERROR_STATUS("calloc board_data", BLADERF_ERR_MEM); + } + dev->board_data = board_data; + board_data->phy = NULL; + board_data->rfic_init_params = (void *)&bladerf2_rfic_init_params; + + /* Allocate flash architecture */ + dev->flash_arch = calloc(1, sizeof(struct bladerf_flash_arch)); + if (NULL == dev->flash_arch) { + return BLADERF_ERR_MEM; + } + + /* Initialize board data */ + board_data->fpga_version.describe = board_data->fpga_version_str; + board_data->fw_version.describe = board_data->fw_version_str; + + board_data->module_format[BLADERF_RX] = -1; + board_data->module_format[BLADERF_TX] = -1; + + dev->flash_arch->status = STATUS_FLASH_UNINITIALIZED; + dev->flash_arch->manufacturer_id = 0x0; + dev->flash_arch->device_id = 0x0; + + board_data->rfic_reset_on_close = false; + + /* Read firmware version */ + CHECK_STATUS(dev->backend->get_fw_version(dev, &board_data->fw_version)); + + log_verbose("Read Firmware version: %s\n", board_data->fw_version.describe); + + /* Determine firmware capabilities */ + board_data->capabilities |= + bladerf2_get_fw_capabilities(&board_data->fw_version); + + log_verbose("Capability mask before FPGA load: 0x%016" PRIx64 "\n", + board_data->capabilities); + + /* Update device state */ + board_data->state = STATE_FIRMWARE_LOADED; + + /* Wait until firmware is ready */ + for (i = 0; i < MAX_RETRIES; i++) { + ready = dev->backend->is_fw_ready(dev); + if (ready != 1) { + if (0 == i) { + log_info("Waiting for device to become ready...\n"); + } else { + log_debug("Retry %02u/%02u.\n", i + 1, MAX_RETRIES); + } + usleep(1000000); + } else { + break; + } + } + + if (ready != 1) { + RETURN_ERROR_STATUS("is_fw_ready", BLADERF_ERR_TIMEOUT); + } + + /* Determine data message size */ + CHECK_STATUS(dev->backend->get_device_speed(dev, &usb_speed)); + + switch (usb_speed) { + case BLADERF_DEVICE_SPEED_SUPER: + board_data->msg_size = USB_MSG_SIZE_SS; + break; + case BLADERF_DEVICE_SPEED_HIGH: + board_data->msg_size = USB_MSG_SIZE_HS; + break; + default: + log_error("%s: unsupported device speed (%d)\n", __FUNCTION__, + usb_speed); + return BLADERF_ERR_UNSUPPORTED; + } + + /* Verify that we have a sufficent firmware version before continuing. */ + status = version_check_fw(&bladerf2_fw_compat_table, + &board_data->fw_version, &required_fw_version); + if (status != 0) { +#ifdef LOGGING_ENABLED + if (BLADERF_ERR_UPDATE_FW == status) { + log_warning("Firmware v%u.%u.%u was detected. libbladeRF v%s " + "requires firmware v%u.%u.%u or later. An upgrade via " + "the bootloader is required.\n\n", + &board_data->fw_version.major, + &board_data->fw_version.minor, + &board_data->fw_version.patch, LIBBLADERF_VERSION, + required_fw_version.major, required_fw_version.minor, + required_fw_version.patch); + } +#endif + return status; + } + + /* Probe SPI flash architecture information */ + if (have_cap(board_data->capabilities, BLADERF_CAP_FW_FLASH_ID)) { + status = spi_flash_read_flash_id(dev, &dev->flash_arch->manufacturer_id, + &dev->flash_arch->device_id); + if (status < 0) { + log_error("Failed to probe SPI flash ID information.\n"); + } + } else { + log_debug("FX3 firmware v%u.%u.%u does not support SPI flash ID. A " + "firmware update is recommended in order to probe the SPI " + "flash ID information.\n", + board_data->fw_version.major, board_data->fw_version.minor, + board_data->fw_version.patch); + } + + /* Decode SPI flash ID information to figure out its architecture. + * We need to know a little about the flash architecture before we can + * read anything from it, including FPGA size and other cal data. + * If the firmware does not have the capability to get the flash ID, + * sane defaults will be chosen. + * + * Not checking return code because it is irrelevant. */ + spi_flash_decode_flash_architecture(dev, &board_data->fpga_size); + + /* Get FPGA size */ + status = spi_flash_read_fpga_size(dev, &board_data->fpga_size); + if (status < 0) { + log_warning("Failed to get FPGA size %s\n", bladerf_strerror(status)); + } + + if (getenv("BLADERF_FORCE_FPGA_A9")) { + log_info("BLADERF_FORCE_FPGA_A9 is set, assuming A9 FPGA\n"); + board_data->fpga_size = BLADERF_FPGA_A9; + } + + /* If the flash architecture could not be decoded earlier, try again now + * that the FPGA size is known. */ + if (dev->flash_arch->status != STATUS_SUCCESS) { + status = + spi_flash_decode_flash_architecture(dev, &board_data->fpga_size); + if (status < 0) { + log_debug("Assumptions were made about the SPI flash architecture! " + "Flash commands may not function as expected.\n"); + } + } + + /* Skip further work if BLADERF_FORCE_NO_FPGA_PRESENT is set */ + if (getenv("BLADERF_FORCE_NO_FPGA_PRESENT")) { + log_debug("Skipping FPGA configuration and initialization - " + "BLADERF_FORCE_NO_FPGA_PRESENT is set.\n"); + return 0; + } + + /* Check if FPGA is configured */ + status = dev->backend->is_fpga_configured(dev); + if (status < 0) { + RETURN_ERROR_STATUS("is_fpga_configured", status); + } else if (1 == status) { + board_data->state = STATE_FPGA_LOADED; + } else if (status != 1 && BLADERF_FPGA_UNKNOWN == board_data->fpga_size) { + log_warning("Unknown FPGA size. Skipping FPGA configuration...\n"); + log_warning("Skipping further initialization...\n"); + return 0; + } else if (status != 1) { + /* Try searching for an FPGA in the config search path */ + switch (board_data->fpga_size) { + case BLADERF_FPGA_A4: + full_path = file_find("hostedxA4.rbf"); + break; + + case BLADERF_FPGA_A5: + full_path = file_find("hostedxA5.rbf"); + break; + + case BLADERF_FPGA_A9: + full_path = file_find("hostedxA9.rbf"); + break; + + default: + log_error("%s: invalid FPGA size: %d\n", __FUNCTION__, + board_data->fpga_size); + return BLADERF_ERR_UNEXPECTED; + } + + if (full_path != NULL) { + uint8_t *buf; + size_t buf_size; + + log_debug("Loading FPGA from: %s\n", full_path); + + status = file_read_buffer(full_path, &buf, &buf_size); + free(full_path); + full_path = NULL; + + if (status != 0) { + RETURN_ERROR_STATUS("file_read_buffer", status); + } + + CHECK_STATUS(dev->backend->load_fpga(dev, buf, buf_size)); + + board_data->state = STATE_FPGA_LOADED; + } else { + log_warning("FPGA bitstream file not found.\n"); + log_warning("Skipping further initialization...\n"); + return 0; + } + } + + /* Initialize the board */ + CHECK_STATUS(_bladerf2_initialize(dev)); + + if (have_cap(board_data->capabilities, BLADERF_CAP_SCHEDULED_RETUNE)) { + /* Cancel any pending re-tunes that may have been left over as the + * result of a user application crashing or forgetting to call + * bladerf_close() */ + + bladerf_direction dir; + + FOR_EACH_DIRECTION(dir) + { + size_t idx; + bladerf_channel ch; + + FOR_EACH_CHANNEL(dir, 1, idx, ch) + { + CHECK_STATUS(dev->board->cancel_scheduled_retunes(dev, ch)); + } + } + } + + return 0; +} + +static void bladerf2_close(struct bladerf *dev) +{ + if (dev != NULL) { + struct bladerf2_board_data *board_data = dev->board_data; + struct controller_fns const *rfic = board_data->rfic; + struct bladerf_flash_arch *flash_arch = dev->flash_arch; + + if (board_data != NULL) { + bladerf_direction dir; + + FOR_EACH_DIRECTION(dir) + { + size_t idx; + bladerf_channel ch; + + FOR_EACH_CHANNEL(dir, 1, idx, ch) + { + sync_deinit(&board_data->sync[ch]); + + /* Cancel scheduled retunes here to avoid the device + * retuning underneath the user should they open it again in + * the future. + * + * This is intended to help developers avoid a situation + * during debugging where they schedule "far" into the + * future, but hit a case where their program aborts or + * exits early. If we do not cancel these scheduled retunes, + * the device could start up and/or "unexpectedly" switch to + * a different frequency. + */ + if (dev->backend->is_fpga_configured(dev) && + have_cap(board_data->capabilities, + BLADERF_CAP_SCHEDULED_RETUNE)) { + dev->board->cancel_scheduled_retunes(dev, ch); + } + } + } + + if (board_data->state >= STATE_INITIALIZED && rfic != NULL) { + if (board_data->rfic_reset_on_close) { + /* We need to fully de-initialize the RFIC, so it can be + * reset on the next open. This seems to be necessary after + * doing direct SPI control of the RFIC. + */ + rfic->deinitialize(dev); + } else { + /* Put the RFIC into standby mode. This will shut down any + * current RF activity, but it will not lose the RF state. + */ + rfic->standby(dev); + } + } + + free(board_data); + board_data = NULL; + } + + if (flash_arch != NULL) { + free(flash_arch); + flash_arch = NULL; + } + } +} + + +/******************************************************************************/ +/* Properties */ +/******************************************************************************/ + +static bladerf_dev_speed bladerf2_device_speed(struct bladerf *dev) +{ + CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED); + + bladerf_dev_speed usb_speed; + int status; + + status = dev->backend->get_device_speed(dev, &usb_speed); + if (status < 0) { + log_error("%s: get_device_speed failed: %s\n", __FUNCTION__, + bladerf_strerror(status)); + return BLADERF_DEVICE_SPEED_UNKNOWN; + } + + return usb_speed; +} + +static int bladerf2_get_serial(struct bladerf *dev, char *serial) +{ + CHECK_BOARD_STATE(STATE_UNINITIALIZED); + NULL_CHECK(serial); + + // TODO: don't use strcpy + strcpy(serial, dev->ident.serial); + + return 0; +} + +static int bladerf2_get_fpga_size(struct bladerf *dev, bladerf_fpga_size *size) +{ + CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED); + NULL_CHECK(size); + + struct bladerf2_board_data *board_data = dev->board_data; + + *size = board_data->fpga_size; + + return 0; +} + +static int bladerf2_get_fpga_bytes(struct bladerf *dev, size_t *size) +{ + CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED); + NULL_CHECK(size); + + struct bladerf2_board_data *board_data = dev->board_data; + + switch (board_data->fpga_size) { + case BLADERF_FPGA_A4: + *size = 2632660; + break; + + case BLADERF_FPGA_A5: + *size = 4244820; + break; + + case BLADERF_FPGA_A9: + *size = 12858972; + break; + + default: + log_debug("%s: unknown fpga_size: %x\n", board_data->fpga_size); + return BLADERF_ERR_INVAL; + } + + return 0; +} + +static int bladerf2_get_flash_size(struct bladerf *dev, + uint32_t *size, + bool *is_guess) +{ + CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED); + NULL_CHECK(size); + NULL_CHECK(is_guess); + + *size = dev->flash_arch->tsize_bytes; + *is_guess = (dev->flash_arch->status != STATUS_SUCCESS); + + return 0; +} + +static int bladerf2_is_fpga_configured(struct bladerf *dev) +{ + CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED); + + return dev->backend->is_fpga_configured(dev); +} + +static int bladerf2_get_fpga_source(struct bladerf *dev, + bladerf_fpga_source *source) +{ + CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED); + NULL_CHECK(source); + + struct bladerf2_board_data *board_data = dev->board_data; + + if (!have_cap(board_data->capabilities, BLADERF_CAP_FW_FPGA_SOURCE)) { + log_debug("%s: not supported by firmware\n", __FUNCTION__); + *source = BLADERF_FPGA_SOURCE_UNKNOWN; + return BLADERF_ERR_UNSUPPORTED; + } + + *source = dev->backend->get_fpga_source(dev); + + return 0; +} + +static uint64_t bladerf2_get_capabilities(struct bladerf *dev) +{ + CHECK_BOARD_STATE(STATE_UNINITIALIZED); + + struct bladerf2_board_data *board_data = dev->board_data; + + return board_data->capabilities; +} + +static size_t bladerf2_get_channel_count(struct bladerf *dev, + bladerf_direction dir) +{ + return 2; +} + + +/******************************************************************************/ +/* Versions */ +/******************************************************************************/ + +static int bladerf2_get_fpga_version(struct bladerf *dev, + struct bladerf_version *version) +{ + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(version); + + struct bladerf2_board_data *board_data = dev->board_data; + + memcpy(version, &board_data->fpga_version, sizeof(*version)); + + return 0; +} + +static int bladerf2_get_fw_version(struct bladerf *dev, + struct bladerf_version *version) +{ + CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED); + NULL_CHECK(version); + + struct bladerf2_board_data *board_data = dev->board_data; + + memcpy(version, &board_data->fw_version, sizeof(*version)); + + return 0; +} + + +/******************************************************************************/ +/* Enable/disable */ +/******************************************************************************/ + +static int bladerf2_enable_module(struct bladerf *dev, + bladerf_channel ch, + bool enable) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + + struct bladerf2_board_data *board_data = dev->board_data; + + return board_data->rfic->enable_module(dev, ch, enable); +} + + +/******************************************************************************/ +/* Gain */ +/******************************************************************************/ + +static int bladerf2_get_gain_stage_range(struct bladerf *dev, + bladerf_channel ch, + char const *stage, + struct bladerf_range const **range) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(range); + + struct bladerf_gain_range const *ranges = NULL; + bladerf_frequency frequency = 0; + size_t i, ranges_len; + + if (BLADERF_CHANNEL_IS_TX(ch)) { + ranges = bladerf2_tx_gain_ranges; + ranges_len = ARRAY_SIZE(bladerf2_tx_gain_ranges); + } else { + ranges = bladerf2_rx_gain_ranges; + ranges_len = ARRAY_SIZE(bladerf2_rx_gain_ranges); + } + + CHECK_STATUS(dev->board->get_frequency(dev, ch, &frequency)); + + for (i = 0; i < ranges_len; ++i) { + struct bladerf_gain_range const *r = &(ranges[i]); + struct bladerf_range const *rfreq = &(r->frequency); + + // if the frequency range matches, and either: + // both the range name and the stage name are null, or + // neither name is null and the strings match + // then we found our match + if (is_within_range(rfreq, frequency) && + ((NULL == r->name && NULL == stage) || + (r->name != NULL && stage != NULL && + (strcmp(r->name, stage) == 0)))) { + *range = &(r->gain); + return 0; + } + } + + return BLADERF_ERR_INVAL; +} + +static int bladerf2_get_gain_range(struct bladerf *dev, + bladerf_channel ch, + struct bladerf_range const **range) +{ + return dev->board->get_gain_stage_range(dev, ch, NULL, range); +} + +static int bladerf2_get_gain_modes(struct bladerf *dev, + bladerf_channel ch, + struct bladerf_gain_modes const **modes) +{ + struct bladerf_gain_modes const *mode_infos; + unsigned int mode_infos_len; + + if (BLADERF_CHANNEL_IS_TX(ch)) { + mode_infos = NULL; + mode_infos_len = 0; + } else { + mode_infos = bladerf2_rx_gain_modes; + mode_infos_len = ARRAY_SIZE(bladerf2_rx_gain_modes); + } + + if (modes != NULL) { + *modes = mode_infos; + } + + return mode_infos_len; +} + +static int bladerf2_get_gain_stages(struct bladerf *dev, + bladerf_channel ch, + char const **stages, + size_t count) +{ + NULL_CHECK(dev); + + struct bladerf_gain_range const *ranges = NULL; + char const **names = NULL; + size_t stage_count = 0; + unsigned int ranges_len; + size_t i, j; + + IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, { + *stages = NULL; + return 0; + }); + + if (BLADERF_CHANNEL_IS_TX(ch)) { + ranges = bladerf2_tx_gain_ranges; + ranges_len = ARRAY_SIZE(bladerf2_tx_gain_ranges); + } else { + ranges = bladerf2_rx_gain_ranges; + ranges_len = ARRAY_SIZE(bladerf2_rx_gain_ranges); + } + + names = calloc(ranges_len + 1, sizeof(char *)); + if (NULL == names) { + RETURN_ERROR_STATUS("calloc names", BLADERF_ERR_MEM); + } + + // Iterate through all the ranges... + for (i = 0; i < ranges_len; ++i) { + struct bladerf_gain_range const *range = &(ranges[i]); + + if (NULL == range->name) { + // this is system gain, skip it + continue; + } + + // loop through the output array to make sure we record this one + for (j = 0; j < ranges_len; ++j) { + if (NULL == names[j]) { + // Made it to the end of names without finding a match + names[j] = range->name; + ++stage_count; + break; + } else if (strcmp(range->name, names[j]) == 0) { + // found a match, break + break; + } + } + } + + if (NULL != stages && 0 != count) { + count = (stage_count < count) ? stage_count : count; + + for (i = 0; i < count; i++) { + stages[i] = names[i]; + } + } + + free((char **)names); + return (int)stage_count; +} + +static int bladerf2_get_gain_mode(struct bladerf *dev, + bladerf_channel ch, + bladerf_gain_mode *mode) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(mode); + + struct bladerf2_board_data *board_data = dev->board_data; + + return board_data->rfic->get_gain_mode(dev, ch, mode); +} + +static int bladerf2_set_gain_mode(struct bladerf *dev, + bladerf_channel ch, + bladerf_gain_mode mode) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + + struct bladerf2_board_data *board_data = dev->board_data; + + return board_data->rfic->set_gain_mode(dev, ch, mode); +} + +static int bladerf2_get_gain(struct bladerf *dev, bladerf_channel ch, int *gain) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(gain); + + struct bladerf2_board_data *board_data = dev->board_data; + + return board_data->rfic->get_gain(dev, ch, gain); +} + +static int bladerf2_set_gain(struct bladerf *dev, bladerf_channel ch, int gain) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + + struct bladerf2_board_data *board_data = dev->board_data; + struct bladerf_range const *range = NULL; + + CHECK_STATUS(dev->board->get_gain_range(dev, ch, &range)); + + return board_data->rfic->set_gain(dev, ch, clamp_to_range(range, gain)); +} + +static int bladerf2_get_gain_stage(struct bladerf *dev, + bladerf_channel ch, + char const *stage, + int *gain) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(stage); + NULL_CHECK(gain); + + struct bladerf2_board_data *board_data = dev->board_data; + + return board_data->rfic->get_gain_stage(dev, ch, stage, gain); +} + +static int bladerf2_set_gain_stage(struct bladerf *dev, + bladerf_channel ch, + char const *stage, + int gain) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(stage); + + struct bladerf2_board_data *board_data = dev->board_data; + struct bladerf_range const *range = NULL; + + CHECK_STATUS(dev->board->get_gain_stage_range(dev, ch, stage, &range)); + + return board_data->rfic->set_gain_stage(dev, ch, stage, + clamp_to_range(range, gain)); +} + + +/******************************************************************************/ +/* Sample Rate */ +/******************************************************************************/ + +static int bladerf2_get_sample_rate_range(struct bladerf *dev, + bladerf_channel ch, + struct bladerf_range const **range) +{ + NULL_CHECK(range); + + *range = &bladerf2_sample_rate_range_base; + + if (dev->feature == BLADERF_FEATURE_OVERSAMPLE) { + *range = &bladerf2_sample_rate_range_oversample; + } + + return 0; +} + +static int bladerf2_get_rational_sample_rate(struct bladerf *dev, + bladerf_channel ch, + struct bladerf_rational_rate *rate) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(rate); + + bladerf_sample_rate integer_rate; + + CHECK_STATUS(dev->board->get_sample_rate(dev, ch, &integer_rate)); + + rate->integer = integer_rate; + rate->num = 0; + rate->den = 1; + + return 0; +} + +static int bladerf2_set_rational_sample_rate( + struct bladerf *dev, + bladerf_channel ch, + struct bladerf_rational_rate *rate, + struct bladerf_rational_rate *actual) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(rate); + + bladerf_sample_rate integer_rate, actual_integer_rate; + + integer_rate = (bladerf_sample_rate)(rate->integer + rate->num / rate->den); + + CHECK_STATUS(dev->board->set_sample_rate(dev, ch, integer_rate, + &actual_integer_rate)); + + if (actual != NULL) { + CHECK_STATUS(dev->board->get_rational_sample_rate(dev, ch, actual)); + } + + return 0; +} + +static int bladerf2_get_sample_rate(struct bladerf *dev, + bladerf_channel ch, + bladerf_sample_rate *rate) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(rate); + + struct bladerf2_board_data *board_data = dev->board_data; + bladerf_sample_rate double_rate; + CHECK_STATUS(board_data->rfic->get_sample_rate(dev, ch, rate)); + + /* OVERSAMPLE feature reports half of the actual + sample rate so we have to double it on return */ + if (dev->feature == BLADERF_FEATURE_OVERSAMPLE) { + double_rate = *rate*2; + *rate = double_rate; + } + + return 0; +} + +static int bladerf2_set_sample_rate(struct bladerf *dev, + bladerf_channel ch, + bladerf_sample_rate rate, + bladerf_sample_rate *actual) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + + struct bladerf2_board_data *board_data = dev->board_data; + struct controller_fns const *rfic = board_data->rfic; + struct bladerf_range const *range = NULL; + bladerf_sample_rate current; + bool old_low, new_low; + bladerf_rfic_rxfir rxfir; + bladerf_rfic_txfir txfir; + + /* Range checking */ + CHECK_STATUS(dev->board->get_sample_rate_range(dev, ch, &range)); + + if (!is_within_range(range, rate)) { + return BLADERF_ERR_RANGE; + } + + /* Feature range check */ + if (dev->feature == BLADERF_FEATURE_OVERSAMPLE && + !is_within_range(&bladerf2_sample_rate_range_oversample, rate)) { + log_error("Sample rate outside of OVERSAMPLE feature range\n"); + return BLADERF_ERR_RANGE; + } else if (dev->feature == BLADERF_FEATURE_DEFAULT && + !is_within_range(&bladerf2_sample_rate_range_base, rate)) { + log_error("Sample rate outside of DEFAULT feature range\n"); + return BLADERF_ERR_RANGE; + } + + /* Get current sample rate, and check it against the low-rate range */ + CHECK_STATUS(dev->board->get_sample_rate(dev, ch, ¤t)); + + old_low = is_within_range(&bladerf2_sample_rate_range_4x, current); + new_low = is_within_range(&bladerf2_sample_rate_range_4x, rate); + + /* Get current filter status */ + if (new_low || old_low) { + CHECK_STATUS( + rfic->get_filter(dev, BLADERF_CHANNEL_RX(0), &rxfir, NULL)); + CHECK_STATUS( + rfic->get_filter(dev, BLADERF_CHANNEL_TX(0), NULL, &txfir)); + } + + /* If the requested sample rate is below the native range, we must implement + * a 4x decimation/interpolation filter on the RFIC. */ + if (new_low) { + bool fir_set_failed = false; + int status; + + if (rxfir != BLADERF_RFIC_RXFIR_DEC4 || + txfir != BLADERF_RFIC_TXFIR_INT4) { + log_debug("%s: enabling 4x decimation/interpolation filters\n", + __FUNCTION__); + + /* Intermidiate sample rate assignment to circumvent rfic->set_filter error */ + if ((current > 40e6 && rate < 2083334) || (rate > 40e6 && current < 2083334)) { + CHECK_STATUS(rfic->set_sample_rate(dev, ch, 30e6)); + } + + status = rfic->set_filter(dev, BLADERF_CHANNEL_RX(0), + BLADERF_RFIC_RXFIR_DEC4, 0); + if (status < 0) { + log_error("%s: could not set RX filter mode: %s\n", + __FUNCTION__, bladerf_strerror(status)); + fir_set_failed = true; + } + + status = rfic->set_filter(dev, BLADERF_CHANNEL_TX(0), 0, + BLADERF_RFIC_TXFIR_INT4); + if (status < 0) { + log_error("%s: could not set TX filter mode: %s\n", + __FUNCTION__, bladerf_strerror(status)); + fir_set_failed = true; + } + } + + /* Try to restore default operations if there was a failure */ + if (fir_set_failed) { + log_debug("%s: attempting to reset filters to default...\n", + __FUNCTION__); + CHECK_STATUS(rfic->set_filter(dev, BLADERF_CHANNEL_RX(0), + BLADERF_RFIC_RXFIR_DEFAULT, 0)); + CHECK_STATUS(rfic->set_filter(dev, BLADERF_CHANNEL_TX(0), 0, + BLADERF_RFIC_TXFIR_DEFAULT)); + + return BLADERF_ERR_UNEXPECTED; + } + } + + /* The AD9361 doubles the sampling rate in OVERSAMPLE mode + so we must halve the sampling rate prior to setting */ + if (dev->feature == BLADERF_FEATURE_OVERSAMPLE) { + rate /= 2; + } + + /* Set the sample rate */ + CHECK_STATUS(rfic->set_sample_rate(dev, ch, rate)); + + /* If the previous sample rate was below the native range, but the new one + * isn't, switch back to the default filters. */ + if (old_low && !new_low) { + if (rxfir != BLADERF_RFIC_RXFIR_DEFAULT || + txfir != BLADERF_RFIC_TXFIR_DEFAULT) { + log_debug("%s: disabling 4x decimation/interpolation filters\n", + __FUNCTION__); + + CHECK_STATUS(rfic->set_filter(dev, BLADERF_CHANNEL_RX(0), + BLADERF_RFIC_RXFIR_DEFAULT, 0)); + CHECK_STATUS(rfic->set_filter(dev, BLADERF_CHANNEL_TX(0), 0, + BLADERF_RFIC_TXFIR_DEFAULT)); + } + } + + /* If requested, fetch the new sample rate and return it. */ + if (actual != NULL) { + CHECK_STATUS(dev->board->get_sample_rate(dev, ch, actual)); + } + + /* Warn the user if this isn't achievable */ + check_total_sample_rate(dev); + + return 0; +} + + +/******************************************************************************/ +/* Bandwidth */ +/******************************************************************************/ + +static int bladerf2_get_bandwidth_range(struct bladerf *dev, + bladerf_channel ch, + struct bladerf_range const **range) +{ + NULL_CHECK(range); + + *range = &bladerf2_bandwidth_range; + + return 0; +} + +static int bladerf2_get_bandwidth(struct bladerf *dev, + bladerf_channel ch, + bladerf_bandwidth *bandwidth) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + + struct bladerf2_board_data *board_data = dev->board_data; + + return board_data->rfic->get_bandwidth(dev, ch, bandwidth); +} + +static int bladerf2_set_bandwidth(struct bladerf *dev, + bladerf_channel ch, + bladerf_bandwidth bandwidth, + bladerf_bandwidth *actual) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + + if (dev->feature == BLADERF_FEATURE_OVERSAMPLE) { + log_warning("bandwidth assignements with oversample feature enabled yields unkown results\n"); + } + + struct bladerf2_board_data *board_data = dev->board_data; + + return board_data->rfic->set_bandwidth(dev, ch, bandwidth, actual); +} + + +/******************************************************************************/ +/* Frequency */ +/******************************************************************************/ + +static int bladerf2_get_frequency_range(struct bladerf *dev, + bladerf_channel ch, + const struct bladerf_range **range) +{ + NULL_CHECK(range); + + if (BLADERF_CHANNEL_IS_TX(ch)) { + *range = &bladerf2_tx_frequency_range; + } else { + *range = &bladerf2_rx_frequency_range; + } + + return 0; +} + +static int bladerf2_select_band(struct bladerf *dev, + bladerf_channel ch, + bladerf_frequency frequency) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + + struct bladerf2_board_data *board_data = dev->board_data; + + return board_data->rfic->select_band(dev, ch, frequency); +} + +static int bladerf2_get_frequency(struct bladerf *dev, + bladerf_channel ch, + bladerf_frequency *frequency) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(frequency); + + struct bladerf2_board_data *board_data = dev->board_data; + + return board_data->rfic->get_frequency(dev, ch, frequency); +} + +static int bladerf2_set_frequency(struct bladerf *dev, + bladerf_channel ch, + bladerf_frequency frequency) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + + struct bladerf2_board_data *board_data = dev->board_data; + + return board_data->rfic->set_frequency(dev, ch, frequency); +} + + +/******************************************************************************/ +/* RF ports */ +/******************************************************************************/ + +static int bladerf2_set_rf_port(struct bladerf *dev, + bladerf_channel ch, + const char *port) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + struct bladerf_rfic_port_name_map const *pm = NULL; + unsigned int pm_len = 0; + uint32_t port_id = UINT32_MAX; + size_t i; + + IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, { + log_debug("%s: FPGA command mode not supported\n", __FUNCTION__); + return BLADERF_ERR_UNSUPPORTED; + }); + + if (BLADERF_CHANNEL_IS_TX(ch)) { + pm = bladerf2_tx_port_map; + pm_len = ARRAY_SIZE(bladerf2_tx_port_map); + } else { + pm = bladerf2_rx_port_map; + pm_len = ARRAY_SIZE(bladerf2_rx_port_map); + } + + for (i = 0; i < pm_len; i++) { + if (strcmp(pm[i].name, port) == 0) { + port_id = pm[i].id; + break; + } + } + + if (UINT32_MAX == port_id) { + RETURN_INVAL("port", "is not valid"); + } + + if (BLADERF_CHANNEL_IS_TX(ch)) { + CHECK_AD936X(ad9361_set_tx_rf_port_output(phy, port_id)); + } else { + CHECK_AD936X(ad9361_set_rx_rf_port_input(phy, port_id)); + } + + return 0; +} + +static int bladerf2_get_rf_port(struct bladerf *dev, + bladerf_channel ch, + char const **port) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(port); + + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + struct bladerf_rfic_port_name_map const *pm = NULL; + unsigned int pm_len = 0; + uint32_t port_id; + bool ok; + size_t i; + + IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, { + log_debug("%s: FPGA command mode not supported\n", __FUNCTION__); + return BLADERF_ERR_UNSUPPORTED; + }); + + if (BLADERF_CHANNEL_IS_TX(ch)) { + pm = bladerf2_tx_port_map; + pm_len = ARRAY_SIZE(bladerf2_tx_port_map); + CHECK_AD936X(ad9361_get_tx_rf_port_output(phy, &port_id)); + } else { + pm = bladerf2_rx_port_map; + pm_len = ARRAY_SIZE(bladerf2_rx_port_map); + CHECK_AD936X(ad9361_get_rx_rf_port_input(phy, &port_id)); + } + + ok = false; + + for (i = 0; i < pm_len; i++) { + if (port_id == pm[i].id) { + *port = pm[i].name; + ok = true; + break; + } + } + + if (!ok) { + *port = "unknown"; + log_error("%s: unexpected port_id %u\n", __FUNCTION__, port_id); + return BLADERF_ERR_UNEXPECTED; + } + + return 0; +} + +static int bladerf2_get_rf_ports(struct bladerf *dev, + bladerf_channel ch, + char const **ports, + unsigned int count) +{ + struct bladerf_rfic_port_name_map const *pm = NULL; + unsigned int pm_len; + size_t i; + + IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, { + *ports = NULL; + return 0; + }); + + if (BLADERF_CHANNEL_IS_TX(ch)) { + pm = bladerf2_tx_port_map; + pm_len = ARRAY_SIZE(bladerf2_tx_port_map); + } else { + pm = bladerf2_rx_port_map; + pm_len = ARRAY_SIZE(bladerf2_rx_port_map); + } + + if (ports != NULL) { + count = (pm_len < count) ? pm_len : count; + + for (i = 0; i < count; i++) { + ports[i] = pm[i].name; + } + } + + return pm_len; +} + + +/******************************************************************************/ +/* Scheduled Tuning */ +/******************************************************************************/ + +static int bladerf2_get_quick_tune(struct bladerf *dev, + bladerf_channel ch, + struct bladerf_quick_tune *quick_tune) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(quick_tune); + + struct bladerf2_board_data *board_data = dev->board_data; + struct controller_fns const *rfic = board_data->rfic; + struct band_port_map const *pm = NULL; + + bladerf_frequency freq; + + if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_RX(1) && + ch != BLADERF_CHANNEL_TX(0) && ch != BLADERF_CHANNEL_TX(1)) { + RETURN_INVAL_ARG("channel", ch, "is not valid"); + } + + CHECK_STATUS(dev->board->get_frequency(dev, ch, &freq)); + + pm = _get_band_port_map_by_freq(ch, freq); + + if (BLADERF_CHANNEL_IS_TX(ch)) { + if (board_data->quick_tune_tx_profile < NUM_BBP_FASTLOCK_PROFILES) { + /* Assign Nios and RFFE profile numbers */ + quick_tune->nios_profile = board_data->quick_tune_tx_profile++; + log_verbose("Quick tune assigned Nios TX fast lock index: %u\n", + quick_tune->nios_profile); + quick_tune->rffe_profile = + quick_tune->nios_profile % NUM_RFFE_FASTLOCK_PROFILES; + log_verbose("Quick tune assigned RFFE TX fast lock index: %u\n", + quick_tune->rffe_profile); + } else { + log_error("Reached maximum number of TX quick tune profiles."); + return BLADERF_ERR_UNEXPECTED; + } + + /* Create a fast lock profile in the RFIC */ + CHECK_STATUS( + rfic->store_fastlock_profile(dev, ch, quick_tune->rffe_profile)); + + /* Save a copy of the TX fast lock profile to the Nios */ + dev->backend->rffe_fastlock_save(dev, true, quick_tune->rffe_profile, + quick_tune->nios_profile); + + /* Set the TX band */ + quick_tune->port = (pm->rfic_port << 6); + + /* Set the TX SPDTs */ + quick_tune->spdt = (pm->spdt << 6) | (pm->spdt << 4); + + } else { + if (board_data->quick_tune_rx_profile < NUM_BBP_FASTLOCK_PROFILES) { + /* Assign Nios and RFFE profile numbers */ + quick_tune->nios_profile = board_data->quick_tune_rx_profile++; + log_verbose("Quick tune assigned Nios RX fast lock index: %u\n", + quick_tune->nios_profile); + quick_tune->rffe_profile = + quick_tune->nios_profile % NUM_RFFE_FASTLOCK_PROFILES; + log_verbose("Quick tune assigned RFFE RX fast lock index: %u\n", + quick_tune->rffe_profile); + } else { + log_error("Reached maximum number of RX quick tune profiles."); + return BLADERF_ERR_UNEXPECTED; + } + + /* Create a fast lock profile in the RFIC */ + CHECK_STATUS( + rfic->store_fastlock_profile(dev, ch, quick_tune->rffe_profile)); + + /* Save a copy of the RX fast lock profile to the Nios */ + dev->backend->rffe_fastlock_save(dev, false, quick_tune->rffe_profile, + quick_tune->nios_profile); + + /* Set the RX bit */ + quick_tune->port = NIOS_PKT_RETUNE2_PORT_IS_RX_MASK; + + /* Set the RX band */ + if (pm->rfic_port < 3) { + quick_tune->port |= (3 << (pm->rfic_port << 1)); + } else { + quick_tune->port |= (1 << (pm->rfic_port - 3)); + } + + /* Set the RX SPDTs */ + quick_tune->spdt = (pm->spdt << 2) | (pm->spdt); + } + + /* Workaround: the RFIC can end up in a bad state after fastlock use, and + * needs to be reset and re-initialized. This is likely due to our direct + * SPI writes causing state incongruence. */ + board_data->rfic_reset_on_close = true; + + return 0; +} + +static int bladerf2_schedule_retune(struct bladerf *dev, + bladerf_channel ch, + bladerf_timestamp timestamp, + bladerf_frequency frequency, + struct bladerf_quick_tune *quick_tune) +{ + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(quick_tune); + + struct bladerf2_board_data *board_data = dev->board_data; + + if (!have_cap(board_data->capabilities, BLADERF_CAP_SCHEDULED_RETUNE)) { + log_debug("This FPGA version (%u.%u.%u) does not support " + "scheduled retunes.\n", + board_data->fpga_version.major, + board_data->fpga_version.minor, + board_data->fpga_version.patch); + + return BLADERF_ERR_UNSUPPORTED; + } + + return dev->backend->retune2(dev, ch, timestamp, quick_tune->nios_profile, + quick_tune->rffe_profile, quick_tune->port, + quick_tune->spdt); +} + +static int bladerf2_cancel_scheduled_retunes(struct bladerf *dev, + bladerf_channel ch) +{ + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + + struct bladerf2_board_data *board_data = dev->board_data; + + if (!have_cap(board_data->capabilities, BLADERF_CAP_SCHEDULED_RETUNE)) { + log_debug("This FPGA version (%u.%u.%u) does not support " + "scheduled retunes.\n", + board_data->fpga_version.major, + board_data->fpga_version.minor, + board_data->fpga_version.patch); + + return BLADERF_ERR_UNSUPPORTED; + } + + return dev->backend->retune2(dev, ch, NIOS_PKT_RETUNE2_CLEAR_QUEUE, 0, 0, 0, + 0); +} + + +/******************************************************************************/ +/* DC/Phase/Gain Correction */ +/******************************************************************************/ + +// clang-format off +static const struct { + struct { + uint16_t reg[2]; /* Low/High band */ + unsigned int shift; /* Value scaling */ + } corr[4]; +} ad9361_correction_reg_table[4] = { + [BLADERF_CHANNEL_RX(0)].corr = { + [BLADERF_CORR_DCOFF_I] = { + FIELD_INIT(.reg, {0, 0}), /* More complex look up */ + FIELD_INIT(.shift, 0), + }, + [BLADERF_CORR_DCOFF_Q] = { + FIELD_INIT(.reg, {0, 0}), /* More complex look up */ + FIELD_INIT(.shift, 0), + }, + [BLADERF_CORR_PHASE] = { + FIELD_INIT(.reg, { AD936X_REG_RX1_INPUT_A_PHASE_CORR, + AD936X_REG_RX1_INPUT_BC_PHASE_CORR }), + FIELD_INIT(.shift, 6), + }, + [BLADERF_CORR_GAIN] = { + FIELD_INIT(.reg, { AD936X_REG_RX1_INPUT_A_GAIN_CORR, + AD936X_REG_RX1_INPUT_BC_PHASE_CORR }), + FIELD_INIT(.shift, 6), + } + }, + [BLADERF_CHANNEL_RX(1)].corr = { + [BLADERF_CORR_DCOFF_I] = { + FIELD_INIT(.reg, {0, 0}), /* More complex look up */ + FIELD_INIT(.shift, 0), + }, + [BLADERF_CORR_DCOFF_Q] = { + FIELD_INIT(.reg, {0, 0}), /* More complex look up */ + FIELD_INIT(.shift, 0), + }, + [BLADERF_CORR_PHASE] = { + FIELD_INIT(.reg, { AD936X_REG_RX2_INPUT_A_PHASE_CORR, + AD936X_REG_RX2_INPUT_BC_PHASE_CORR }), + FIELD_INIT(.shift, 6), + }, + [BLADERF_CORR_GAIN] = { + FIELD_INIT(.reg, { AD936X_REG_RX2_INPUT_A_GAIN_CORR, + AD936X_REG_RX2_INPUT_BC_PHASE_CORR }), + FIELD_INIT(.shift, 6), + } + }, + [BLADERF_CHANNEL_TX(0)].corr = { + [BLADERF_CORR_DCOFF_I] = { + FIELD_INIT(.reg, { AD936X_REG_TX1_OUT_1_OFFSET_I, + AD936X_REG_TX1_OUT_2_OFFSET_I }), + FIELD_INIT(.shift, 5), + }, + [BLADERF_CORR_DCOFF_Q] = { + FIELD_INIT(.reg, { AD936X_REG_TX1_OUT_1_OFFSET_Q, + AD936X_REG_TX1_OUT_2_OFFSET_Q }), + FIELD_INIT(.shift, 5), + }, + [BLADERF_CORR_PHASE] = { + FIELD_INIT(.reg, { AD936X_REG_TX1_OUT_1_PHASE_CORR, + AD936X_REG_TX1_OUT_2_PHASE_CORR }), + FIELD_INIT(.shift, 6), + }, + [BLADERF_CORR_GAIN] = { + FIELD_INIT(.reg, { AD936X_REG_TX1_OUT_1_GAIN_CORR, + AD936X_REG_TX1_OUT_2_GAIN_CORR }), + FIELD_INIT(.shift, 6), + } + }, + [BLADERF_CHANNEL_TX(1)].corr = { + [BLADERF_CORR_DCOFF_I] = { + FIELD_INIT(.reg, { AD936X_REG_TX2_OUT_1_OFFSET_I, + AD936X_REG_TX2_OUT_2_OFFSET_I }), + FIELD_INIT(.shift, 5), + }, + [BLADERF_CORR_DCOFF_Q] = { + FIELD_INIT(.reg, { AD936X_REG_TX2_OUT_1_OFFSET_Q, + AD936X_REG_TX2_OUT_2_OFFSET_Q }), + FIELD_INIT(.shift, 5), + }, + [BLADERF_CORR_PHASE] = { + FIELD_INIT(.reg, { AD936X_REG_TX2_OUT_1_PHASE_CORR, + AD936X_REG_TX2_OUT_2_PHASE_CORR }), + FIELD_INIT(.shift, 6), + }, + [BLADERF_CORR_GAIN] = { + FIELD_INIT(.reg, { AD936X_REG_TX2_OUT_1_GAIN_CORR, + AD936X_REG_TX2_OUT_2_GAIN_CORR }), + FIELD_INIT(.shift, 6), + } + }, +}; + +static const struct { + uint16_t reg_top; + uint16_t reg_bot; +} ad9361_correction_rx_dcoff_reg_table[4][2][2] = { + /* Channel 1 */ + [BLADERF_CHANNEL_RX(0)] = { + /* A band */ + { + /* I */ + {AD936X_REG_INPUT_A_OFFSETS_1, AD936X_REG_RX1_INPUT_A_OFFSETS}, + /* Q */ + {AD936X_REG_RX1_INPUT_A_OFFSETS, AD936X_REG_RX1_INPUT_A_Q_OFFSET}, + }, + /* B/C band */ + { + /* I */ + {AD936X_REG_INPUT_BC_OFFSETS_1, AD936X_REG_RX1_INPUT_BC_OFFSETS}, + /* Q */ + {AD936X_REG_RX1_INPUT_BC_OFFSETS, AD936X_REG_RX1_INPUT_BC_Q_OFFSET}, + }, + }, + /* Channel 2 */ + [BLADERF_CHANNEL_RX(1)] = { + /* A band */ + { + /* I */ + {AD936X_REG_RX2_INPUT_A_I_OFFSET, AD936X_REG_RX2_INPUT_A_OFFSETS}, + /* Q */ + {AD936X_REG_RX2_INPUT_A_OFFSETS, AD936X_REG_INPUT_A_OFFSETS_1}, + }, + /* B/C band */ + { + /* I */ + {AD936X_REG_RX2_INPUT_BC_I_OFFSET, AD936X_REG_RX2_INPUT_BC_OFFSETS}, + /* Q */ + {AD936X_REG_RX2_INPUT_BC_OFFSETS, AD936X_REG_INPUT_BC_OFFSETS_1}, + }, + }, +}; + +static const int ad9361_correction_force_bit[2][4][2] = { + [0] = { + [BLADERF_CORR_DCOFF_I] = {2, 6}, + [BLADERF_CORR_DCOFF_Q] = {2, 6}, + [BLADERF_CORR_PHASE] = {0, 4}, + [BLADERF_CORR_GAIN] = {0, 4}, + }, + [1] = { + [BLADERF_CORR_DCOFF_I] = {3, 7}, + [BLADERF_CORR_DCOFF_Q] = {3, 7}, + [BLADERF_CORR_PHASE] = {1, 5}, + [BLADERF_CORR_GAIN] = {1, 5}, + }, +}; +// clang-format on + +static int bladerf2_get_correction(struct bladerf *dev, + bladerf_channel ch, + bladerf_correction corr, + int16_t *value) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(value); + + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + bool low_band; + uint16_t reg, data; + unsigned int shift; + int32_t val; + + IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, { + log_debug("%s: FPGA command mode not supported\n", __FUNCTION__); + return BLADERF_ERR_UNSUPPORTED; + }); + + /* Validate channel */ + if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_RX(1) && + ch != BLADERF_CHANNEL_TX(0) && ch != BLADERF_CHANNEL_TX(1)) { + RETURN_INVAL_ARG("channel", ch, "is not valid"); + } + + /* Validate correction */ + if (corr != BLADERF_CORR_DCOFF_I && corr != BLADERF_CORR_DCOFF_Q && + corr != BLADERF_CORR_PHASE && corr != BLADERF_CORR_GAIN) { + RETURN_ERROR_STATUS("corr", BLADERF_ERR_UNSUPPORTED); + } + + /* Look up band */ + if (BLADERF_CHANNEL_IS_TX(ch)) { + uint32_t mode; + + CHECK_AD936X(ad9361_get_tx_rf_port_output(phy, &mode)); + + low_band = (mode != AD936X_TXA); + } else { + uint32_t mode; + + CHECK_AD936X(ad9361_get_rx_rf_port_input(phy, &mode)); + + /* Check if RX RF port mode is supported */ + if (mode != AD936X_A_BALANCED && mode != AD936X_B_BALANCED && + mode != AD936X_C_BALANCED) { + RETURN_ERROR_STATUS("mode", BLADERF_ERR_UNSUPPORTED); + } + + low_band = (mode != AD936X_A_BALANCED); + } + + if ((corr == BLADERF_CORR_DCOFF_I || corr == BLADERF_CORR_DCOFF_Q) && + (ch & BLADERF_DIRECTION_MASK) == BLADERF_RX) { + /* RX DC offset corrections are stuffed in a super convoluted way in + * the register map. See AD9361 register map page 51. */ + bool is_q = (corr == BLADERF_CORR_DCOFF_Q); + uint8_t data_top, data_bot; + uint16_t data; + + /* Read top register */ + val = ad9361_spi_read( + phy->spi, + ad9361_correction_rx_dcoff_reg_table[ch][low_band][is_q].reg_top); + if (val < 0) { + RETURN_ERROR_AD9361("ad9361_spi_read(top)", val); + } + + data_top = val; + + /* Read bottom register */ + val = ad9361_spi_read( + phy->spi, + ad9361_correction_rx_dcoff_reg_table[ch][low_band][is_q].reg_bot); + if (val < 0) { + RETURN_ERROR_AD9361("ad9361_spi_read(bottom)", val); + } + + data_bot = val; + + if (ch == BLADERF_CHANNEL_RX(0)) { + if (!is_q) { + /* top: | x x x x 9 8 7 6 | */ + /* bottom: | 5 4 3 2 1 0 x x | */ + data = ((data_top & 0xf) << 6) | (data_bot >> 2); + } else { + /* top: | x x x x x x 9 8 | */ + /* bottom: | 7 6 5 4 3 2 1 0 | */ + data = ((data_top & 0x3) << 8) | data_bot; + } + } else { + if (!is_q) { + /* top: | 9 8 7 6 5 4 3 2 | */ + /* bottom: | x x x x x x 1 0 | */ + data = (data_top << 2) | (data_bot & 0x3); + } else { + /* top: | x x 9 8 7 6 5 4 | */ + /* bottom: | 3 2 1 0 x x x x | */ + data = (data_top << 4) | (data_bot >> 4); + } + } + + /* Scale 10-bit to 13-bit */ + data = data << 3; + + /* Sign extend value */ + *value = data | ((data & (1 << 12)) ? 0xf000 : 0x0000); + } else { + /* Look up correction register and value shift in table */ + reg = ad9361_correction_reg_table[ch].corr[corr].reg[low_band]; + shift = ad9361_correction_reg_table[ch].corr[corr].shift; + + /* Read register and scale value */ + val = ad9361_spi_read(phy->spi, reg); + if (val < 0) { + RETURN_ERROR_AD9361("ad9361_spi_read(reg)", val); + } + + /* Scale 8-bit to 12-bit/13-bit */ + data = val << shift; + + /* Sign extend value */ + if (shift == 5) { + *value = data | ((data & (1 << 12)) ? 0xf000 : 0x0000); + } else { + *value = data | ((data & (1 << 13)) ? 0xc000 : 0x0000); + } + } + + return 0; +} + +static int bladerf2_set_correction(struct bladerf *dev, + bladerf_channel ch, + bladerf_correction corr, + int16_t value) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + bool low_band; + uint16_t reg, data; + unsigned int shift; + int32_t val; + + IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, { + log_debug("%s: FPGA command mode not supported\n", __FUNCTION__); + return BLADERF_ERR_UNSUPPORTED; + }); + + /* Validate channel */ + if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_RX(1) && + ch != BLADERF_CHANNEL_TX(0) && ch != BLADERF_CHANNEL_TX(1)) { + RETURN_INVAL_ARG("channel", ch, "is not valid"); + } + + /* Validate correction */ + if (corr != BLADERF_CORR_DCOFF_I && corr != BLADERF_CORR_DCOFF_Q && + corr != BLADERF_CORR_PHASE && corr != BLADERF_CORR_GAIN) { + RETURN_ERROR_STATUS("corr", BLADERF_ERR_UNSUPPORTED); + } + + /* Look up band */ + if (BLADERF_CHANNEL_IS_TX(ch)) { + uint32_t mode; + + CHECK_AD936X(ad9361_get_tx_rf_port_output(phy, &mode)); + + low_band = (mode != AD936X_TXA); + } else { + uint32_t mode; + + CHECK_AD936X(ad9361_get_rx_rf_port_input(phy, &mode)); + + /* Check if RX RF port mode is supported */ + if (mode != AD936X_A_BALANCED && mode != AD936X_B_BALANCED && + mode != AD936X_C_BALANCED) { + RETURN_ERROR_STATUS("mode", BLADERF_ERR_UNSUPPORTED); + } + + low_band = (mode != AD936X_A_BALANCED); + } + + if ((corr == BLADERF_CORR_DCOFF_I || corr == BLADERF_CORR_DCOFF_Q) && + (ch & BLADERF_DIRECTION_MASK) == BLADERF_RX) { + /* RX DC offset corrections are stuffed in a super convoluted way in + * the register map. See AD9361 register map page 51. */ + bool is_q = (corr == BLADERF_CORR_DCOFF_Q); + uint8_t data_top, data_bot; + + /* Scale 13-bit to 10-bit */ + data = value >> 3; + + /* Read top register */ + val = ad9361_spi_read( + phy->spi, + ad9361_correction_rx_dcoff_reg_table[ch][low_band][is_q].reg_top); + if (val < 0) { + RETURN_ERROR_AD9361("ad9361_spi_read(top)", val); + } + + data_top = val; + + /* Read bottom register */ + val = ad9361_spi_read( + phy->spi, + ad9361_correction_rx_dcoff_reg_table[ch][low_band][is_q].reg_bot); + if (val < 0) { + RETURN_ERROR_AD9361("ad9361_spi_read(bottom)", val); + } + + data_bot = val; + + /* Modify registers */ + if (ch == BLADERF_CHANNEL_RX(0)) { + if (!is_q) { + /* top: | x x x x 9 8 7 6 | */ + /* bottom: | 5 4 3 2 1 0 x x | */ + data_top = (data_top & 0xf0) | ((data >> 6) & 0x0f); + data_bot = (data_bot & 0x03) | ((data & 0x3f) << 2); + } else { + /* top: | x x x x x x 9 8 | */ + /* bottom: | 7 6 5 4 3 2 1 0 | */ + data_top = (data_top & 0xfc) | ((data >> 8) & 0x03); + data_bot = data & 0xff; + } + } else { + if (!is_q) { + /* top: | 9 8 7 6 5 4 3 2 | */ + /* bottom: | x x x x x x 1 0 | */ + data_top = (data >> 2) & 0xff; + data_bot = (data_bot & 0xfc) | (data & 0x03); + } else { + /* top: | x x 9 8 7 6 5 4 | */ + /* bottom: | 3 2 1 0 x x x x | */ + data_top = (data & 0xc0) | ((data >> 4) & 0x3f); + data_bot = (data & 0x0f) | ((data & 0x0f) << 4); + } + } + + /* Write top register */ + CHECK_AD936X(ad9361_spi_write( + phy->spi, + ad9361_correction_rx_dcoff_reg_table[ch][low_band][is_q].reg_top, + data_top)); + + /* Write bottom register */ + CHECK_AD936X(ad9361_spi_write( + phy->spi, + ad9361_correction_rx_dcoff_reg_table[ch][low_band][is_q].reg_bot, + data_bot)); + } else { + /* Look up correction register and value shift in table */ + reg = ad9361_correction_reg_table[ch].corr[corr].reg[low_band]; + shift = ad9361_correction_reg_table[ch].corr[corr].shift; + + /* Scale 12-bit/13-bit to 8-bit */ + data = (value >> shift) & 0xff; + + /* Write register */ + CHECK_AD936X(ad9361_spi_write(phy->spi, reg, data & 0xff)); + } + + reg = (BLADERF_CHANNEL_IS_TX(ch)) ? AD936X_REG_TX_FORCE_BITS + : AD936X_REG_FORCE_BITS; + + /* Read force bit register */ + val = ad9361_spi_read(phy->spi, reg); + if (val < 0) { + RETURN_ERROR_AD9361("ad9361_spi_read(force)", val); + } + + /* Modify register */ + data = val | (1 << ad9361_correction_force_bit[ch >> 1][corr][low_band]); + + /* Write force bit register */ + CHECK_AD936X(ad9361_spi_write(phy->spi, reg, data)); + + return 0; +} + + +/******************************************************************************/ +/* Trigger */ +/******************************************************************************/ + +static int bladerf2_trigger_init(struct bladerf *dev, + bladerf_channel ch, + bladerf_trigger_signal signal, + struct bladerf_trigger *trigger) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(trigger); + + return fpga_trigger_init(dev, ch, signal, trigger); +} + +static int bladerf2_trigger_arm(struct bladerf *dev, + struct bladerf_trigger const *trigger, + bool arm, + uint64_t resv1, + uint64_t resv2) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(trigger); + + return fpga_trigger_arm(dev, trigger, arm); +} + +static int bladerf2_trigger_fire(struct bladerf *dev, + struct bladerf_trigger const *trigger) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(trigger); + + return fpga_trigger_fire(dev, trigger); +} + +static int bladerf2_trigger_state(struct bladerf *dev, + struct bladerf_trigger const *trigger, + bool *is_armed, + bool *has_fired, + bool *fire_requested, + uint64_t *resv1, + uint64_t *resv2) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(trigger); + NULL_CHECK(is_armed); + NULL_CHECK(has_fired); + NULL_CHECK(fire_requested); + + /* Reserved for future metadata (e.g., trigger counts, timestamp) */ + if (resv1 != NULL) { + *resv1 = 0; + } + + if (resv2 != NULL) { + *resv2 = 0; + } + + return fpga_trigger_state(dev, trigger, is_armed, has_fired, + fire_requested); +} + + +/******************************************************************************/ +/* Streaming */ +/******************************************************************************/ + +static int bladerf2_init_stream(struct bladerf_stream **stream, + struct bladerf *dev, + bladerf_stream_cb callback, + void ***buffers, + size_t num_buffers, + bladerf_format format, + size_t samples_per_buffer, + size_t num_transfers, + void *user_data) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + + return async_init_stream(stream, dev, callback, buffers, num_buffers, + format, samples_per_buffer, num_transfers, + user_data); +} + +static int bladerf2_stream(struct bladerf_stream *stream, + bladerf_channel_layout layout) +{ + bladerf_direction dir = layout & BLADERF_DIRECTION_MASK; + int rv; + + switch (layout) { + case BLADERF_RX_X1: + case BLADERF_RX_X2: + case BLADERF_TX_X1: + case BLADERF_TX_X2: + break; + default: + return -EINVAL; + } + + WITH_MUTEX(&stream->dev->lock, { + CHECK_STATUS_LOCKED( + perform_format_config(stream->dev, dir, stream->format)); + }); + + rv = async_run_stream(stream, layout); + + WITH_MUTEX(&stream->dev->lock, { + CHECK_STATUS_LOCKED( + perform_format_deconfig(stream->dev, dir)); + }); + + return rv; +} + +static int bladerf2_submit_stream_buffer(struct bladerf_stream *stream, + void *buffer, + unsigned int timeout_ms, + bool nonblock) +{ + size_t len; + len = async_stream_buf_bytes(stream); + return async_submit_stream_buffer(stream, buffer, &len, timeout_ms, nonblock); +} + +static void bladerf2_deinit_stream(struct bladerf_stream *stream) +{ + async_deinit_stream(stream); +} + +static int bladerf2_set_stream_timeout(struct bladerf *dev, + bladerf_direction dir, + unsigned int timeout) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + + struct bladerf2_board_data *board_data = dev->board_data; + + WITH_MUTEX(&board_data->sync[dir].lock, + { board_data->sync[dir].stream_config.timeout_ms = timeout; }); + + return 0; +} + +static int bladerf2_get_stream_timeout(struct bladerf *dev, + bladerf_direction dir, + unsigned int *timeout) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(timeout); + + struct bladerf2_board_data *board_data = dev->board_data; + + WITH_MUTEX(&board_data->sync[dir].lock, + { *timeout = board_data->sync[dir].stream_config.timeout_ms; }); + + return 0; +} + +static int bladerf2_sync_config(struct bladerf *dev, + bladerf_channel_layout layout, + bladerf_format format, + unsigned int num_buffers, + unsigned int buffer_size, + unsigned int num_transfers, + unsigned int stream_timeout) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + + struct bladerf2_board_data *board_data = dev->board_data; + + bladerf_direction dir = layout & BLADERF_DIRECTION_MASK; + int status; + + if (dev->feature == BLADERF_FEATURE_OVERSAMPLE + && (format == BLADERF_FORMAT_SC16_Q11 || format == BLADERF_FORMAT_SC16_Q11_META)) { + log_error("16bit format unsupported with OVERSAMPLE feature enabled\n"); + return BLADERF_ERR_UNSUPPORTED; + } + + switch (layout) { + case BLADERF_RX_X1: + case BLADERF_RX_X2: + case BLADERF_TX_X1: + case BLADERF_TX_X2: + break; + default: + return -EINVAL; + } + + status = perform_format_config(dev, dir, format); + if (0 == status) { + status = sync_init(&board_data->sync[dir], dev, layout, format, + num_buffers, buffer_size, board_data->msg_size, + num_transfers, stream_timeout); + if (status != 0) { + perform_format_deconfig(dev, dir); + } + } + + return status; +} + +static int bladerf2_sync_tx(struct bladerf *dev, + void const *samples, + unsigned int num_samples, + struct bladerf_metadata *metadata, + unsigned int timeout_ms) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + + struct bladerf2_board_data *board_data = dev->board_data; + + if (!board_data->sync[BLADERF_TX].initialized) { + RETURN_INVAL("sync tx", "not initialized"); + } + + return sync_tx(&board_data->sync[BLADERF_TX], samples, num_samples, + metadata, timeout_ms); +} + +static int bladerf2_sync_rx(struct bladerf *dev, + void *samples, + unsigned int num_samples, + struct bladerf_metadata *metadata, + unsigned int timeout_ms) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + + struct bladerf2_board_data *board_data = dev->board_data; + + if (!board_data->sync[BLADERF_RX].initialized) { + RETURN_INVAL("sync rx", "not initialized"); + } + + return sync_rx(&board_data->sync[BLADERF_RX], samples, num_samples, + metadata, timeout_ms); +} + +static int bladerf2_get_timestamp(struct bladerf *dev, + bladerf_direction dir, + bladerf_timestamp *value) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(value); + + return dev->backend->get_timestamp(dev, dir, value); +} + + +/******************************************************************************/ +/* FPGA/Firmware Loading/Flashing */ +/******************************************************************************/ + +static int bladerf2_load_fpga(struct bladerf *dev, + uint8_t const *buf, + size_t length) +{ + CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED); + NULL_CHECK(buf); + + struct bladerf2_board_data *board_data = dev->board_data; + + if (!is_valid_fpga_size(dev, board_data->fpga_size, length)) { + RETURN_INVAL("fpga file", "incorrect file size"); + } + + CHECK_STATUS(dev->backend->load_fpga(dev, buf, length)); + + /* Update device state */ + board_data->state = STATE_FPGA_LOADED; + + CHECK_STATUS(_bladerf2_initialize(dev)); + + return 0; +} + +static int bladerf2_flash_fpga(struct bladerf *dev, + uint8_t const *buf, + size_t length) +{ + CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED); + NULL_CHECK(buf); + + struct bladerf2_board_data *board_data = dev->board_data; + + if (!is_valid_fpga_size(dev, board_data->fpga_size, length)) { + RETURN_INVAL("fpga file", "incorrect file size"); + } + + return spi_flash_write_fpga_bitstream(dev, buf, length); +} + +static int bladerf2_erase_stored_fpga(struct bladerf *dev) +{ + CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED); + + return spi_flash_erase_fpga(dev); +} + +static int bladerf2_flash_firmware(struct bladerf *dev, + uint8_t const *buf, + size_t length) +{ + CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED); + NULL_CHECK(buf); + + char const env_override[] = "BLADERF_SKIP_FW_SIZE_CHECK"; + + /* Sanity check firmware length. + * + * TODO in the future, better sanity checks can be performed when + * using the bladerf image format currently used to backup/restore + * calibration data + */ + if (!getenv(env_override) && !is_valid_fw_size(length)) { + log_info("Detected potentially invalid firmware file.\n"); + log_info("Define BLADERF_SKIP_FW_SIZE_CHECK in your environment " + "to skip this check.\n"); + RETURN_INVAL_ARG("firmware size", length, "is not valid"); + } + + return spi_flash_write_fx3_fw(dev, buf, length); +} + +static int bladerf2_device_reset(struct bladerf *dev) +{ + CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED); + + return dev->backend->device_reset(dev); +} + + +/******************************************************************************/ +/* Tuning mode */ +/******************************************************************************/ + +static inline bool _supports_fpga_tuning(struct bladerf *dev) +{ + extern struct controller_fns const rfic_fpga_control; + + struct bladerf2_board_data *board_data = dev->board_data; + + return (have_cap(board_data->capabilities, BLADERF_CAP_FPGA_TUNING) && + rfic_fpga_control.is_present(dev)); +} + +static int bladerf2_set_tuning_mode(struct bladerf *dev, + bladerf_tuning_mode mode) +{ + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + + extern struct controller_fns const rfic_host_control; + extern struct controller_fns const rfic_fpga_control; + + struct bladerf2_board_data *board_data = dev->board_data; + struct controller_fns const *rfic_new = NULL; + struct controller_fns const *rfic_other = NULL; + + bladerf_tuning_mode mode_other; + bladerf_rfic_init_state init_state; + + log_debug("%s: New tuning mode: %s\n", __FUNCTION__, tuningmode2str(mode)); + + switch (mode) { + case BLADERF_TUNING_MODE_HOST: + rfic_new = &rfic_host_control; + rfic_other = _supports_fpga_tuning(dev) ? &rfic_fpga_control : NULL; + mode_other = BLADERF_TUNING_MODE_FPGA; + break; + + case BLADERF_TUNING_MODE_FPGA: + /* Test capability */ + if (!_supports_fpga_tuning(dev)) { + log_debug("%s: The loaded FPGA version (%u.%u.%u) does not " + "support FPGA RFIC control\n", + __FUNCTION__, board_data->fpga_version.major, + board_data->fpga_version.minor, + board_data->fpga_version.patch); + return BLADERF_ERR_UNSUPPORTED; + } + + rfic_new = &rfic_fpga_control; + rfic_other = &rfic_host_control; + mode_other = BLADERF_TUNING_MODE_HOST; + break; + + default: + log_error("%s: invalid tuning mode (%d)\n", mode); + return BLADERF_ERR_INVAL; + } + + /* De-initialize RFIC if it's initialized by another tuning mode */ + if (NULL != rfic_other) { + CHECK_STATUS(rfic_other->get_init_state(dev, &init_state)); + + if (init_state != BLADERF_RFIC_INIT_STATE_OFF) { + log_debug("%s: %s %s RFIC control\n", __FUNCTION__, "Releasing", + tuningmode2str(mode_other)); + CHECK_STATUS(rfic_other->deinitialize(dev)); + } + } + + /* Set board data */ + board_data->rfic = rfic_new; + board_data->tuning_mode = mode; + + /* Bring RFIC to initialized state */ + CHECK_STATUS(rfic_new->get_init_state(dev, &init_state)); + + switch (init_state) { + case BLADERF_RFIC_INIT_STATE_OFF: + log_debug("%s: %s %s RFIC control\n", __FUNCTION__, "Initializing", + tuningmode2str(mode)); + return rfic_new->initialize(dev); + + case BLADERF_RFIC_INIT_STATE_STANDBY: + log_debug("%s: %s %s RFIC control\n", __FUNCTION__, "Restoring", + tuningmode2str(mode)); + return rfic_new->initialize(dev); + + case BLADERF_RFIC_INIT_STATE_ON: + log_debug("%s: %s %s RFIC control\n", __FUNCTION__, "Maintaining", + tuningmode2str(mode)); + return 0; + + default: + log_error("%s: invalid RFIC initialization state (%d)\n", + init_state); + return BLADERF_ERR_UNEXPECTED; + } +} + +static int bladerf2_get_tuning_mode(struct bladerf *dev, + bladerf_tuning_mode *mode) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(mode); + + struct bladerf2_board_data *board_data = dev->board_data; + + *mode = board_data->tuning_mode; + + return 0; +} + + +/******************************************************************************/ +/* Loopback */ +/******************************************************************************/ + +static int bladerf2_get_loopback_modes( + struct bladerf *dev, struct bladerf_loopback_modes const **modes) +{ + if (modes != NULL) { + *modes = bladerf2_loopback_modes; + } + + return ARRAY_SIZE(bladerf2_loopback_modes); +} + +static int bladerf2_set_loopback(struct bladerf *dev, bladerf_loopback mode) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + bool firmware_loopback = false; + int32_t bist_loopback = 0; + + + IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, { + if (BLADERF_LB_RFIC_BIST == mode) { + log_debug( + "%s: BLADERF_LB_RFIC_BIST not supported in FPGA command mode\n", + __FUNCTION__); + return BLADERF_ERR_UNSUPPORTED; + } + }); + + switch (mode) { + case BLADERF_LB_NONE: + break; + case BLADERF_LB_FIRMWARE: + firmware_loopback = true; + break; + case BLADERF_LB_RFIC_BIST: + bist_loopback = 1; + break; + default: + log_error("%s: unknown loopback mode (%d)\n", __FUNCTION__, mode); + return BLADERF_ERR_UNEXPECTED; + } + + IF_COMMAND_MODE(dev, RFIC_COMMAND_HOST, { + /* Set digital loopback state */ + CHECK_AD936X(ad9361_bist_loopback(phy, bist_loopback)); + }); + + /* Set firmware loopback state */ + CHECK_STATUS(dev->backend->set_firmware_loopback(dev, firmware_loopback)); + + return 0; +} + +static int bladerf2_get_loopback(struct bladerf *dev, bladerf_loopback *mode) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(mode); + + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + int32_t ad9361_loopback; + bool fw_loopback; + + /* Read firwmare loopback */ + CHECK_STATUS(dev->backend->get_firmware_loopback(dev, &fw_loopback)); + + if (fw_loopback) { + *mode = BLADERF_LB_FIRMWARE; + return 0; + } + + IF_COMMAND_MODE(dev, RFIC_COMMAND_HOST, { + /* Read AD9361 bist loopback */ + /* Note: this returns void */ + ad9361_get_bist_loopback(phy, &ad9361_loopback); + + if (ad9361_loopback == 1) { + *mode = BLADERF_LB_RFIC_BIST; + return 0; + } + }); + + *mode = BLADERF_LB_NONE; + + return 0; +} + + +/******************************************************************************/ +/* Sample RX FPGA Mux */ +/******************************************************************************/ + +static int bladerf2_set_rx_mux(struct bladerf *dev, bladerf_rx_mux mode) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + + uint32_t rx_mux_val, config_gpio; + + /* Validate desired mux mode */ + switch (mode) { + case BLADERF_RX_MUX_BASEBAND: + case BLADERF_RX_MUX_12BIT_COUNTER: + case BLADERF_RX_MUX_32BIT_COUNTER: + case BLADERF_RX_MUX_DIGITAL_LOOPBACK: + rx_mux_val = ((uint32_t)mode) << BLADERF_GPIO_RX_MUX_SHIFT; + break; + + default: + log_debug("Invalid RX mux mode setting passed to %s(): %d\n", mode, + __FUNCTION__); + RETURN_INVAL_ARG("bladerf_rx_mux", mode, "is invalid"); + } + + CHECK_STATUS(dev->backend->config_gpio_read(dev, &config_gpio)); + + /* Clear out and assign the associated RX mux bits */ + config_gpio &= ~BLADERF_GPIO_RX_MUX_MASK; + config_gpio |= rx_mux_val; + + CHECK_STATUS(dev->backend->config_gpio_write(dev, config_gpio)); + + return 0; +} + +static int bladerf2_get_rx_mux(struct bladerf *dev, bladerf_rx_mux *mode) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(mode); + + bladerf_rx_mux val; + uint32_t config_gpio; + + CHECK_STATUS(dev->backend->config_gpio_read(dev, &config_gpio)); + + /* Extract RX mux bits */ + config_gpio &= BLADERF_GPIO_RX_MUX_MASK; + config_gpio >>= BLADERF_GPIO_RX_MUX_SHIFT; + val = config_gpio; + + /* Ensure it's a valid/supported value */ + switch (val) { + case BLADERF_RX_MUX_BASEBAND: + case BLADERF_RX_MUX_12BIT_COUNTER: + case BLADERF_RX_MUX_32BIT_COUNTER: + case BLADERF_RX_MUX_DIGITAL_LOOPBACK: + *mode = val; + break; + + default: + *mode = BLADERF_RX_MUX_INVALID; + log_debug("Invalid rx mux mode %d read from config gpio\n", val); + return BLADERF_ERR_UNEXPECTED; + } + + return 0; +} + + +/******************************************************************************/ +/* Low-level VCTCXO Tamer Mode */ +/******************************************************************************/ + +static int bladerf2_set_vctcxo_tamer_mode(struct bladerf *dev, + bladerf_vctcxo_tamer_mode mode) +{ + return BLADERF_ERR_UNSUPPORTED; +} + +static int bladerf2_get_vctcxo_tamer_mode(struct bladerf *dev, + bladerf_vctcxo_tamer_mode *mode) +{ + return BLADERF_ERR_UNSUPPORTED; +} + + +/******************************************************************************/ +/* Low-level VCTCXO Trim DAC access */ +/******************************************************************************/ + +static int _bladerf2_get_trim_dac_enable(struct bladerf *dev, bool *enable) +{ + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(enable); + + uint16_t trim; + + // Read current trim DAC setting + CHECK_STATUS(dev->backend->ad56x1_vctcxo_trim_dac_read(dev, &trim)); + + // Determine if it's enabled... + *enable = (TRIMDAC_EN_ACTIVE == (trim >> TRIMDAC_EN)); + + log_debug("trim DAC is %s\n", (*enable ? "enabled" : "disabled")); + + if ((trim >> TRIMDAC_EN) != TRIMDAC_EN_ACTIVE && + (trim >> TRIMDAC_EN) != TRIMDAC_EN_HIGHZ) { + log_warning("unknown trim DAC state: 0x%x\n", (trim >> TRIMDAC_EN)); + } + + return 0; +} + +static int _bladerf2_set_trim_dac_enable(struct bladerf *dev, bool enable) +{ + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + + struct bladerf2_board_data *board_data = dev->board_data; + uint16_t trim; + bool current_state; + + // See if we have anything to do + CHECK_STATUS(_bladerf2_get_trim_dac_enable(dev, ¤t_state)); + + if (enable == current_state) { + log_debug("trim DAC already %s, nothing to do\n", + enable ? "enabled" : "disabled"); + return 0; + } + + // Read current trim DAC setting + CHECK_STATUS(dev->backend->ad56x1_vctcxo_trim_dac_read(dev, &trim)); + + // Set the trim DAC to high z if applicable + if (!enable && trim != (TRIMDAC_EN_HIGHZ << TRIMDAC_EN)) { + board_data->trimdac_last_value = trim; + log_debug("saving current trim DAC value: 0x%04x\n", trim); + trim = TRIMDAC_EN_HIGHZ << TRIMDAC_EN; + } else if (enable && trim == (TRIMDAC_EN_HIGHZ << TRIMDAC_EN)) { + trim = board_data->trimdac_last_value; + log_debug("restoring old trim DAC value: 0x%04x\n", trim); + } + + // Write back the trim DAC setting + CHECK_STATUS(dev->backend->ad56x1_vctcxo_trim_dac_write(dev, trim)); + + // Update our state flag + board_data->trim_source = enable ? TRIM_SOURCE_TRIM_DAC : TRIM_SOURCE_NONE; + + return 0; +} + +/** + * @brief Read the VCTCXO trim value from the SPI flash + * + * Retrieves the factory VCTCXO value from the SPI flash. This function + * should not be used while sample streaming is in progress. + * + * @param dev Device handle + * @param trim Pointer to populate with the trim value + * + * @return 0 on success, value from \ref RETCODES list on failure + */ +static int bladerf2_read_flash_vctcxo_trim(struct bladerf *dev, uint16_t *trim) +{ + CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED); + NULL_CHECK(trim); + + int status; + + status = spi_flash_read_vctcxo_trim(dev, trim); + if (status < 0) { + log_warning("Failed to get VCTCXO trim value: %s\n", + bladerf_strerror(status)); + log_debug("Defaulting DAC trim to 0x1ffc.\n"); + *trim = 0x1ffc; + return 0; + } + + return status; +} + +static int bladerf2_get_vctcxo_trim(struct bladerf *dev, uint16_t *trim) +{ + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(trim); + + struct bladerf2_board_data *board_data = dev->board_data; + + *trim = board_data->trimdac_stored_value; + + return 0; +} + +static int bladerf2_trim_dac_read(struct bladerf *dev, uint16_t *trim) +{ + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(trim); + + return dev->backend->ad56x1_vctcxo_trim_dac_read(dev, trim); +} + +static int bladerf2_trim_dac_write(struct bladerf *dev, uint16_t trim) +{ + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + + struct bladerf2_board_data *board_data = dev->board_data; + uint16_t trim_control = (trim >> TRIMDAC_EN) & TRIMDAC_EN_MASK; + uint16_t trim_value = trim & TRIMDAC_MASK; + bool enable; + + log_debug("requested trim 0x%04x (control 0x%01x value 0x%04x)\n", trim, + trim_control, trim_value); + + // Is the trimdac enabled? + CHECK_STATUS(_bladerf2_get_trim_dac_enable(dev, &enable)); + + // If the trimdac is not enabled, save this value for later but don't + // apply it. + if (!enable && trim_control != TRIMDAC_EN_HIGHZ) { + log_warning("trim DAC is disabled. New value will be saved until " + "trim DAC is enabled\n"); + board_data->trimdac_last_value = trim_value; + return 0; + } + + return dev->backend->ad56x1_vctcxo_trim_dac_write(dev, trim); +} + + +/******************************************************************************/ +/* Low-level Trigger control access */ +/******************************************************************************/ + +static int bladerf2_read_trigger(struct bladerf *dev, + bladerf_channel ch, + bladerf_trigger_signal trigger, + uint8_t *val) +{ + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(val); + + return fpga_trigger_read(dev, ch, trigger, val); +} + +static int bladerf2_write_trigger(struct bladerf *dev, + bladerf_channel ch, + bladerf_trigger_signal trigger, + uint8_t val) +{ + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + + return fpga_trigger_write(dev, ch, trigger, val); +} + +/******************************************************************************/ +/* Low-level Wishbone Master access */ +/******************************************************************************/ + +static int bladerf2_wishbone_master_read(struct bladerf *dev, uint32_t addr, uint32_t *data) +{ + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(data); + + return dev->backend->wishbone_master_read(dev, addr, data); +} + +static int bladerf2_wishbone_master_write(struct bladerf *dev, uint32_t addr, uint32_t data) +{ + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + + return dev->backend->wishbone_master_write(dev, addr, data); +} + + +/******************************************************************************/ +/* Low-level Configuration GPIO access */ +/******************************************************************************/ + +static int bladerf2_config_gpio_read(struct bladerf *dev, uint32_t *val) +{ + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(val); + + return dev->backend->config_gpio_read(dev, val); +} + +static int bladerf2_config_gpio_write(struct bladerf *dev, uint32_t val) +{ + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + + return dev->backend->config_gpio_write(dev, val); +} + + +/******************************************************************************/ +/* Low-level SPI Flash access */ +/******************************************************************************/ + +static int bladerf2_erase_flash(struct bladerf *dev, + uint32_t erase_block, + uint32_t count) +{ + CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED); + + return spi_flash_erase(dev, erase_block, count); +} + +static int bladerf2_read_flash(struct bladerf *dev, + uint8_t *buf, + uint32_t page, + uint32_t count) +{ + CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED); + NULL_CHECK(buf); + + return spi_flash_read(dev, buf, page, count); +} + +static int bladerf2_write_flash(struct bladerf *dev, + uint8_t const *buf, + uint32_t page, + uint32_t count) +{ + CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED); + NULL_CHECK(buf); + + return spi_flash_write(dev, buf, page, count); +} + + +/******************************************************************************/ +/* Expansion support */ +/******************************************************************************/ + +static int bladerf2_expansion_attach(struct bladerf *dev, bladerf_xb xb) +{ + return BLADERF_ERR_UNSUPPORTED; +} + +static int bladerf2_expansion_get_attached(struct bladerf *dev, bladerf_xb *xb) +{ + NULL_CHECK(xb); + + *xb = BLADERF_XB_NONE; + + return 0; +} + + +/******************************************************************************/ +/* Board binding */ +/******************************************************************************/ + +struct board_fns const bladerf2_board_fns = { + FIELD_INIT(.matches, bladerf2_matches), + FIELD_INIT(.open, bladerf2_open), + FIELD_INIT(.close, bladerf2_close), + FIELD_INIT(.device_speed, bladerf2_device_speed), + FIELD_INIT(.get_serial, bladerf2_get_serial), + FIELD_INIT(.get_fpga_size, bladerf2_get_fpga_size), + FIELD_INIT(.get_fpga_bytes, bladerf2_get_fpga_bytes), + FIELD_INIT(.get_flash_size, bladerf2_get_flash_size), + FIELD_INIT(.is_fpga_configured, bladerf2_is_fpga_configured), + FIELD_INIT(.get_fpga_source, bladerf2_get_fpga_source), + FIELD_INIT(.get_capabilities, bladerf2_get_capabilities), + FIELD_INIT(.get_channel_count, bladerf2_get_channel_count), + FIELD_INIT(.get_fpga_version, bladerf2_get_fpga_version), + FIELD_INIT(.get_fw_version, bladerf2_get_fw_version), + FIELD_INIT(.set_gain, bladerf2_set_gain), + FIELD_INIT(.get_gain, bladerf2_get_gain), + FIELD_INIT(.set_gain_mode, bladerf2_set_gain_mode), + FIELD_INIT(.get_gain_mode, bladerf2_get_gain_mode), + FIELD_INIT(.get_gain_modes, bladerf2_get_gain_modes), + FIELD_INIT(.get_gain_range, bladerf2_get_gain_range), + FIELD_INIT(.set_gain_stage, bladerf2_set_gain_stage), + FIELD_INIT(.get_gain_stage, bladerf2_get_gain_stage), + FIELD_INIT(.get_gain_stage_range, bladerf2_get_gain_stage_range), + FIELD_INIT(.get_gain_stages, bladerf2_get_gain_stages), + FIELD_INIT(.set_sample_rate, bladerf2_set_sample_rate), + FIELD_INIT(.set_rational_sample_rate, bladerf2_set_rational_sample_rate), + FIELD_INIT(.get_sample_rate, bladerf2_get_sample_rate), + FIELD_INIT(.get_sample_rate_range, bladerf2_get_sample_rate_range), + FIELD_INIT(.get_rational_sample_rate, bladerf2_get_rational_sample_rate), + FIELD_INIT(.set_bandwidth, bladerf2_set_bandwidth), + FIELD_INIT(.get_bandwidth, bladerf2_get_bandwidth), + FIELD_INIT(.get_bandwidth_range, bladerf2_get_bandwidth_range), + FIELD_INIT(.get_frequency, bladerf2_get_frequency), + FIELD_INIT(.set_frequency, bladerf2_set_frequency), + FIELD_INIT(.get_frequency_range, bladerf2_get_frequency_range), + FIELD_INIT(.select_band, bladerf2_select_band), + FIELD_INIT(.set_rf_port, bladerf2_set_rf_port), + FIELD_INIT(.get_rf_port, bladerf2_get_rf_port), + FIELD_INIT(.get_rf_ports, bladerf2_get_rf_ports), + FIELD_INIT(.get_quick_tune, bladerf2_get_quick_tune), + FIELD_INIT(.schedule_retune, bladerf2_schedule_retune), + FIELD_INIT(.cancel_scheduled_retunes, bladerf2_cancel_scheduled_retunes), + FIELD_INIT(.get_correction, bladerf2_get_correction), + FIELD_INIT(.set_correction, bladerf2_set_correction), + FIELD_INIT(.trigger_init, bladerf2_trigger_init), + FIELD_INIT(.trigger_arm, bladerf2_trigger_arm), + FIELD_INIT(.trigger_fire, bladerf2_trigger_fire), + FIELD_INIT(.trigger_state, bladerf2_trigger_state), + FIELD_INIT(.enable_module, bladerf2_enable_module), + FIELD_INIT(.init_stream, bladerf2_init_stream), + FIELD_INIT(.stream, bladerf2_stream), + FIELD_INIT(.submit_stream_buffer, bladerf2_submit_stream_buffer), + FIELD_INIT(.deinit_stream, bladerf2_deinit_stream), + FIELD_INIT(.set_stream_timeout, bladerf2_set_stream_timeout), + FIELD_INIT(.get_stream_timeout, bladerf2_get_stream_timeout), + FIELD_INIT(.sync_config, bladerf2_sync_config), + FIELD_INIT(.sync_tx, bladerf2_sync_tx), + FIELD_INIT(.sync_rx, bladerf2_sync_rx), + FIELD_INIT(.get_timestamp, bladerf2_get_timestamp), + FIELD_INIT(.load_fpga, bladerf2_load_fpga), + FIELD_INIT(.flash_fpga, bladerf2_flash_fpga), + FIELD_INIT(.erase_stored_fpga, bladerf2_erase_stored_fpga), + FIELD_INIT(.flash_firmware, bladerf2_flash_firmware), + FIELD_INIT(.device_reset, bladerf2_device_reset), + FIELD_INIT(.set_tuning_mode, bladerf2_set_tuning_mode), + FIELD_INIT(.get_tuning_mode, bladerf2_get_tuning_mode), + FIELD_INIT(.get_loopback_modes, bladerf2_get_loopback_modes), + FIELD_INIT(.set_loopback, bladerf2_set_loopback), + FIELD_INIT(.get_loopback, bladerf2_get_loopback), + FIELD_INIT(.get_rx_mux, bladerf2_get_rx_mux), + FIELD_INIT(.set_rx_mux, bladerf2_set_rx_mux), + FIELD_INIT(.set_vctcxo_tamer_mode, bladerf2_set_vctcxo_tamer_mode), + FIELD_INIT(.get_vctcxo_tamer_mode, bladerf2_get_vctcxo_tamer_mode), + FIELD_INIT(.get_vctcxo_trim, bladerf2_get_vctcxo_trim), + FIELD_INIT(.trim_dac_read, bladerf2_trim_dac_read), + FIELD_INIT(.trim_dac_write, bladerf2_trim_dac_write), + FIELD_INIT(.read_trigger, bladerf2_read_trigger), + FIELD_INIT(.write_trigger, bladerf2_write_trigger), + FIELD_INIT(.wishbone_master_read, bladerf2_wishbone_master_read), + FIELD_INIT(.wishbone_master_write, bladerf2_wishbone_master_write), + FIELD_INIT(.config_gpio_read, bladerf2_config_gpio_read), + FIELD_INIT(.config_gpio_write, bladerf2_config_gpio_write), + FIELD_INIT(.erase_flash, bladerf2_erase_flash), + FIELD_INIT(.read_flash, bladerf2_read_flash), + FIELD_INIT(.write_flash, bladerf2_write_flash), + FIELD_INIT(.expansion_attach, bladerf2_expansion_attach), + FIELD_INIT(.expansion_get_attached, bladerf2_expansion_get_attached), + FIELD_INIT(.name, "bladerf2"), +}; + + +/****************************************************************************** + ****************************************************************************** + * bladeRF2-specific Functions * + ****************************************************************************** + ******************************************************************************/ + +/******************************************************************************/ +/* Bias Tee Control */ +/******************************************************************************/ + +int bladerf_get_bias_tee(struct bladerf *dev, bladerf_channel ch, bool *enable) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(enable); + + WITH_MUTEX(&dev->lock, { + uint32_t reg; + uint32_t shift; + + shift = BLADERF_CHANNEL_IS_TX(ch) ? RFFE_CONTROL_TX_BIAS_EN + : RFFE_CONTROL_RX_BIAS_EN; + + /* Read RFFE control register */ + CHECK_STATUS_LOCKED(dev->backend->rffe_control_read(dev, ®)); + + /* Check register value */ + *enable = (reg >> shift) & 0x1; + }); + + return 0; +} + +int bladerf_set_bias_tee(struct bladerf *dev, bladerf_channel ch, bool enable) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + + WITH_MUTEX(&dev->lock, { + uint32_t reg; + uint32_t shift; + + shift = BLADERF_CHANNEL_IS_TX(ch) ? RFFE_CONTROL_TX_BIAS_EN + : RFFE_CONTROL_RX_BIAS_EN; + + /* Read RFFE control register */ + CHECK_STATUS_LOCKED(dev->backend->rffe_control_read(dev, ®)); + + /* Clear register value */ + reg &= ~(1 << shift); + + /* Set register value */ + if (enable) { + reg |= (1 << shift); + } + + /* Write RFFE control register */ + log_debug("%s: rffe_control_write %08x\n", __FUNCTION__, reg); + CHECK_STATUS_LOCKED(dev->backend->rffe_control_write(dev, reg)); + }); + + return 0; +} + + +/******************************************************************************/ +/* Low level RFIC Accessors */ +/******************************************************************************/ + +int bladerf_get_rfic_register(struct bladerf *dev, + uint16_t address, + uint8_t *val) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(val); + + WITH_MUTEX(&dev->lock, { + uint64_t data; + + address |= (AD936X_READ | AD936X_CNT(1)); + + CHECK_AD936X_LOCKED(dev->backend->ad9361_spi_read(dev, address, &data)); + + *val = (data >> 56) & 0xff; + }); + + return 0; +} + +int bladerf_set_rfic_register(struct bladerf *dev, + uint16_t address, + uint8_t val) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + + WITH_MUTEX(&dev->lock, { + uint64_t data = (((uint64_t)val) << 56); + + address |= (AD936X_WRITE | AD936X_CNT(1)); + + CHECK_AD936X_LOCKED(dev->backend->ad9361_spi_write(dev, address, data)); + }); + + return 0; +} + +int bladerf_get_rfic_temperature(struct bladerf *dev, float *val) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(val); + + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + + IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, { + log_debug("%s: FPGA command mode not supported\n", __FUNCTION__); + return BLADERF_ERR_UNSUPPORTED; + }); + + WITH_MUTEX(&dev->lock, { *val = ad9361_get_temp(phy) / 1000.0F; }); + + return 0; +} + +int bladerf_get_rfic_rssi(struct bladerf *dev, + bladerf_channel ch, + int *pre_rssi, + int *sym_rssi) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(pre_rssi); + NULL_CHECK(sym_rssi); + + struct bladerf2_board_data *board_data = dev->board_data; + struct controller_fns const *rfic = board_data->rfic; + + WITH_MUTEX(&dev->lock, { + CHECK_STATUS_LOCKED(rfic->get_rssi(dev, ch, pre_rssi, sym_rssi)); + }); + + return 0; +} + +int bladerf_get_rfic_ctrl_out(struct bladerf *dev, uint8_t *ctrl_out) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(ctrl_out); + + WITH_MUTEX(&dev->lock, { + uint32_t reg; + + /* Read RFFE control register */ + CHECK_STATUS_LOCKED(dev->backend->rffe_control_read(dev, ®)); + + *ctrl_out = (uint8_t)((reg >> RFFE_CONTROL_CTRL_OUT) & 0xFF); + }); + + return 0; +} + +int bladerf_get_rfic_rx_fir(struct bladerf *dev, bladerf_rfic_rxfir *rxfir) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(rxfir); + + struct bladerf2_board_data *board_data = dev->board_data; + struct controller_fns const *rfic = board_data->rfic; + bladerf_channel const ch = BLADERF_CHANNEL_RX(0); + + WITH_MUTEX(&dev->lock, { + CHECK_STATUS_LOCKED(rfic->get_filter(dev, ch, rxfir, NULL)); + }); + + return 0; +} + +int bladerf_set_rfic_rx_fir(struct bladerf *dev, bladerf_rfic_rxfir rxfir) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + + struct bladerf2_board_data *board_data = dev->board_data; + struct controller_fns const *rfic = board_data->rfic; + struct bladerf_range const sr_range = bladerf2_sample_rate_range_4x; + bladerf_channel const ch = BLADERF_CHANNEL_RX(0); + + WITH_MUTEX(&dev->lock, { + /* Verify that sample rate is not too low */ + if (rxfir != BLADERF_RFIC_RXFIR_DEC4) { + bladerf_sample_rate sr; + + CHECK_STATUS_LOCKED(dev->board->get_sample_rate(dev, ch, &sr)); + + if (is_within_range(&sr_range, sr)) { + log_error("%s: sample rate too low for filter (%d < %d)\n", + __FUNCTION__, sr, sr_range.min); + MUTEX_UNLOCK(&dev->lock); + return BLADERF_ERR_INVAL; + } + } + + CHECK_STATUS_LOCKED(rfic->set_filter(dev, ch, rxfir, 0)); + }); + + return 0; +} + +int bladerf_get_rfic_tx_fir(struct bladerf *dev, bladerf_rfic_txfir *txfir) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(txfir); + + struct bladerf2_board_data *board_data = dev->board_data; + struct controller_fns const *rfic = board_data->rfic; + bladerf_channel const ch = BLADERF_CHANNEL_TX(0); + + WITH_MUTEX(&dev->lock, { + CHECK_STATUS_LOCKED(rfic->get_filter(dev, ch, NULL, txfir)); + }); + + return 0; +} + +int bladerf_set_rfic_tx_fir(struct bladerf *dev, bladerf_rfic_txfir txfir) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + + struct bladerf2_board_data *board_data = dev->board_data; + struct controller_fns const *rfic = board_data->rfic; + struct bladerf_range const sr_range = bladerf2_sample_rate_range_4x; + bladerf_channel const ch = BLADERF_CHANNEL_TX(0); + + WITH_MUTEX(&dev->lock, { + /* Verify that sample rate is not too low */ + if (txfir != BLADERF_RFIC_TXFIR_INT4) { + bladerf_sample_rate sr; + + CHECK_STATUS_LOCKED(dev->board->get_sample_rate(dev, ch, &sr)); + + if (is_within_range(&sr_range, sr)) { + log_error("%s: sample rate too low for filter (%d < %d)\n", + __FUNCTION__, sr, sr_range.min); + MUTEX_UNLOCK(&dev->lock); + return BLADERF_ERR_INVAL; + } + } + + CHECK_STATUS_LOCKED(rfic->set_filter(dev, ch, 0, txfir)); + }); + + return 0; +} + + +/******************************************************************************/ +/* Low level PLL Accessors */ +/******************************************************************************/ + +static int bladerf_pll_configure(struct bladerf *dev, uint16_t R, uint16_t N) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + + uint32_t init_array[3]; + bool is_enabled; + uint8_t i; + + if (R < 1 || R > 16383) { + RETURN_INVAL("R", "outside range [1,16383]"); + } + + if (N < 1 || N > 8191) { + RETURN_INVAL("N", "outside range [1,8191]"); + } + + /* Get the present state... */ + CHECK_STATUS(bladerf_get_pll_enable(dev, &is_enabled)); + + /* Enable the chip if applicable */ + if (!is_enabled) { + CHECK_STATUS(bladerf_set_pll_enable(dev, true)); + } + + /* Register 0: Reference Counter Latch */ + init_array[0] = 0; + /* R Counter: */ + init_array[0] |= (R & ((1 << 14) - 1)) << 2; + /* Hardcoded values: */ + /* Anti-backlash: 00 (2.9 ns) */ + /* Lock detect precision: 0 (three cycles) */ + + /* Register 1: N Counter Latch */ + init_array[1] = 1; + /* N Counter: */ + init_array[1] |= (N & ((1 << 13) - 1)) << 8; + /* Hardcoded values: */ + /* CP Gain: 0 (Setting 1) */ + + /* Register 2: Function Latch */ + init_array[2] = 2; + /* Hardcoded values: */ + /* Counter operation: 0 (Normal) */ + /* Power down control: 00 (Normal) */ + /* Muxout control: 0x1 (digital lock detect) */ + init_array[2] |= (1 & ((1 << 3) - 1)) << 4; + /* PD Polarity: 1 (positive) */ + init_array[2] |= 1 << 7; + /* CP three-state: 0 (normal) */ + /* Fastlock Mode: 00 (disabled) */ + /* Timer Counter Control: 0000 (3 PFD cycles) */ + /* Current Setting 1: 111 (5 mA) */ + init_array[2] |= 0x7 << 15; + /* Current Setting 2: 111 (5 mA) */ + init_array[2] |= 0x7 << 18; + + /* Write the values to the chip */ + for (i = 0; i < ARRAY_SIZE(init_array); ++i) { + CHECK_STATUS(bladerf_set_pll_register(dev, i, init_array[i])); + } + + /* Re-disable the chip if applicable */ + if (!is_enabled) { + CHECK_STATUS(bladerf_set_pll_enable(dev, false)); + } + + return 0; +} + +static int bladerf_pll_calculate_ratio(bladerf_frequency ref_freq, + bladerf_frequency clock_freq, + uint16_t *R, + uint16_t *N) +{ + NULL_CHECK(R); + NULL_CHECK(N); + + size_t const Rmax = 16383; + double const tol = 0.00001; + double target = (double)clock_freq / (double)ref_freq; + uint16_t R_try, N_try; + + struct bladerf_range const clock_frequency_range = { + FIELD_INIT(.min, 5000000), + FIELD_INIT(.max, 400000000), + FIELD_INIT(.step, 1), + FIELD_INIT(.scale, 1), + }; + + if (!is_within_range(&bladerf2_pll_refclk_range, ref_freq)) { + return BLADERF_ERR_RANGE; + } + + if (!is_within_range(&clock_frequency_range, clock_freq)) { + return BLADERF_ERR_RANGE; + } + + for (R_try = 1; R_try < Rmax; ++R_try) { + double ratio, delta; + + N_try = (uint16_t)(target * R_try + 0.5); + + if (N_try > 8191) { + continue; + } + + ratio = (double)N_try / (double)R_try; + delta = (ratio > target) ? (ratio - target) : (target - ratio); + + if (delta < tol) { + *R = R_try; + *N = N_try; + + return 0; + } + } + + RETURN_INVAL("requested ratio", "not achievable"); +} + +int bladerf_get_pll_lock_state(struct bladerf *dev, bool *locked) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(locked); + + WITH_MUTEX(&dev->lock, { + uint32_t reg; + + /* Read RFFE control register */ + CHECK_STATUS_LOCKED(dev->backend->rffe_control_read(dev, ®)); + + *locked = (reg >> RFFE_CONTROL_ADF_MUXOUT) & 0x1; + }); + + return 0; +} + +int bladerf_get_pll_enable(struct bladerf *dev, bool *enabled) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(enabled); + + WITH_MUTEX(&dev->lock, { + uint32_t data; + + CHECK_STATUS_LOCKED(dev->backend->config_gpio_read(dev, &data)); + + *enabled = (data >> CFG_GPIO_PLL_EN) & 0x01; + }); + + return 0; +} + +int bladerf_set_pll_enable(struct bladerf *dev, bool enable) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + + WITH_MUTEX(&dev->lock, { + struct bladerf2_board_data *board_data = dev->board_data; + uint32_t data; + + // Disable the trim DAC when we're using the PLL + if (enable) { + CHECK_STATUS_LOCKED(_bladerf2_set_trim_dac_enable(dev, false)); + } + + // Read current config GPIO value + CHECK_STATUS_LOCKED(dev->backend->config_gpio_read(dev, &data)); + + // Set the PLL enable bit accordingly + data &= ~(1 << CFG_GPIO_PLL_EN); + data |= ((enable ? 1 : 0) << CFG_GPIO_PLL_EN); + + // Write back the config GPIO + CHECK_STATUS_LOCKED(dev->backend->config_gpio_write(dev, data)); + + // Update our state flag + board_data->trim_source = enable ? TRIM_SOURCE_PLL : TRIM_SOURCE_NONE; + + // Enable the trim DAC if we're done with the + // PLL + if (!enable) { + CHECK_STATUS_LOCKED(_bladerf2_set_trim_dac_enable(dev, true)); + } + }); + + return 0; +} + +int bladerf_get_pll_refclk_range(struct bladerf *dev, + const struct bladerf_range **range) +{ + NULL_CHECK(range); + + *range = &bladerf2_pll_refclk_range; + + return 0; +} + +int bladerf_get_pll_refclk(struct bladerf *dev, bladerf_frequency *frequency) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(frequency); + + uint8_t const R_LATCH_REG = 0; + size_t const R_LATCH_SHIFT = 2; + uint32_t const R_LATCH_MASK = 0x3fff; + uint8_t const N_LATCH_REG = 1; + size_t const N_LATCH_SHIFT = 8; + uint32_t const N_LATCH_MASK = 0x1fff; + uint32_t reg; + uint16_t R, N; + + // Get the current R value (latch 0, bits 2-15) + CHECK_STATUS(bladerf_get_pll_register(dev, R_LATCH_REG, ®)); + R = (reg >> R_LATCH_SHIFT) & R_LATCH_MASK; + + // Get the current N value (latch 1, bits 8-20) + CHECK_STATUS(bladerf_get_pll_register(dev, N_LATCH_REG, ®)); + N = (reg >> N_LATCH_SHIFT) & N_LATCH_MASK; + + // We assume the system clock frequency is + // BLADERF_VCTCXO_FREQUENCY. If it isn't, do your + // own math + *frequency = R * BLADERF_VCTCXO_FREQUENCY / N; + + return 0; +} + +int bladerf_set_pll_refclk(struct bladerf *dev, bladerf_frequency frequency) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + + uint16_t R, N; + + // We assume the system clock frequency is + // BLADERF_VCTCXO_FREQUENCY. If it isn't, do your + // own math + CHECK_STATUS(bladerf_pll_calculate_ratio(frequency, + BLADERF_VCTCXO_FREQUENCY, &R, &N)); + + CHECK_STATUS(bladerf_pll_configure(dev, R, N)); + + return 0; +} + +int bladerf_get_pll_register(struct bladerf *dev, + uint8_t address, + uint32_t *val) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(val); + + WITH_MUTEX(&dev->lock, { + uint32_t data; + + address &= 0x03; + + CHECK_STATUS_LOCKED(dev->backend->adf400x_read(dev, address, &data)); + + *val = data; + }); + + return 0; +} + +int bladerf_set_pll_register(struct bladerf *dev, uint8_t address, uint32_t val) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + + WITH_MUTEX(&dev->lock, { + uint32_t data; + + address &= 0x03; + + data = val; + + CHECK_STATUS_LOCKED(dev->backend->adf400x_write(dev, address, data)); + }); + + return 0; +} + + +/******************************************************************************/ +/* Low level Power Source Accessors */ +/******************************************************************************/ + +int bladerf_get_power_source(struct bladerf *dev, bladerf_power_sources *src) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(src); + + WITH_MUTEX(&dev->lock, { + uint32_t data; + + CHECK_STATUS_LOCKED(dev->backend->config_gpio_read(dev, &data)); + + if ((data >> CFG_GPIO_POWERSOURCE) & 0x01) { + *src = BLADERF_PS_USB_VBUS; + } else { + *src = BLADERF_PS_DC; + } + }); + + return 0; +} + + +/******************************************************************************/ +/* Low level clock source selection accessors */ +/******************************************************************************/ + +int bladerf_get_clock_select(struct bladerf *dev, bladerf_clock_select *sel) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(sel); + + WITH_MUTEX(&dev->lock, { + uint32_t gpio; + + CHECK_STATUS_LOCKED(dev->backend->config_gpio_read(dev, &gpio)); + + if ((gpio & (1 << CFG_GPIO_CLOCK_SELECT)) == 0x0) { + *sel = CLOCK_SELECT_ONBOARD; + } else { + *sel = CLOCK_SELECT_EXTERNAL; + } + }); + + return 0; +} + +int bladerf_set_clock_select(struct bladerf *dev, bladerf_clock_select sel) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + + if (bladerf_device_speed(dev) == BLADERF_DEVICE_SPEED_HIGH) { + log_warning("USB 3.0 recommended for reliable clock select assignment.\n"); + } + + WITH_MUTEX(&dev->lock, { + uint32_t gpio; + + CHECK_STATUS_LOCKED(dev->backend->config_gpio_read(dev, &gpio)); + + // Set the clock select bit(s) accordingly + switch (sel) { + case CLOCK_SELECT_ONBOARD: + gpio &= ~(1 << CFG_GPIO_CLOCK_SELECT); + break; + case CLOCK_SELECT_EXTERNAL: + gpio |= (1 << CFG_GPIO_CLOCK_SELECT); + break; + default: + break; + } + + // Write back the config GPIO + CHECK_STATUS_LOCKED(dev->backend->config_gpio_write(dev, gpio)); + }); + + return 0; +} + + +/******************************************************************************/ +/* Low level clock buffer output accessors */ +/******************************************************************************/ + +int bladerf_get_clock_output(struct bladerf *dev, bool *state) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(state); + + WITH_MUTEX(&dev->lock, { + uint32_t gpio; + + CHECK_STATUS_LOCKED(dev->backend->config_gpio_read(dev, &gpio)); + + *state = ((gpio & (1 << CFG_GPIO_CLOCK_OUTPUT)) != 0x0); + }); + + return 0; +} + +int bladerf_set_clock_output(struct bladerf *dev, bool enable) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + + WITH_MUTEX(&dev->lock, { + uint32_t gpio; + + CHECK_STATUS_LOCKED(dev->backend->config_gpio_read(dev, &gpio)); + + // Set or clear the clock output enable bit + gpio &= ~(1 << CFG_GPIO_CLOCK_OUTPUT); + gpio |= ((enable ? 1 : 0) << CFG_GPIO_CLOCK_OUTPUT); + + // Write back the config GPIO + CHECK_STATUS_LOCKED(dev->backend->config_gpio_write(dev, gpio)); + }); + + return 0; +} + + +/******************************************************************************/ +/* Low level INA219 Accessors */ +/******************************************************************************/ + +int bladerf_get_pmic_register(struct bladerf *dev, + bladerf_pmic_register reg, + void *val) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_FPGA_LOADED); + NULL_CHECK(val); + + int rv; + + WITH_MUTEX(&dev->lock, { + switch (reg) { + case BLADERF_PMIC_CONFIGURATION: + case BLADERF_PMIC_CALIBRATION: + default: + rv = BLADERF_ERR_UNSUPPORTED; + break; + + case BLADERF_PMIC_VOLTAGE_SHUNT: + rv = ina219_read_shunt_voltage(dev, (float *)val); + break; + + case BLADERF_PMIC_VOLTAGE_BUS: + rv = ina219_read_bus_voltage(dev, (float *)val); + break; + + case BLADERF_PMIC_POWER: + rv = ina219_read_power(dev, (float *)val); + break; + + case BLADERF_PMIC_CURRENT: + rv = ina219_read_current(dev, (float *)val); + break; + } + }); + + return rv; +} + + +/******************************************************************************/ +/* Low level RF Switch Accessors */ +/******************************************************************************/ + +int bladerf_get_rf_switch_config(struct bladerf *dev, + bladerf_rf_switch_config *config) +{ + CHECK_BOARD_IS_BLADERF2(dev); + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(config); + + WITH_MUTEX(&dev->lock, { + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + struct controller_fns const *rfic = board_data->rfic; + uint32_t val; + uint32_t reg; + + /* Get AD9361 status */ + if (RFIC_COMMAND_HOST == rfic->command_mode) { + CHECK_AD936X_LOCKED(ad9361_get_tx_rf_port_output(phy, &val)); + } else { + val = 0xFF; + } + + config->tx1_rfic_port = val; + config->tx2_rfic_port = val; + + if (RFIC_COMMAND_HOST == rfic->command_mode) { + CHECK_AD936X_LOCKED(ad9361_get_rx_rf_port_input(phy, &val)); + } else { + val = 0xFF; + } + + config->rx1_rfic_port = val; + config->rx2_rfic_port = val; + + /* Read RFFE control register */ + CHECK_STATUS_LOCKED(dev->backend->rffe_control_read(dev, ®)); + + config->rx1_spdt_port = + (reg >> RFFE_CONTROL_RX_SPDT_1) & RFFE_CONTROL_SPDT_MASK; + config->rx2_spdt_port = + (reg >> RFFE_CONTROL_RX_SPDT_2) & RFFE_CONTROL_SPDT_MASK; + config->tx1_spdt_port = + (reg >> RFFE_CONTROL_TX_SPDT_1) & RFFE_CONTROL_SPDT_MASK; + config->tx2_spdt_port = + (reg >> RFFE_CONTROL_TX_SPDT_2) & RFFE_CONTROL_SPDT_MASK; + }); + + return 0; +} diff --git a/Radio/HW/BladeRF/src/board/bladerf2/capabilities.c b/Radio/HW/BladeRF/src/board/bladerf2/capabilities.c new file mode 100644 index 0000000..1373a0b --- /dev/null +++ b/Radio/HW/BladeRF/src/board/bladerf2/capabilities.c @@ -0,0 +1,106 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2015 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <inttypes.h> + +#include "helpers/version.h" +#include "log.h" + +#include "capabilities.h" + +uint64_t bladerf2_get_fw_capabilities(const struct bladerf_version *fw_version) +{ + uint64_t capabilities = 0; + + if (version_fields_greater_or_equal(fw_version, 1, 7, 1)) { + capabilities |= BLADERF_CAP_FW_LOOPBACK; + } + + if (version_fields_greater_or_equal(fw_version, 1, 8, 0)) { + capabilities |= BLADERF_CAP_QUERY_DEVICE_READY; + } + + if (version_fields_greater_or_equal(fw_version, 1, 9, 0)) { + capabilities |= BLADERF_CAP_READ_FW_LOG_ENTRY; + } + + if (version_fields_greater_or_equal(fw_version, 2, 1, 0)) { + capabilities |= BLADERF_CAP_FW_SUPPORTS_BLADERF2; + } + + if (version_fields_greater_or_equal(fw_version, 2, 3, 0)) { + capabilities |= BLADERF_CAP_FW_FLASH_ID; + } + + if (version_fields_greater_or_equal(fw_version, 2, 3, 1)) { + capabilities |= BLADERF_CAP_FW_FPGA_SOURCE; + } + + if (version_fields_greater_or_equal(fw_version, 2, 4, 0)) { + capabilities |= BLADERF_CAP_FW_SHORT_PACKET; + } + + return capabilities; +} + +uint64_t bladerf2_get_fpga_capabilities( + const struct bladerf_version *fpga_version) +{ + uint64_t capabilities = 0; + + if (version_fields_greater_or_equal(fpga_version, 0, 1, 0)) { + capabilities |= BLADERF_CAP_TIMESTAMPS; + } + + if (version_fields_greater_or_equal(fpga_version, 0, 3, 0)) { + capabilities |= BLADERF_CAP_PKT_HANDLER_FMT; + } + + if (version_fields_greater_or_equal(fpga_version, 0, 3, 2)) { + capabilities |= BLADERF_CAP_VCTCXO_TRIMDAC_READ; + } + + if (version_fields_greater_or_equal(fpga_version, 0, 4, 1)) { + capabilities |= BLADERF_CAP_MASKED_XBIO_WRITE; + } + + if (version_fields_greater_or_equal(fpga_version, 0, 6, 0)) { + capabilities |= BLADERF_CAP_TRX_SYNC_TRIG; + } + + if (version_fields_greater_or_equal(fpga_version, 0, 10, 0)) { + capabilities |= BLADERF_CAP_SCHEDULED_RETUNE; + } + + if (version_fields_greater_or_equal(fpga_version, 0, 10, 1)) { + capabilities |= BLADERF_CAP_FPGA_TUNING; + } + + if (version_fields_greater_or_equal(fpga_version, 0, 12, 0)) { + capabilities |= BLADERF_CAP_FPGA_PACKET_META; + } + + if (version_fields_greater_or_equal(fpga_version, 0, 15, 0)) { + capabilities |= BLADERF_CAP_FPGA_8BIT_SAMPLES; + } + + return capabilities; +} diff --git a/Radio/HW/BladeRF/src/board/bladerf2/capabilities.h b/Radio/HW/BladeRF/src/board/bladerf2/capabilities.h new file mode 100644 index 0000000..43f3bde --- /dev/null +++ b/Radio/HW/BladeRF/src/board/bladerf2/capabilities.h @@ -0,0 +1,52 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2015-2017 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* This file defines device capabilities added across libbladeRF, FX3, and FPGA + * versions that we can check for */ + +#ifndef BLADERF2_CAPABILITIES_H_ +#define BLADERF2_CAPABILITIES_H_ + +#include <stdint.h> + +#include "board/board.h" +#include "helpers/have_cap.h" + +/** + * Determine device's firmware capabilities. + * + * @param[in] fw_version Firmware version + * + * @return Capabilities bitmask + */ +uint64_t bladerf2_get_fw_capabilities(const struct bladerf_version *fw_version); + +/** + * Add capability bits based upon FPGA version stored in the device handle + * + * @param[in] fpga_version FPGA version + * + * @return Capabilities bitmask + */ +uint64_t bladerf2_get_fpga_capabilities( + const struct bladerf_version *fpga_version); + +#endif diff --git a/Radio/HW/BladeRF/src/board/bladerf2/common.c b/Radio/HW/BladeRF/src/board/bladerf2/common.c new file mode 100644 index 0000000..15202f8 --- /dev/null +++ b/Radio/HW/BladeRF/src/board/bladerf2/common.c @@ -0,0 +1,415 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2018 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> + +#include "board/board.h" +#include "capabilities.h" +#include "common.h" + + +/******************************************************************************/ +/* Constants */ +/******************************************************************************/ + +/* Board state to string map */ +char const *bladerf2_state_to_string[] = { + [STATE_UNINITIALIZED] = "Uninitialized", + [STATE_FIRMWARE_LOADED] = "Firmware Loaded", + [STATE_FPGA_LOADED] = "FPGA Loaded", + [STATE_INITIALIZED] = "Initialized", +}; + + +/******************************************************************************/ +/* Sample format control */ +/******************************************************************************/ + +static inline int requires_timestamps(bladerf_format format, bool *required) +{ + switch (format) { + case BLADERF_FORMAT_SC8_Q7_META: + case BLADERF_FORMAT_SC16_Q11_META: + case BLADERF_FORMAT_PACKET_META: + *required = true; + break; + + case BLADERF_FORMAT_SC8_Q7: + case BLADERF_FORMAT_SC16_Q11: + *required = false; + break; + + default: + return BLADERF_ERR_INVAL; + } + + return 0; +} + +int perform_format_config(struct bladerf *dev, + bladerf_direction dir, + bladerf_format format) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + + struct bladerf2_board_data *board_data = dev->board_data; + bool use_timestamps, other_using_timestamps; + bladerf_channel other; + uint32_t gpio_val; + int status; + + status = requires_timestamps(format, &use_timestamps); + if (status != 0) { + log_debug("%s: Invalid format: %d\n", __FUNCTION__, format); + return status; + } + + switch (dir) { + case BLADERF_RX: + other = BLADERF_TX; + break; + + case BLADERF_TX: + other = BLADERF_RX; + break; + + default: + log_debug("Invalid direction: %d\n", dir); + return BLADERF_ERR_INVAL; + } + + status = requires_timestamps(board_data->module_format[other], + &other_using_timestamps); + if ((status == 0) && (other_using_timestamps != use_timestamps)) { + log_debug("Format conflict detected: RX=%d, TX=%d\n"); + return BLADERF_ERR_INVAL; + } + + CHECK_STATUS(dev->backend->config_gpio_read(dev, &gpio_val)); + + if (use_timestamps) { + gpio_val |= BLADERF_GPIO_TIMESTAMP; + } else { + gpio_val &= ~BLADERF_GPIO_TIMESTAMP; + } + + if (format == BLADERF_FORMAT_PACKET_META) { + gpio_val |= BLADERF_GPIO_PACKET | BLADERF_GPIO_TIMESTAMP; + } else { + gpio_val &= ~BLADERF_GPIO_PACKET; + } + + if (format == BLADERF_FORMAT_SC8_Q7 || format == BLADERF_FORMAT_SC8_Q7_META) { + gpio_val |= BLADERF_GPIO_8BIT_MODE; + } else { + gpio_val &= ~BLADERF_GPIO_8BIT_MODE; + } + + CHECK_STATUS(dev->backend->config_gpio_write(dev, gpio_val)); + + board_data->module_format[dir] = format; + + return 0; +} + +int perform_format_deconfig(struct bladerf *dev, bladerf_direction dir) +{ + struct bladerf2_board_data *board_data = dev->board_data; + + switch (dir) { + case BLADERF_RX: + case BLADERF_TX: + /* We'll reconfigure the HW when we call perform_format_config, + * so we just need to update our stored information */ + board_data->module_format[dir] = -1; + break; + + default: + log_debug("%s: Invalid direction: %d\n", __FUNCTION__, dir); + return BLADERF_ERR_INVAL; + } + + return 0; +} + + +/******************************************************************************/ +/* Size checks */ +/******************************************************************************/ + +bool is_valid_fpga_size(struct bladerf *dev, bladerf_fpga_size fpga, size_t len) +{ + /* We do not build FPGAs with compression enabled. Therfore, they + * will always have a fixed file size. + */ + char const env_override[] = "BLADERF_SKIP_FPGA_SIZE_CHECK"; + + bool valid; + size_t expected; + int status; + + status = dev->board->get_fpga_bytes(dev, &expected); + if (status < 0) { + log_error( + "Error %d querying FPGA size.\n", + status); + return false; + } + + /* Provide a means to override this check. This is intended to allow + * folks who know what they're doing to work around this quickly without + * needing to make a code change. (e.g., someone building a custom FPGA + * image that enables compressoin) */ + if (getenv(env_override)) { + log_info("Overriding FPGA size check per %s\n", env_override); + valid = true; + } else if (expected > 0) { + valid = (len == expected); + } else { + log_debug("Unknown FPGA type (%d). Using relaxed size criteria.\n", + fpga); + + if (len < (1 * 1024 * 1024)) { + valid = false; + } else if (len > + (dev->flash_arch->tsize_bytes - BLADERF_FLASH_ADDR_FPGA)) { + valid = false; + } else { + valid = true; + } + } + + if (!valid) { + log_warning("Detected potentially incorrect FPGA file (length was %d, " + "expected %d).\n", + len, expected); + + log_debug("If you are certain this file is valid, you may define\n" + "BLADERF_SKIP_FPGA_SIZE_CHECK in your environment to skip " + "this check.\n\n"); + } + + return valid; +} + +bool is_valid_fw_size(size_t len) +{ + /* Simple FW applications generally are significantly larger than this + */ + if (len < (50 * 1024)) { + return false; + } else if (len > BLADERF_FLASH_BYTE_LEN_FIRMWARE) { + return false; + } else { + return true; + } +} + +bladerf_tuning_mode default_tuning_mode(struct bladerf *dev) +{ + struct bladerf2_board_data *board_data = dev->board_data; + bladerf_tuning_mode mode; + char const *env_var; + extern struct controller_fns const rfic_fpga_control; + + mode = BLADERF_TUNING_MODE_HOST; + + /* Detect TX FPGA bug and report warning */ + if (BLADERF_TUNING_MODE_FPGA == mode && rfic_fpga_control.is_present(dev) && + version_fields_less_than(&board_data->fpga_version, 0, 10, 2)) { + log_warning("FPGA v%u.%u.%u has errata related to FPGA-based tuning; " + "defaulting to host-based tuning. To use FPGA-based " + "tuning, update to FPGA v%u.%u.%u, or set the " + "BLADERF_DEFAULT_TUNING_MODE enviroment variable to " + "'fpga'.\n", + board_data->fpga_version.major, + board_data->fpga_version.minor, + board_data->fpga_version.patch, 0, 10, 2); + mode = BLADERF_TUNING_MODE_HOST; + } + + env_var = getenv("BLADERF_DEFAULT_TUNING_MODE"); + + if (env_var != NULL) { + if (!strcasecmp("host", env_var)) { + mode = BLADERF_TUNING_MODE_HOST; + } else if (!strcasecmp("fpga", env_var)) { + /* Capability check */ + if (!have_cap(board_data->capabilities, BLADERF_CAP_FPGA_TUNING)) { + log_error("The loaded FPGA version (%u.%u.%u) does not support " + "the tuning mode being used to override the default. " + "Ignoring BLADERF_DEFAULT_TUNING_MODE.\n", + board_data->fpga_version.major, + board_data->fpga_version.minor, + board_data->fpga_version.patch); + } else { + mode = BLADERF_TUNING_MODE_FPGA; + } + } else { + log_debug("Invalid tuning mode override: %s\n", env_var); + } + } + + /* Check if the FPGA actually has RFIC control built in */ + if (BLADERF_TUNING_MODE_FPGA == mode && + !rfic_fpga_control.is_present(dev)) { + log_debug("FPGA does not have RFIC tuning capabilities, " + "defaulting to host-based control.\n"); + mode = BLADERF_TUNING_MODE_HOST; + } + + switch (mode) { + case BLADERF_TUNING_MODE_HOST: + log_debug("Default tuning mode: Host\n"); + break; + case BLADERF_TUNING_MODE_FPGA: + log_debug("Default tuning mode: FPGA\n"); + break; + default: + assert(!"Bug encountered."); + mode = BLADERF_TUNING_MODE_HOST; + } + + return mode; +} + + +/******************************************************************************/ +/* Misc */ +/******************************************************************************/ + +bool check_total_sample_rate(struct bladerf *dev) +{ + int status; + uint32_t reg; + size_t i; + + bladerf_sample_rate rate_accum = 0; + size_t active_channels = 0; + + const bladerf_sample_rate MAX_SAMPLE_THROUGHPUT = 80000000; + + /* Read RFFE control register */ + status = dev->backend->rffe_control_read(dev, ®); + if (status < 0) { + return false; + } + + /* Accumulate sample rates for all channels */ + if (_rffe_dir_enabled(reg, BLADERF_RX)) { + bladerf_sample_rate rx_rate; + + status = + dev->board->get_sample_rate(dev, BLADERF_CHANNEL_RX(0), &rx_rate); + if (status < 0) { + return false; + } + + for (i = 0; i < dev->board->get_channel_count(dev, BLADERF_RX); ++i) { + if (_rffe_ch_enabled(reg, BLADERF_CHANNEL_RX(i))) { + rate_accum += rx_rate; + ++active_channels; + } + } + } + + if (_rffe_dir_enabled(reg, BLADERF_TX)) { + bladerf_sample_rate tx_rate; + + status = + dev->board->get_sample_rate(dev, BLADERF_CHANNEL_TX(0), &tx_rate); + if (status < 0) { + return false; + } + + for (i = 0; i < dev->board->get_channel_count(dev, BLADERF_TX); ++i) { + if (_rffe_ch_enabled(reg, BLADERF_CHANNEL_TX(i))) { + rate_accum += tx_rate; + ++active_channels; + } + } + } + + log_verbose("%s: active_channels=%d, rate_accum=%d, maximum=%d\n", + __FUNCTION__, active_channels, rate_accum, + MAX_SAMPLE_THROUGHPUT); + + if (rate_accum > MAX_SAMPLE_THROUGHPUT) { + log_warning("The total sample throughput for the %d active channel%s, " + "%g Msps, is greater than the recommended maximum sample " + "throughput, %g Msps. You may experience dropped samples " + "unless the sample rate is reduced, or some channels are " + "deactivated.\n", + active_channels, (active_channels == 1 ? "" : "s"), + rate_accum / 1e6, MAX_SAMPLE_THROUGHPUT / 1e6); + return false; + } + + return true; +} + +bool does_rffe_dir_have_enabled_ch(uint32_t reg, bladerf_direction dir) +{ + switch (dir) { + case BLADERF_RX: + return _rffe_ch_enabled(reg, BLADERF_CHANNEL_RX(0)) || + _rffe_ch_enabled(reg, BLADERF_CHANNEL_RX(1)); + case BLADERF_TX: + return _rffe_ch_enabled(reg, BLADERF_CHANNEL_TX(0)) || + _rffe_ch_enabled(reg, BLADERF_CHANNEL_TX(1)); + } + + return false; +} + +int get_gain_offset(struct bladerf *dev, bladerf_channel ch, float *offset) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(offset); + + struct bladerf_gain_range const *ranges = NULL; + bladerf_frequency frequency = 0; + size_t i, ranges_len; + + if (BLADERF_CHANNEL_IS_TX(ch)) { + ranges = bladerf2_tx_gain_ranges; + ranges_len = ARRAY_SIZE(bladerf2_tx_gain_ranges); + } else { + ranges = bladerf2_rx_gain_ranges; + ranges_len = ARRAY_SIZE(bladerf2_rx_gain_ranges); + } + + CHECK_STATUS(dev->board->get_frequency(dev, ch, &frequency)); + + for (i = 0; i < ranges_len; ++i) { + struct bladerf_gain_range const *r = &(ranges[i]); + struct bladerf_range const *rfreq = &(r->frequency); + + // if the frequency range matches, and the range name is null, + // then we found our match + if (is_within_range(rfreq, frequency) && (NULL == r->name)) { + *offset = r->offset; + return 0; + } + } + + return BLADERF_ERR_INVAL; +} diff --git a/Radio/HW/BladeRF/src/board/bladerf2/common.h b/Radio/HW/BladeRF/src/board/bladerf2/common.h new file mode 100644 index 0000000..5a88cb4 --- /dev/null +++ b/Radio/HW/BladeRF/src/board/bladerf2/common.h @@ -0,0 +1,494 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2018 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef BLADERF2_COMMON_H_ +#define BLADERF2_COMMON_H_ + +#if !defined(BLADERF_NIOS_BUILD) && !defined(BLADERF_NIOS_PC_SIMULATION) +#include "log.h" +#endif + +#include "bladerf2_common.h" +#include "helpers/version.h" +#include "streaming/sync.h" + + +/******************************************************************************/ +/* Types */ +/******************************************************************************/ + +enum bladerf2_vctcxo_trim_source { + TRIM_SOURCE_NONE, + TRIM_SOURCE_TRIM_DAC, + TRIM_SOURCE_PLL, + TRIM_SOURCE_AUXDAC +}; + +enum bladerf2_rfic_command_mode { + RFIC_COMMAND_HOST, /**< Host-based control */ + RFIC_COMMAND_FPGA, /**< FPGA-based control */ +}; + +struct controller_fns { + bool (*is_present)(struct bladerf *dev); + bool (*is_initialized)(struct bladerf *dev); + bool (*is_standby)(struct bladerf *dev); + int (*get_init_state)(struct bladerf *dev, bladerf_rfic_init_state *state); + + int (*initialize)(struct bladerf *dev); + int (*standby)(struct bladerf *dev); + int (*deinitialize)(struct bladerf *dev); + + int (*enable_module)(struct bladerf *dev, bladerf_channel ch, bool enable); + + int (*get_sample_rate)(struct bladerf *dev, + bladerf_channel ch, + bladerf_sample_rate *rate); + int (*set_sample_rate)(struct bladerf *dev, + bladerf_channel ch, + bladerf_sample_rate rate); + + int (*get_frequency)(struct bladerf *dev, + bladerf_channel ch, + bladerf_frequency *frequency); + int (*set_frequency)(struct bladerf *dev, + bladerf_channel ch, + bladerf_frequency frequency); + int (*select_band)(struct bladerf *dev, + bladerf_channel ch, + bladerf_frequency frequency); + + int (*get_bandwidth)(struct bladerf *dev, + bladerf_channel ch, + bladerf_bandwidth *bandwidth); + int (*set_bandwidth)(struct bladerf *dev, + bladerf_channel ch, + bladerf_bandwidth bandwidth, + bladerf_bandwidth *actual); + + int (*get_gain_mode)(struct bladerf *dev, + bladerf_channel ch, + bladerf_gain_mode *mode); + int (*set_gain_mode)(struct bladerf *dev, + bladerf_channel ch, + bladerf_gain_mode mode); + + int (*get_gain)(struct bladerf *dev, bladerf_channel ch, int *gain); + int (*set_gain)(struct bladerf *dev, bladerf_channel ch, int gain); + + int (*get_gain_stage)(struct bladerf *dev, + bladerf_channel ch, + char const *stage, + int *gain); + int (*set_gain_stage)(struct bladerf *dev, + bladerf_channel ch, + char const *stage, + int gain); + + int (*get_rssi)(struct bladerf *dev, + bladerf_channel ch, + int *pre_rssi, + int *sym_rssi); + + int (*get_filter)(struct bladerf *dev, + bladerf_channel ch, + bladerf_rfic_rxfir *rxfir, + bladerf_rfic_txfir *txfir); + int (*set_filter)(struct bladerf *dev, + bladerf_channel ch, + bladerf_rfic_rxfir rxfir, + bladerf_rfic_txfir txfir); + + int (*get_txmute)(struct bladerf *dev, bladerf_channel ch, bool *state); + int (*set_txmute)(struct bladerf *dev, bladerf_channel ch, bool state); + + int (*store_fastlock_profile)(struct bladerf *dev, + bladerf_channel ch, + uint32_t profile); + + int (*save_fastlock_profile)(struct bladerf *dev, + bladerf_channel ch, + uint32_t profile, + uint8_t *values); + + enum bladerf2_rfic_command_mode const command_mode; +}; + +struct bladerf2_board_data { + /* Board state */ + enum { + STATE_UNINITIALIZED, + STATE_FIRMWARE_LOADED, + STATE_FPGA_LOADED, + STATE_INITIALIZED, + } state; + + /* AD9361 PHY Handle */ + struct ad9361_rf_phy *phy; + + /* RFIC configuration parameters */ + void *rfic_init_params; + + /* Bitmask of capabilities determined by version numbers */ + uint64_t capabilities; + + /* Format currently being used with a module, or -1 if module is not used */ + bladerf_format module_format[NUM_MODULES]; + + /* Which mode of operation we use for tuning */ + bladerf_tuning_mode tuning_mode; + + /* Board properties */ + bladerf_fpga_size fpga_size; + /* Data message size */ + size_t msg_size; + + /* Version information */ + struct bladerf_version fpga_version; + struct bladerf_version fw_version; + char fpga_version_str[BLADERF_VERSION_STR_MAX + 1]; + char fw_version_str[BLADERF_VERSION_STR_MAX + 1]; + + /* Synchronous interface handles */ + struct bladerf_sync sync[2]; + + /* VCTCXO trim state */ + enum bladerf2_vctcxo_trim_source trim_source; + uint16_t trimdac_last_value; /**< saved running value */ + uint16_t trimdac_stored_value; /**< cached value read from SPI flash */ + + /* Quick Tune Profile Status */ + uint16_t quick_tune_tx_profile; + uint16_t quick_tune_rx_profile; + + /* RFIC backend command handling */ + struct controller_fns const *rfic; + + /* RFIC FIR Filter status */ + bladerf_rfic_rxfir rxfir; + bladerf_rfic_txfir txfir; + + /* If true, RFIC control will be fully de-initialized on close, instead of + * just put into a standby state. */ + bool rfic_reset_on_close; +}; + +struct bladerf_rfic_status_register { + bool rfic_initialized; + size_t write_queue_length; +}; + + +/******************************************************************************/ +/* Externs */ +/******************************************************************************/ + +extern AD9361_InitParam bladerf2_rfic_init_params; +extern AD9361_InitParam bladerf2_rfic_init_params_fastagc_burst; +extern AD9361_RXFIRConfig bladerf2_rfic_rx_fir_config; +extern AD9361_TXFIRConfig bladerf2_rfic_tx_fir_config; +extern AD9361_RXFIRConfig bladerf2_rfic_rx_fir_config_dec2; +extern AD9361_TXFIRConfig bladerf2_rfic_tx_fir_config_int2; +extern AD9361_RXFIRConfig bladerf2_rfic_rx_fir_config_dec4; +extern AD9361_TXFIRConfig bladerf2_rfic_tx_fir_config_int4; +extern const float ina219_r_shunt; + + +/******************************************************************************/ +/* Constants */ +/******************************************************************************/ + +extern char const *bladerf2_state_to_string[4]; + + +/******************************************************************************/ +/* Macros */ +/******************************************************************************/ + +/* Macro for logging and returning an error status. This should be used for + * errors defined in the \ref RETCODES list. */ +#define RETURN_ERROR_STATUS(_what, _status) \ + do { \ + log_error("%s: %s failed: %s\n", __FUNCTION__, _what, \ + bladerf_strerror(_status)); \ + return _status; \ + } while (0) + +/* Macro for converting, logging, and returning libad9361 error codes. */ +#define RETURN_ERROR_AD9361(_what, _status) \ + do { \ + RETURN_ERROR_STATUS(_what, errno_ad9361_to_bladerf(_status)); \ + } while (0) + +/* Macro for logging and returning ::BLADERF_ERR_INVAL */ +#define RETURN_INVAL_ARG(_what, _arg, _why) \ + do { \ + log_error("%s: %s '%s' invalid: %s\n", __FUNCTION__, _what, #_arg, \ + _why); \ + return BLADERF_ERR_INVAL; \ + } while (0) + +#define RETURN_INVAL(_what, _why) \ + do { \ + log_error("%s: %s invalid: %s\n", __FUNCTION__, _what, _why); \ + return BLADERF_ERR_INVAL; \ + } while (0) + +/** + * @brief Null test + * + * @param _var The variable to check + * + * @return RETURN_INVAL if _var is null, continues otherwise + */ +#define NULL_CHECK(_var) \ + do { \ + if (NULL == _var) { \ + RETURN_INVAL(#_var, "is null"); \ + } \ + } while (0) + +/** + * @brief Null test, with mutex unlock on failure + * + * @param _var The variable to check + * + * @return RETURN_INVAL if _var is null, continues otherwise + */ +#define NULL_CHECK_LOCKED(_var) \ + do { \ + NULL_CHECK(dev); \ + \ + if (NULL == _var) { \ + MUTEX_UNLOCK(__lock); \ + RETURN_INVAL(#_var, "is null"); \ + } \ + } while (0) + +/** + * @brief Board state check + * + * @param _state Minimum sufficient board state + * + * @return BLADERF_ERR_NOT_INIT if board's state is less than _state, continues + * otherwise + */ +#define CHECK_BOARD_STATE(_state) \ + do { \ + NULL_CHECK(dev); \ + NULL_CHECK(dev->board); \ + \ + struct bladerf2_board_data *_bd = dev->board_data; \ + \ + if (_bd->state < _state) { \ + log_error("%s: Board state insufficient for operation " \ + "(current \"%s\", requires \"%s\").\n", \ + __FUNCTION__, bladerf2_state_to_string[_bd->state], \ + bladerf2_state_to_string[_state]); \ + \ + return BLADERF_ERR_NOT_INIT; \ + } \ + } while (0) + +/** + * @brief Test if board is a bladeRF 2 + * + * @param _dev Device handle + * + * @return BLADERF_ERR_UNSUPPORTED if board is not a bladeRF 2, continues + * otherwise + */ +#define CHECK_BOARD_IS_BLADERF2(_dev) \ + do { \ + NULL_CHECK(_dev); \ + NULL_CHECK(_dev->board); \ + \ + if (_dev->board != &bladerf2_board_fns) { \ + log_error("%s: Board type \"%s\" not supported\n", __FUNCTION__, \ + _dev->board->name); \ + return BLADERF_ERR_UNSUPPORTED; \ + } \ + } while (0) + +/** + * @brief Call a function and return early if it fails + * + * @param _fn The function + * + * @return function return value if less than zero; continues otherwise + */ +#define CHECK_STATUS(_fn) \ + do { \ + int _s = _fn; \ + if (_s < 0) { \ + RETURN_ERROR_STATUS(#_fn, _s); \ + } \ + } while (0) + +/** + * @brief Call a function and goto error if it fails + * + * @param _fn The function + * + * @note `int status` must be declared in the including scope + */ +#define CHECK_STATUS_GOTO(_fn) \ + do { \ + status = _fn; \ + if (status < 0) { \ + log_error("%s: %i failed: %s\n", #_fn, status, \ + bladerf_strerror(status)); \ + goto error; \ + } \ + } while (0) + +/** + * @brief Call a function and, if it fails, unlock the mutex and return + * + * @param _fn The function + * + * @return function return value if less than zero; continues otherwise + */ +#define CHECK_STATUS_LOCKED(_fn) \ + do { \ + int _s = _fn; \ + if (_s < 0) { \ + MUTEX_UNLOCK(__lock); \ + RETURN_ERROR_STATUS(#_fn, _s); \ + } \ + } while (0) + +/** + * @brief Call a function and return early if it fails, with error translation + * from AD936x to bladeRF return codes + * + * @param _fn The function + * + * @return function return value if less than zero; continues otherwise + */ +#define CHECK_AD936X(_fn) \ + do { \ + int _s = _fn; \ + if (_s < 0) { \ + RETURN_ERROR_AD9361(#_fn, _s); \ + } \ + } while (0) + +/** + * @brief Call a function and, if it fails, unlock the mutex and return, with + * error translation from AD936x to bladeRF return codes + * + * @param _fn The function + * + * @return function return value if less than zero; continues otherwise + */ +#define CHECK_AD936X_LOCKED(_fn) \ + do { \ + int _s = _fn; \ + if (_s < 0) { \ + MUTEX_UNLOCK(__lock); \ + RETURN_ERROR_AD9361(#_fn, _s); \ + } \ + } while (0) + +/** + * @brief Execute a command block with a mutex lock + * + * @note Variables declared within the including scope must be declared + * one-per-line. + * + * @param _lock Lock to hold + * @param _thing Block to execute + */ +#define WITH_MUTEX(_lock, _thing) \ + do { \ + MUTEX *__lock = _lock; \ + \ + MUTEX_LOCK(__lock); \ + _thing; \ + MUTEX_UNLOCK(__lock); \ + } while (0) + +/** + * @brief Execute command block, conditional to _mode + * + * @param _dev Device handle + * @param _mode Command mode + * @param _thing Block to do if it happens + */ +#define IF_COMMAND_MODE(_dev, _mode, _thing) \ + do { \ + NULL_CHECK(_dev); \ + NULL_CHECK(_dev->board_data); \ + \ + struct bladerf2_board_data *bd = _dev->board_data; \ + \ + if (bd->rfic->command_mode == _mode) { \ + _thing; \ + }; \ + } while (0) + + +/******************************************************************************/ +/* Functions */ +/******************************************************************************/ + +/** + * Perform the neccessary device configuration for the specified format + * (e.g., enabling/disabling timestamp support), first checking that the + * requested format would not conflict with the other stream direction. + * + * @param dev Device handle + * @param[in] dir Direction that is currently being configured + * @param[in] format Format the channel is being configured for + * + * @return 0 on success, BLADERF_ERR_* on failure + */ +int perform_format_config(struct bladerf *dev, + bladerf_direction dir, + bladerf_format format); + +/** + * Deconfigure and update any state pertaining what a format that a stream + * direction is no longer using. + * + * @param dev Device handle + * @param[in] dir Direction that is currently being deconfigured + * + * @return 0 on success, BLADERF_ERR_* on failure + */ +int perform_format_deconfig(struct bladerf *dev, bladerf_direction dir); + +bool is_valid_fpga_size(struct bladerf *dev, + bladerf_fpga_size fpga, + size_t len); + +bool is_valid_fw_size(size_t len); + +bladerf_tuning_mode default_tuning_mode(struct bladerf *dev); + +bool check_total_sample_rate(struct bladerf *dev); + +bool does_rffe_dir_have_enabled_ch(uint32_t reg, bladerf_direction dir); + +int get_gain_offset(struct bladerf *dev, bladerf_channel ch, float *offset); + +#endif // BLADERF2_COMMON_H_ diff --git a/Radio/HW/BladeRF/src/board/bladerf2/compatibility.c b/Radio/HW/BladeRF/src/board/bladerf2/compatibility.c new file mode 100644 index 0000000..7ee0b8e --- /dev/null +++ b/Radio/HW/BladeRF/src/board/bladerf2/compatibility.c @@ -0,0 +1,50 @@ +#include "host_config.h" + +#include "helpers/version.h" + +/* Firmware-FPGA compatibility tables + * + * This list should be kept in decending order, such that the most recent + * versions are first, and the last entry should contain the earliest version + * that libbladeRF supports. + */ + +#define VERSION(major, minor, patch) { major, minor, patch, NULL } + +static const struct compat fw_compat[] = { + /* Firmware requires >= FPGA */ + { VERSION(2, 4, 0), VERSION(0, 6, 0) }, + { VERSION(2, 3, 2), VERSION(0, 6, 0) }, + { VERSION(2, 3, 1), VERSION(0, 6, 0) }, + { VERSION(2, 3, 0), VERSION(0, 6, 0) }, + { VERSION(2, 2, 0), VERSION(0, 6, 0) }, + { VERSION(2, 1, 1), VERSION(0, 6, 0) }, + { VERSION(2, 1, 0), VERSION(0, 6, 0) }, + { VERSION(2, 0, 0), VERSION(0, 6, 0) }, +}; + +const struct version_compat_table bladerf2_fw_compat_table = {fw_compat, ARRAY_SIZE(fw_compat)}; + +static const struct compat fpga_compat[] = { + /* FPGA requires >= Firmware */ + { VERSION(0, 15, 3), VERSION(2, 4, 0) }, + { VERSION(0, 15, 2), VERSION(2, 4, 0) }, + { VERSION(0, 15, 1), VERSION(2, 4, 0) }, + { VERSION(0, 15, 0), VERSION(2, 4, 0) }, + { VERSION(0, 14, 0), VERSION(2, 4, 0) }, + { VERSION(0, 12, 0), VERSION(2, 2, 0) }, + { VERSION(0, 11, 1), VERSION(2, 1, 0) }, + { VERSION(0, 11, 0), VERSION(2, 1, 0) }, + { VERSION(0, 10, 2), VERSION(2, 1, 0) }, + { VERSION(0, 10, 1), VERSION(2, 1, 0) }, + { VERSION(0, 10, 0), VERSION(2, 1, 0) }, + { VERSION(0, 9, 0), VERSION(2, 1, 0) }, + { VERSION(0, 8, 0), VERSION(2, 1, 0) }, + { VERSION(0, 7, 3), VERSION(2, 1, 0) }, + { VERSION(0, 7, 2), VERSION(2, 1, 0) }, + { VERSION(0, 7, 1), VERSION(2, 0, 0) }, + { VERSION(0, 7, 0), VERSION(2, 0, 0) }, + { VERSION(0, 6, 0), VERSION(2, 0, 0) }, +}; + +const struct version_compat_table bladerf2_fpga_compat_table = {fpga_compat, ARRAY_SIZE(fpga_compat)}; diff --git a/Radio/HW/BladeRF/src/board/bladerf2/compatibility.h b/Radio/HW/BladeRF/src/board/bladerf2/compatibility.h new file mode 100644 index 0000000..67447b9 --- /dev/null +++ b/Radio/HW/BladeRF/src/board/bladerf2/compatibility.h @@ -0,0 +1,9 @@ +#ifndef BLADERF2_COMPATIBILITY_H_ +#define BLADERF2_COMPATIBILITY_H_ + +#include "helpers/version.h" + +extern const struct version_compat_table bladerf2_fw_compat_table; +extern const struct version_compat_table bladerf2_fpga_compat_table; + +#endif diff --git a/Radio/HW/BladeRF/src/board/bladerf2/rfic_fpga.c b/Radio/HW/BladeRF/src/board/bladerf2/rfic_fpga.c new file mode 100644 index 0000000..089a8cb --- /dev/null +++ b/Radio/HW/BladeRF/src/board/bladerf2/rfic_fpga.c @@ -0,0 +1,767 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2018 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <libbladeRF.h> + +#include "board/board.h" +#include "common.h" +#include "conversions.h" +#include "iterators.h" +#include "log.h" + +// #define BLADERF_HEADLESS_C_DEBUG + + +/******************************************************************************/ +/* Declarations */ +/******************************************************************************/ + +/** + * @brief RFIC Command Read + * + * Executes a command using the FPGA-based RFIC interface, returning the reply + * value in *data. + * + * @param dev Device handle + * @param[in] ch Channel to act upon; use ::BLADERF_CHANNEL_INVALID for + * non-channel-specific commands + * @param[in] cmd Command + * @param[out] data Pointer for response data + * + * @return 0 on success, value from \ref RETCODES list on failure + */ +static int _rfic_cmd_read(struct bladerf *dev, + bladerf_channel ch, + bladerf_rfic_command cmd, + uint64_t *data); + +/** + * @brief RFIC Command Write + * + * Executes a command using the FPGA-based RFIC interface, using the supplied + * data value as an argument. Waits until the command has been executed before + * returning. + * + * @param dev Device handle + * @param[in] ch Channel to act upon; use ::BLADERF_CHANNEL_INVALID for + * non-channel-specific commands + * @param[in] cmd Command + * @param[in] data Argument for command + * + * @return 0 on success, value from \ref RETCODES list on failure + */ +static int _rfic_cmd_write(struct bladerf *dev, + bladerf_channel ch, + bladerf_rfic_command cmd, + uint64_t data); + + +/******************************************************************************/ +/* Helpers */ +/******************************************************************************/ + +/* Build RFIC address from bladerf_rfic_command and bladerf_channel */ +#define RFIC_ADDRESS(cmd, ch) ((cmd & 0xFF) + ((ch & 0xF) << 8)) + +static int _rfic_fpga_get_status( + struct bladerf *dev, struct bladerf_rfic_status_register *rfic_status) +{ + uint64_t sreg = 0; + int status; + + status = _rfic_cmd_read(dev, BLADERF_CHANNEL_INVALID, + BLADERF_RFIC_COMMAND_STATUS, &sreg); + + rfic_status->rfic_initialized = ((sreg >> 0) & 0x1); + rfic_status->write_queue_length = ((sreg >> 8) & 0xFF); + + return status; +} + +static int _rfic_fpga_get_status_wqlen(struct bladerf *dev) +{ + struct bladerf_rfic_status_register rfic_status; + int status; + + status = _rfic_fpga_get_status(dev, &rfic_status); + if (status < 0) { + return status; + } + +#ifdef BLADERF_HEADLESS_C_DEBUG + if (rfic_status.write_queue_length > 0) { + log_verbose("%s: queue len = %d\n", __FUNCTION__, + rfic_status.write_queue_length); + } +#endif + + return (int)rfic_status.write_queue_length; +} + +static int _rfic_fpga_spinwait(struct bladerf *dev) +{ + size_t const TRIES = 30; + unsigned int const DELAY = 100; + size_t count = 0; + int jobs; + + /* Poll the CPU and spin until the job has been completed. */ + do { + jobs = _rfic_fpga_get_status_wqlen(dev); + if (0 != jobs) { + usleep(DELAY); + } + } while ((0 != jobs) && (++count < TRIES)); + + /* If it's simply taking too long to dequeue the command, status will + * have the number of items in the queue. Bonk this down to a timeout. */ + if (jobs > 0) { + jobs = BLADERF_ERR_TIMEOUT; + } + + return jobs; +} + + +/******************************************************************************/ +/* Low level RFIC Accessors */ +/******************************************************************************/ + +static int _rfic_cmd_read(struct bladerf *dev, + bladerf_channel ch, + bladerf_rfic_command cmd, + uint64_t *data) +{ + return dev->backend->rfic_command_read(dev, RFIC_ADDRESS(cmd, ch), data); +} + +static int _rfic_cmd_write(struct bladerf *dev, + bladerf_channel ch, + bladerf_rfic_command cmd, + uint64_t data) +{ + /* Perform the write command. */ + CHECK_STATUS( + dev->backend->rfic_command_write(dev, RFIC_ADDRESS(cmd, ch), data)); + + /* Block until the job has been completed. */ + return _rfic_fpga_spinwait(dev); +} + + +/******************************************************************************/ +/* Initialization */ +/******************************************************************************/ + +static bool _rfic_fpga_is_present(struct bladerf *dev) +{ + uint64_t sreg = 0; + int status; + + status = _rfic_cmd_read(dev, BLADERF_CHANNEL_INVALID, + BLADERF_RFIC_COMMAND_STATUS, &sreg); + if (status < 0) { + return false; + } + + return true; +} + +static int _rfic_fpga_get_init_state(struct bladerf *dev, + bladerf_rfic_init_state *state) +{ + uint64_t data; + + CHECK_STATUS(_rfic_cmd_read(dev, BLADERF_CHANNEL_INVALID, + BLADERF_RFIC_COMMAND_INIT, &data)); + + *state = (bladerf_rfic_init_state)data; + + return 0; +} + +static bool _rfic_fpga_is_initialized(struct bladerf *dev) +{ + bladerf_rfic_init_state state; + int status; + + status = _rfic_fpga_get_init_state(dev, &state); + if (status < 0) { + log_error("%s: failed to get RFIC initialization state: %s\n", + __FUNCTION__, bladerf_strerror(status)); + return false; + } + + return BLADERF_RFIC_INIT_STATE_ON == state; +} + +static bool _rfic_fpga_is_standby(struct bladerf *dev) +{ + bladerf_rfic_init_state state; + int status; + + status = _rfic_fpga_get_init_state(dev, &state); + if (status < 0) { + log_error("%s: failed to get RFIC initialization state: %s\n", + __FUNCTION__, bladerf_strerror(status)); + return false; + } + + return BLADERF_RFIC_INIT_STATE_STANDBY == state; +} + +static int _rfic_fpga_initialize(struct bladerf *dev) +{ + log_debug("%s: initializing\n", __FUNCTION__); + + return _rfic_cmd_write(dev, BLADERF_CHANNEL_INVALID, + BLADERF_RFIC_COMMAND_INIT, + BLADERF_RFIC_INIT_STATE_ON); +} + +static int _rfic_fpga_standby(struct bladerf *dev) +{ + log_debug("%s: entering standby mode\n", __FUNCTION__); + + return _rfic_cmd_write(dev, BLADERF_CHANNEL_INVALID, + BLADERF_RFIC_COMMAND_INIT, + BLADERF_RFIC_INIT_STATE_STANDBY); +} + +static int _rfic_fpga_deinitialize(struct bladerf *dev) +{ + log_debug("%s: deinitializing\n", __FUNCTION__); + + return _rfic_cmd_write(dev, BLADERF_CHANNEL_INVALID, + BLADERF_RFIC_COMMAND_INIT, + BLADERF_RFIC_INIT_STATE_OFF); +} + + +/******************************************************************************/ +/* Enable */ +/******************************************************************************/ + +static int _rfic_fpga_enable_module(struct bladerf *dev, + bladerf_channel ch, + bool ch_enable) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct controller_fns const *rfic = board_data->rfic; + bladerf_direction dir = BLADERF_CHANNEL_IS_TX(ch) ? BLADERF_TX : BLADERF_RX; + uint32_t reg; /* RFFE register value */ + bool ch_enabled; /* Channel: initial state */ + bool ch_pending; /* Channel: target state is not initial state */ + bool dir_enabled; /* Direction: initial state */ + bool dir_enable; /* Direction: target state */ + bool dir_pending; /* Direction: target state is not initial state */ + bool be_toggle; /* Backend: toggle off/on to reset backend FIFO */ + bool be_teardown; /* Backend: disable backend module */ + bool be_setup; /* Backend: enable backend module */ + + /* Read RFFE control register */ + CHECK_STATUS(dev->backend->rffe_control_read(dev, ®)); + +#ifdef BLADERF_HEADLESS_C_DEBUG + uint32_t reg_old = reg; +#endif + + /* Calculate initial and target states */ + ch_enabled = _rffe_ch_enabled(reg, ch); + dir_enabled = _rffe_dir_enabled(reg, dir); + dir_enable = ch_enable || _rffe_dir_otherwise_enabled(reg, ch); + ch_pending = ch_enabled != ch_enable; + dir_pending = dir_enabled != dir_enable; + be_toggle = !BLADERF_CHANNEL_IS_TX(ch) && ch_enable && !dir_pending; + be_setup = be_toggle || (dir_pending && dir_enable); + be_teardown = be_toggle || (dir_pending && !dir_enable); + + /* Perform Direction Teardown */ + if (dir_pending && !dir_enable) { + sync_deinit(&board_data->sync[dir]); + perform_format_deconfig(dev, dir); + } + + /* Perform Channel Setup/Teardown */ + if (ch_pending) { + /* Set/unset TX mute */ + if (BLADERF_CHANNEL_IS_TX(ch)) { + CHECK_STATUS(rfic->set_txmute(dev, ch, !ch_enable)); + } + + /* Execute RFIC enable command. */ + CHECK_STATUS(_rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_ENABLE, + ch_enable ? 1 : 0)); + } + + /* Perform backend teardown */ + if (be_teardown) { + CHECK_STATUS(dev->backend->enable_module(dev, dir, false)); + } + + /* Perform backend setup */ + if (be_setup) { + CHECK_STATUS(dev->backend->enable_module(dev, dir, true)); + } + + /* Warn the user if the sample rate isn't reasonable */ + if (ch_pending && ch_enable) { + check_total_sample_rate(dev); + } + +#ifdef BLADERF_HEADLESS_C_DEBUG + /* Debug output... */ + if (BLADERF_LOG_LEVEL_VERBOSE == log_get_verbosity()) { + CHECK_STATUS(dev->backend->rffe_control_read(dev, ®)); + + log_verbose( + "%s: %s ch[en=%d->%d pend=%d] dir[en=%d->%d pend=%d] be[clr=%d " + "su=%d td=%d] reg=0x%08x->0x%08x\n", + __FUNCTION__, channel2str(ch), ch_enabled, ch_enable, ch_pending, + dir_enabled, dir_enable, dir_pending, be_toggle, be_setup, + be_teardown, reg_old, reg); + } +#endif + + return 0; +} + + +/******************************************************************************/ +/* Sample rate */ +/******************************************************************************/ + +static int _rfic_fpga_get_sample_rate(struct bladerf *dev, + bladerf_channel ch, + bladerf_sample_rate *rate) +{ + uint64_t readval; + + CHECK_STATUS( + _rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_SAMPLERATE, &readval)); + + *rate = (uint32_t)readval; + + return 0; +} + +static int _rfic_fpga_set_sample_rate(struct bladerf *dev, + bladerf_channel ch, + bladerf_sample_rate rate) +{ + return _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_SAMPLERATE, rate); +} + + +/******************************************************************************/ +/* Frequency */ +/******************************************************************************/ + +static int _rfic_fpga_get_frequency(struct bladerf *dev, + bladerf_channel ch, + bladerf_frequency *frequency) +{ + bladerf_frequency freq; + + CHECK_STATUS( + _rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_FREQUENCY, &freq)); + + *frequency = freq; + + return 0; +} + +static int _rfic_fpga_set_frequency(struct bladerf *dev, + bladerf_channel ch, + bladerf_frequency frequency) +{ + struct bladerf_range const *range = NULL; + + CHECK_STATUS(dev->board->get_frequency_range(dev, ch, &range)); + + if (!is_within_range(range, frequency)) { + return BLADERF_ERR_RANGE; + } + + return _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_FREQUENCY, frequency); +} + +static int _rfic_fpga_select_band(struct bladerf *dev, + bladerf_channel ch, + bladerf_frequency frequency) +{ + // This functionality is unnecessary with the headless architecture. + return 0; +} + + +/******************************************************************************/ +/* Bandwidth */ +/******************************************************************************/ + +static int _rfic_fpga_get_bandwidth(struct bladerf *dev, + bladerf_channel ch, + bladerf_bandwidth *bandwidth) +{ + uint64_t readval; + + CHECK_STATUS( + _rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_BANDWIDTH, &readval)); + + *bandwidth = (uint32_t)readval; + + return 0; +} + +static int _rfic_fpga_set_bandwidth(struct bladerf *dev, + bladerf_channel ch, + bladerf_bandwidth bandwidth, + bladerf_bandwidth *actual) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct controller_fns const *rfic = board_data->rfic; + struct bladerf_range const *range = NULL; + + CHECK_STATUS(dev->board->get_bandwidth_range(dev, ch, &range)); + + if (!is_within_range(range, bandwidth)) { + return BLADERF_ERR_RANGE; + } + + CHECK_STATUS( + _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_BANDWIDTH, bandwidth)); + + if (actual != NULL) { + return rfic->get_bandwidth(dev, ch, actual); + } + + return 0; +} + + +/******************************************************************************/ +/* Gain */ +/* These functions handle offset */ +/******************************************************************************/ + +static int _rfic_fpga_get_gain(struct bladerf *dev, + bladerf_channel ch, + int *gain) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct controller_fns const *rfic = board_data->rfic; + char const *stage = BLADERF_CHANNEL_IS_TX(ch) ? "dsa" : "full"; + int val; + float offset; + + CHECK_STATUS(get_gain_offset(dev, ch, &offset)); + + CHECK_STATUS(rfic->get_gain_stage(dev, ch, stage, &val)); + + *gain = __round_int(val + offset); + + return 0; +} + +static int _rfic_fpga_set_gain(struct bladerf *dev, + bladerf_channel ch, + int gain) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct controller_fns const *rfic = board_data->rfic; + char const *stage = BLADERF_CHANNEL_IS_TX(ch) ? "dsa" : "full"; + float offset; + + CHECK_STATUS(get_gain_offset(dev, ch, &offset)); + + return rfic->set_gain_stage(dev, ch, stage, __round_int(gain - offset)); +} + + +/******************************************************************************/ +/* Gain mode */ +/******************************************************************************/ + +static int _rfic_fpga_get_gain_mode(struct bladerf *dev, + bladerf_channel ch, + bladerf_gain_mode *mode) +{ + uint64_t readval; + + if (BLADERF_CHANNEL_IS_TX(ch)) { + log_warning("%s: automatic gain control not valid for TX channels\n", + __FUNCTION__); + *mode = BLADERF_GAIN_DEFAULT; + return 0; + } + + CHECK_STATUS( + _rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_GAINMODE, &readval)); + + *mode = (bladerf_gain_mode)readval; + + return 0; +} + +static int _rfic_fpga_set_gain_mode(struct bladerf *dev, + bladerf_channel ch, + bladerf_gain_mode mode) +{ + if (BLADERF_CHANNEL_IS_TX(ch)) { + log_warning("%s: automatic gain control not valid for TX channels\n", + __FUNCTION__); + return 0; + } + + return _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_GAINMODE, mode); +} + + +/******************************************************************************/ +/* Gain stage */ +/* These functions handle scaling */ +/******************************************************************************/ + +static int _rfic_fpga_get_gain_stage(struct bladerf *dev, + bladerf_channel ch, + char const *stage, + int *gain) +{ + struct bladerf_range const *range = NULL; + uint64_t val; + + if ((BLADERF_CHANNEL_IS_TX(ch) && strcmp(stage, "dsa") != 0) || + (!BLADERF_CHANNEL_IS_TX(ch) && strcmp(stage, "full") != 0)) { + log_error("%s: unknown gain stage '%s'\n", __FUNCTION__, stage); + return BLADERF_ERR_INVAL; + } + + CHECK_STATUS(dev->board->get_gain_stage_range(dev, ch, stage, &range)); + + CHECK_STATUS(_rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_GAIN, &val)); + + *gain = __unscale_int(range, val); + + if (BLADERF_CHANNEL_IS_TX(ch)) { + *gain = -(*gain); + } + + return 0; +} + +static int _rfic_fpga_set_gain_stage(struct bladerf *dev, + bladerf_channel ch, + char const *stage, + int gain) +{ + struct bladerf_range const *range = NULL; + int64_t val; + + if ((BLADERF_CHANNEL_IS_TX(ch) && strcmp(stage, "dsa") != 0) || + (!BLADERF_CHANNEL_IS_TX(ch) && strcmp(stage, "full") != 0)) { + log_error("%s: unknown gain stage '%s'\n", __FUNCTION__, stage); + return BLADERF_ERR_INVAL; + } + + CHECK_STATUS(dev->board->get_gain_stage_range(dev, ch, stage, &range)); + + if (BLADERF_CHANNEL_IS_TX(ch) && gain < -89) { + val = -89750; + } else { + val = __scale_int64(range, clamp_to_range(range, gain)); + } + + if (BLADERF_CHANNEL_IS_TX(ch)) { + val = -val; + } + + return _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_GAIN, (uint64_t)val); +} + + +/******************************************************************************/ +/* RSSI */ +/******************************************************************************/ + +static int _rfic_fpga_get_rssi(struct bladerf *dev, + bladerf_channel ch, + int *pre_rssi, + int *sym_rssi) +{ + uint64_t readval; + int16_t mult, pre, sym; + + CHECK_STATUS(_rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_RSSI, &readval)); + + mult = (int16_t)((readval >> BLADERF_RFIC_RSSI_MULT_SHIFT) & + BLADERF_RFIC_RSSI_MULT_MASK); + pre = (int16_t)((readval >> BLADERF_RFIC_RSSI_PRE_SHIFT) & + BLADERF_RFIC_RSSI_PRE_MASK); + sym = (int16_t)((readval >> BLADERF_RFIC_RSSI_SYM_SHIFT) & + BLADERF_RFIC_RSSI_SYM_MASK); + + *pre_rssi = __round_int(pre / (float)mult); + *sym_rssi = __round_int(sym / (float)mult); + + return 0; +} + + +/******************************************************************************/ +/* Filter */ +/******************************************************************************/ + +static int _rfic_fpga_get_filter(struct bladerf *dev, + bladerf_channel ch, + bladerf_rfic_rxfir *rxfir, + bladerf_rfic_txfir *txfir) +{ + uint64_t readval; + + CHECK_STATUS( + _rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_FILTER, &readval)); + + if (BLADERF_CHANNEL_IS_TX(ch)) { + if (NULL != txfir) { + *txfir = (bladerf_rfic_txfir)readval; + } else { + return BLADERF_ERR_INVAL; + } + } else { + if (NULL != rxfir) { + *rxfir = (bladerf_rfic_rxfir)readval; + } else { + return BLADERF_ERR_INVAL; + } + } + + return 0; +} + +static int _rfic_fpga_set_filter(struct bladerf *dev, + bladerf_channel ch, + bladerf_rfic_rxfir rxfir, + bladerf_rfic_txfir txfir) +{ + uint64_t data; + + if (BLADERF_CHANNEL_IS_TX(ch)) { + data = (uint64_t)txfir; + } else { + data = (uint64_t)rxfir; + } + + return _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_FILTER, data); +} + + +/******************************************************************************/ +/* TX Mute */ +/******************************************************************************/ + +static int _rfic_fpga_get_txmute(struct bladerf *dev, + bladerf_channel ch, + bool *state) +{ + if (BLADERF_CHANNEL_IS_TX(ch)) { + uint64_t readval; + + CHECK_STATUS( + _rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_TXMUTE, &readval)); + + return (readval > 0); + } + + return BLADERF_ERR_UNSUPPORTED; +} + +static int _rfic_fpga_set_txmute(struct bladerf *dev, + bladerf_channel ch, + bool state) +{ + if (BLADERF_CHANNEL_IS_TX(ch)) { + return _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_TXMUTE, + state ? 1 : 0); + } + + return BLADERF_ERR_UNSUPPORTED; +} + + +/******************************************************************************/ +/* Fastlock */ +/******************************************************************************/ + +static int _rfic_fpga_store_fastlock_profile(struct bladerf *dev, + bladerf_channel ch, + uint32_t profile) +{ + return _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_FASTLOCK, profile); +} + + +/******************************************************************************/ +/* Function pointers */ +/******************************************************************************/ + +struct controller_fns const rfic_fpga_control = { + FIELD_INIT(.is_present, _rfic_fpga_is_present), + FIELD_INIT(.is_standby, _rfic_fpga_is_standby), + FIELD_INIT(.is_initialized, _rfic_fpga_is_initialized), + FIELD_INIT(.get_init_state, _rfic_fpga_get_init_state), + + FIELD_INIT(.initialize, _rfic_fpga_initialize), + FIELD_INIT(.standby, _rfic_fpga_standby), + FIELD_INIT(.deinitialize, _rfic_fpga_deinitialize), + + FIELD_INIT(.enable_module, _rfic_fpga_enable_module), + + FIELD_INIT(.get_sample_rate, _rfic_fpga_get_sample_rate), + FIELD_INIT(.set_sample_rate, _rfic_fpga_set_sample_rate), + + FIELD_INIT(.get_frequency, _rfic_fpga_get_frequency), + FIELD_INIT(.set_frequency, _rfic_fpga_set_frequency), + FIELD_INIT(.select_band, _rfic_fpga_select_band), + + FIELD_INIT(.get_bandwidth, _rfic_fpga_get_bandwidth), + FIELD_INIT(.set_bandwidth, _rfic_fpga_set_bandwidth), + + FIELD_INIT(.get_gain_mode, _rfic_fpga_get_gain_mode), + FIELD_INIT(.set_gain_mode, _rfic_fpga_set_gain_mode), + + FIELD_INIT(.get_gain, _rfic_fpga_get_gain), + FIELD_INIT(.set_gain, _rfic_fpga_set_gain), + + FIELD_INIT(.get_gain_stage, _rfic_fpga_get_gain_stage), + FIELD_INIT(.set_gain_stage, _rfic_fpga_set_gain_stage), + + FIELD_INIT(.get_rssi, _rfic_fpga_get_rssi), + + FIELD_INIT(.get_filter, _rfic_fpga_get_filter), + FIELD_INIT(.set_filter, _rfic_fpga_set_filter), + + FIELD_INIT(.get_txmute, _rfic_fpga_get_txmute), + FIELD_INIT(.set_txmute, _rfic_fpga_set_txmute), + + FIELD_INIT(.store_fastlock_profile, _rfic_fpga_store_fastlock_profile), + + FIELD_INIT(.command_mode, RFIC_COMMAND_FPGA), +}; diff --git a/Radio/HW/BladeRF/src/board/bladerf2/rfic_host.c b/Radio/HW/BladeRF/src/board/bladerf2/rfic_host.c new file mode 100644 index 0000000..7cccbe6 --- /dev/null +++ b/Radio/HW/BladeRF/src/board/bladerf2/rfic_host.c @@ -0,0 +1,1058 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2018 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> + +#include <libbladeRF.h> + +#include "ad936x_helpers.h" +#include "bladerf2_common.h" +#include "board/board.h" +#include "common.h" +#include "helpers/wallclock.h" +#include "iterators.h" +#include "log.h" + +// #define BLADERF_HOSTED_C_DEBUG + + +/******************************************************************************/ +/* Initialization */ +/******************************************************************************/ + +static bool _rfic_host_is_present(struct bladerf *dev) +{ + return true; +} + +static bool _rfic_host_is_initialized(struct bladerf *dev) +{ + if (NULL == dev || NULL == dev->board_data) { + return false; + } + + struct bladerf2_board_data *board_data = dev->board_data; + + return (NULL != board_data->phy); +} + +static int _rfic_host_get_init_state(struct bladerf *dev, + bladerf_rfic_init_state *state) +{ + if (_rfic_host_is_initialized(dev)) { + *state = BLADERF_RFIC_INIT_STATE_ON; + } else { + *state = BLADERF_RFIC_INIT_STATE_OFF; + } + + return 0; +} + +static bool _rfic_host_is_standby(struct bladerf *dev) +{ + return false; +} + +static int _rfic_host_initialize(struct bladerf *dev) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = NULL; + struct controller_fns const *rfic = board_data->rfic; + uint32_t reg; + bladerf_direction dir; + bladerf_channel ch; + size_t i; + uint32_t config_gpio; + + log_debug("%s: initializating\n", __FUNCTION__); + + /* Initialize RFFE control */ + CHECK_STATUS(dev->backend->rffe_control_write( + dev, (1 << RFFE_CONTROL_ENABLE) | (1 << RFFE_CONTROL_TXNRX))); + + + CHECK_STATUS(dev->backend->config_gpio_read(dev, &config_gpio)); + + board_data->rfic_init_params = (config_gpio & BLADERF_GPIO_PACKET_CORE_PRESENT) ? + (void *)&bladerf2_rfic_init_params_fastagc_burst : + (void *)&bladerf2_rfic_init_params; + + /* Initialize AD9361 */ + CHECK_AD936X(ad9361_init(&phy, (AD9361_InitParam *)board_data->rfic_init_params, dev)); + + if (NULL == phy || NULL == phy->pdata) { + RETURN_ERROR_STATUS("ad9361_init struct initialization", + BLADERF_ERR_UNEXPECTED); + } + + log_verbose("%s: ad9361 initialized @ %p\n", __FUNCTION__, phy); + + board_data->phy = phy; + + /* Force AD9361 to a non-default freq. This will entice it to do a + * proper re-tuning when we set it back to the default freq later on. */ + FOR_EACH_DIRECTION(dir) + { + FOR_EACH_CHANNEL(dir, 1, i, ch) + { + CHECK_STATUS(rfic->set_frequency(dev, ch, RESET_FREQUENCY)); + } + } + + /* Set up AD9361 FIR filters */ + /* TODO: permit specification of filter taps, for the truly brave */ + CHECK_STATUS(rfic->set_filter(dev, BLADERF_CHANNEL_RX(0), + BLADERF_RFIC_RXFIR_DEFAULT, 0)); + CHECK_STATUS(rfic->set_filter(dev, BLADERF_CHANNEL_TX(0), 0, + BLADERF_RFIC_TXFIR_DEFAULT)); + + /* Clear RFFE control */ + CHECK_STATUS(dev->backend->rffe_control_read(dev, ®)); + reg &= ~(1 << RFFE_CONTROL_TXNRX); + reg &= ~(1 << RFFE_CONTROL_ENABLE); + reg &= ~(1 << RFFE_CONTROL_RX_SPDT_1); + reg &= ~(1 << RFFE_CONTROL_RX_SPDT_2); + reg &= ~(1 << RFFE_CONTROL_TX_SPDT_1); + reg &= ~(1 << RFFE_CONTROL_TX_SPDT_2); + reg &= ~(1 << RFFE_CONTROL_MIMO_RX_EN_0); + reg &= ~(1 << RFFE_CONTROL_MIMO_TX_EN_0); + reg &= ~(1 << RFFE_CONTROL_MIMO_RX_EN_1); + reg &= ~(1 << RFFE_CONTROL_MIMO_TX_EN_1); + CHECK_STATUS(dev->backend->rffe_control_write(dev, reg)); + + /* Move AD9361 back to desired frequency */ + CHECK_STATUS(rfic->set_frequency(dev, BLADERF_CHANNEL_RX(0), + phy->pdata->rx_synth_freq)); + CHECK_STATUS(rfic->set_frequency(dev, BLADERF_CHANNEL_TX(0), + phy->pdata->tx_synth_freq)); + + /* Mute TX channels */ + FOR_EACH_CHANNEL(BLADERF_TX, dev->board->get_channel_count(dev, BLADERF_TX), + i, ch) + { + CHECK_STATUS(rfic->set_txmute(dev, ch, true)); + } + + log_debug("%s: initialization complete\n", __FUNCTION__); + + return 0; +} + +static int _rfic_host_deinitialize(struct bladerf *dev) +{ + struct bladerf2_board_data *board_data = dev->board_data; + uint32_t reg; + + log_debug("%s: deinitializing\n", __FUNCTION__); + + /* Clear RFFE control */ + CHECK_STATUS(dev->backend->rffe_control_read(dev, ®)); + reg &= ~(1 << RFFE_CONTROL_TXNRX); + reg &= ~(1 << RFFE_CONTROL_ENABLE); + reg &= ~(1 << RFFE_CONTROL_RX_SPDT_1); + reg &= ~(1 << RFFE_CONTROL_RX_SPDT_2); + reg &= ~(1 << RFFE_CONTROL_TX_SPDT_1); + reg &= ~(1 << RFFE_CONTROL_TX_SPDT_2); + reg &= ~(1 << RFFE_CONTROL_MIMO_RX_EN_0); + reg &= ~(1 << RFFE_CONTROL_MIMO_TX_EN_0); + reg &= ~(1 << RFFE_CONTROL_MIMO_RX_EN_1); + reg &= ~(1 << RFFE_CONTROL_MIMO_TX_EN_1); + CHECK_STATUS(dev->backend->rffe_control_write(dev, reg)); + + if (NULL != board_data->phy) { + CHECK_STATUS(ad9361_deinit(board_data->phy)); + board_data->phy = NULL; + } + + return 0; +} + +static int _rfic_host_standby(struct bladerf *dev) +{ + return _rfic_host_deinitialize(dev); +} + + +/******************************************************************************/ +/* Enable */ +/******************************************************************************/ + +static int _rfic_host_enable_module(struct bladerf *dev, + bladerf_channel ch, + bool enable) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + struct controller_fns const *rfic = board_data->rfic; + bladerf_direction dir = BLADERF_CHANNEL_IS_TX(ch) ? BLADERF_TX : BLADERF_RX; + bladerf_frequency freq = 0; + bladerf_channel_layout layout; + uint32_t reg; /* RFFE register value */ + uint32_t reg_old; /* Original RFFE register value */ + int ch_bit; /* RFFE MIMO channel bit */ + int dir_bit; /* RFFE RX/TX enable bit */ + bool ch_pending; /* Requested channel state differs */ + bool dir_enable; /* Direction is enabled */ + bool dir_pending; /* Requested direction state differs */ + bool backend_clear; /* Backend requires reset */ + bool mimo_enabled; + + /* Look up the RFFE bits affecting this channel */ + ch_bit = _get_rffe_control_bit_for_ch(ch); + dir_bit = _get_rffe_control_bit_for_dir(dir); + if (ch_bit < 0 || dir_bit < 0) { + RETURN_ERROR_STATUS("_get_rffe_control_bit", BLADERF_ERR_INVAL); + } + + /* Query the current frequency if necessary */ + if (enable) { + CHECK_STATUS(rfic->get_frequency(dev, ch, &freq)); + } + + /** + * If we are moving from 0 active channels to 1 active channel: + * Channel: Setup SPDT, MIMO, TX Mute + * Direction: Setup ENABLE/TXNRX, RFIC port + * Backend: Enable + * + * If we are moving from 1 active channel to 0 active channels: + * Channel: Teardown SPDT, MIMO, TX Mute + * Direction: Teardown ENABLE/TXNRX, RFIC port, Sync + * Backend: Disable + * + * If we are enabling an nth channel, where n > 1: + * Channel: Setup SPDT, MIMO, TX Mute + * Direction: no change + * Backend: Clear + * + * If we are disabling an nth channel, where n > 1: + * Channel: Teardown SPDT, MIMO, TX Mute + * Direction: no change + * Backend: no change + */ + + /* Read RFFE control register */ + CHECK_STATUS(dev->backend->rffe_control_read(dev, ®)); + reg_old = reg; + ch_pending = _rffe_ch_enabled(reg, ch) != enable; + layout = board_data->sync->stream_config.layout; + mimo_enabled = layout == BLADERF_RX_X2 || layout == BLADERF_TX_X2; + + if (layout == BLADERF_TX_X2) { + if (enable) { + log_debug("Enabling both TX channels. MIMO layout configured.\n"); + reg |= (1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_TX(0))); + reg |= (1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_TX(1))); + } else { + log_debug("Disabling both TX channels. MIMO layout configured.\n"); + reg &= ~(1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_TX(0))); + reg &= ~(1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_TX(1))); + } + } + + if (layout == BLADERF_RX_X2) { + if (enable) { + log_debug("Enabling both RX channels. MIMO layout configured.\n"); + reg |= (1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_RX(0))); + reg |= (1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_RX(1))); + } else { + log_debug("Disabling both RX channels. MIMO layout configured.\n"); + reg &= ~(1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_RX(0))); + reg &= ~(1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_RX(1))); + } + } + + /* Channel Setup/Teardown */ + if (ch_pending) { + /* Modify SPDT bits */ + CHECK_STATUS(_modify_spdt_bits_by_freq(®, ch, enable, freq)); + + /* Modify MIMO channel enable bits */ + if (enable) { + reg |= (1 << ch_bit); + } else { + reg &= ~(1 << ch_bit); + } + + /* Set/unset TX mute */ + if (BLADERF_CHANNEL_IS_TX(ch)) { + txmute_set(phy, ch, !enable); + } + } + + dir_enable = enable || does_rffe_dir_have_enabled_ch(reg, dir); + dir_pending = _rffe_dir_enabled(reg, dir) != dir_enable; + + /* Direction Setup/Teardown */ + if (dir_pending) { + /* Modify ENABLE/TXNRX bits */ + if (dir_enable) { + reg |= (1 << dir_bit); + } else { + reg &= ~(1 << dir_bit); + } + + /* Select RFIC port */ + CHECK_STATUS(set_ad9361_port_by_freq(phy, ch, dir_enable, freq)); + + /* Tear down sync interface if required */ + if (!dir_enable) { + sync_deinit(&board_data->sync[dir]); + perform_format_deconfig(dev, dir); + } + } + + /* Reset FIFO if we are enabling an additional RX channel */ + backend_clear = enable && !dir_pending && BLADERF_RX == dir && !mimo_enabled; + + /* Write RFFE control register */ + if (reg_old != reg) { + CHECK_STATUS(dev->backend->rffe_control_write(dev, reg)); + } else { + log_debug("%s: reg value unchanged? (%08x)\n", __FUNCTION__, reg); + } + + /* Backend Setup/Teardown/Reset */ + if (dir_pending || backend_clear) { + if (!dir_enable || backend_clear) { + CHECK_STATUS(dev->backend->enable_module(dev, dir, false)); + } + + if (dir_enable || backend_clear) { + CHECK_STATUS(dev->backend->enable_module(dev, dir, true)); + } + } + + /* Warn the user if the sample rate isn't reasonable */ + if (ch_pending && enable) { + check_total_sample_rate(dev); + } + +#ifdef BLADERF_HOSTED_C_DEBUG + /* Debug logging */ + static uint64_t lastrun = 0; /* nsec value at last run */ + uint64_t nsec = wallclock_get_current_nsec(); + + log_debug("%s: %s%d ch_en=%d ch_pend=%d dir_en=%d dir_pend=%d be_clr=%d " + "reg=0x%08x->0x%08x nsec=%" PRIu64 " (delta: %" PRIu64 ")\n", + __FUNCTION__, BLADERF_TX == dir ? "TX" : "RX", (ch >> 1) + 1, + enable, ch_pending, dir_enable, dir_pending, backend_clear, + reg_old, reg, nsec, nsec - lastrun); + + lastrun = nsec; +#endif + + /** Force rerun frequency calibration */ + if (enable) { + bladerf_frequency current_frequency; + CHECK_STATUS(dev->board->get_frequency(dev, ch, ¤t_frequency)); + CHECK_STATUS(dev->board->set_frequency(dev, ch, current_frequency)); + } + + return 0; +} + + +/******************************************************************************/ +/* Sample rate */ +/******************************************************************************/ + +static int _rfic_host_get_sample_rate(struct bladerf *dev, + bladerf_channel ch, + bladerf_sample_rate *rate) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + + if (BLADERF_CHANNEL_IS_TX(ch)) { + CHECK_AD936X(ad9361_get_tx_sampling_freq(phy, rate)); + } else { + CHECK_AD936X(ad9361_get_rx_sampling_freq(phy, rate)); + } + + return 0; +} + +static int _rfic_host_set_sample_rate(struct bladerf *dev, + bladerf_channel ch, + bladerf_sample_rate rate) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + + if (BLADERF_CHANNEL_IS_TX(ch)) { + CHECK_AD936X(ad9361_set_tx_sampling_freq(phy, rate)); + } else { + CHECK_AD936X(ad9361_set_rx_sampling_freq(phy, rate)); + } + + return 0; +} + + +/******************************************************************************/ +/* Frequency */ +/******************************************************************************/ + +static int _rfic_host_get_frequency(struct bladerf *dev, + bladerf_channel ch, + bladerf_frequency *frequency) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + bladerf_frequency lo_frequency; + + if (BLADERF_CHANNEL_IS_TX(ch)) { + CHECK_AD936X(ad9361_get_tx_lo_freq(phy, &lo_frequency)); + } else { + CHECK_AD936X(ad9361_get_rx_lo_freq(phy, &lo_frequency)); + } + + *frequency = lo_frequency; + + return 0; +} + +static int _rfic_host_set_frequency(struct bladerf *dev, + bladerf_channel ch, + bladerf_frequency frequency) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + struct controller_fns const *rfic = board_data->rfic; + struct bladerf_range const *range = NULL; + + CHECK_STATUS(dev->board->get_frequency_range(dev, ch, &range)); + + if (!is_within_range(range, frequency)) { + return BLADERF_ERR_RANGE; + } + + /* Set up band selection */ + CHECK_STATUS(rfic->select_band(dev, ch, frequency)); + + /* Change LO frequency */ + if (BLADERF_CHANNEL_IS_TX(ch)) { + CHECK_AD936X(ad9361_set_tx_lo_freq(phy, frequency)); + } else { + CHECK_AD936X(ad9361_set_rx_lo_freq(phy, frequency)); + } + + return 0; +} + +static int _rfic_host_select_band(struct bladerf *dev, + bladerf_channel ch, + bladerf_frequency frequency) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + uint32_t reg; + size_t i; + + /* Read RFFE control register */ + CHECK_STATUS(dev->backend->rffe_control_read(dev, ®)); + + /* Modify the SPDT bits. */ + /* We have to do this for all the channels sharing the same LO. */ + for (i = 0; i < 2; ++i) { + bladerf_channel bch = BLADERF_CHANNEL_IS_TX(ch) ? BLADERF_CHANNEL_TX(i) + : BLADERF_CHANNEL_RX(i); + CHECK_STATUS(_modify_spdt_bits_by_freq( + ®, bch, _rffe_ch_enabled(reg, bch), frequency)); + } + + /* Write RFFE control register */ + CHECK_STATUS(dev->backend->rffe_control_write(dev, reg)); + + /* Set AD9361 port */ + CHECK_STATUS( + set_ad9361_port_by_freq(phy, ch, _rffe_ch_enabled(reg, ch), frequency)); + + return 0; +} + + +/******************************************************************************/ +/* Bandwidth */ +/******************************************************************************/ + +static int _rfic_host_get_bandwidth(struct bladerf *dev, + bladerf_channel ch, + bladerf_bandwidth *bandwidth) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + + if (BLADERF_CHANNEL_IS_TX(ch)) { + CHECK_AD936X(ad9361_get_tx_rf_bandwidth(phy, bandwidth)); + } else { + CHECK_AD936X(ad9361_get_rx_rf_bandwidth(phy, bandwidth)); + } + + return 0; +} + +static int _rfic_host_set_bandwidth(struct bladerf *dev, + bladerf_channel ch, + bladerf_bandwidth bandwidth, + bladerf_bandwidth *actual) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + struct controller_fns const *rfic = board_data->rfic; + struct bladerf_range const *range = NULL; + + CHECK_STATUS(dev->board->get_bandwidth_range(dev, ch, &range)); + + bandwidth = (unsigned int)clamp_to_range(range, bandwidth); + + if (BLADERF_CHANNEL_IS_TX(ch)) { + CHECK_AD936X(ad9361_set_tx_rf_bandwidth(phy, bandwidth)); + } else { + CHECK_AD936X(ad9361_set_rx_rf_bandwidth(phy, bandwidth)); + } + + if (actual != NULL) { + return rfic->get_bandwidth(dev, ch, actual); + } + + return 0; +} + + +/******************************************************************************/ +/* Gain mode */ +/******************************************************************************/ + +static int _rfic_host_get_gain_mode(struct bladerf *dev, + bladerf_channel ch, + bladerf_gain_mode *mode) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + uint8_t const rfic_ch = ch >> 1; + uint8_t gc_mode; + bool ok; + + if (BLADERF_CHANNEL_IS_TX(ch)) { + log_warning("%s: automatic gain control not valid for TX channels\n", + __FUNCTION__); + *mode = BLADERF_GAIN_DEFAULT; + return 0; + } + + /* Get the gain */ + CHECK_AD936X(ad9361_get_rx_gain_control_mode(phy, rfic_ch, &gc_mode)); + + /* Mode conversion */ + if (mode != NULL) { + *mode = gainmode_ad9361_to_bladerf(gc_mode, &ok); + if (!ok) { + RETURN_INVAL("mode", "is not valid"); + } + } + + return 0; +} + +static int _rfic_host_set_gain_mode(struct bladerf *dev, + bladerf_channel ch, + bladerf_gain_mode mode) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + uint8_t const rfic_ch = ch >> 1; + enum rf_gain_ctrl_mode gc_mode; + bool ok; + + if (BLADERF_CHANNEL_IS_TX(ch)) { + log_warning("%s: automatic gain control not valid for TX channels\n", + __FUNCTION__); + return 0; + } + + /* Channel conversion */ + switch (ch) { + case BLADERF_CHANNEL_RX(0): + gc_mode = ((AD9361_InitParam *)board_data->rfic_init_params)->gc_rx1_mode; + break; + + case BLADERF_CHANNEL_RX(1): + gc_mode = ((AD9361_InitParam *)board_data->rfic_init_params)->gc_rx2_mode; + break; + + default: + log_error("%s: unknown channel index (%d)\n", __FUNCTION__, ch); + return BLADERF_ERR_UNSUPPORTED; + } + + /* Mode conversion */ + if (mode != BLADERF_GAIN_DEFAULT) { + gc_mode = gainmode_bladerf_to_ad9361(mode, &ok); + if (!ok) { + RETURN_INVAL("mode", "is not valid"); + } + } + + /* Set the mode! */ + CHECK_AD936X(ad9361_set_rx_gain_control_mode(phy, rfic_ch, gc_mode)); + + return 0; +} + + +/******************************************************************************/ +/* Gain */ +/* These functions handle offset */ +/******************************************************************************/ + +static int _rfic_host_get_gain(struct bladerf *dev, + bladerf_channel ch, + int *gain) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + struct controller_fns const *rfic = board_data->rfic; + int val; + float offset; + + CHECK_STATUS(get_gain_offset(dev, ch, &offset)); + + if (BLADERF_CHANNEL_IS_TX(ch)) { + bool muted; + + CHECK_STATUS(txmute_get(phy, ch, &muted)); + + if (muted) { + struct bladerf_range const *range = NULL; + + CHECK_STATUS( + dev->board->get_gain_stage_range(dev, ch, "dsa", &range)); + + val = -__unscale_int(range, txmute_get_cached(phy, ch)); + } else { + CHECK_STATUS(rfic->get_gain_stage(dev, ch, "dsa", &val)); + } + } else { + CHECK_STATUS(rfic->get_gain_stage(dev, ch, "full", &val)); + } + + *gain = __round_int(val + offset); + + return 0; +} + +static int _rfic_host_set_gain(struct bladerf *dev, + bladerf_channel ch, + int gain) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + struct controller_fns const *rfic = board_data->rfic; + int val; + float offset; + + CHECK_STATUS(get_gain_offset(dev, ch, &offset)); + + val = gain - offset; + + if (BLADERF_CHANNEL_IS_TX(ch)) { + bool muted; + + CHECK_STATUS(txmute_get(phy, ch, &muted)); + + if (muted) { + struct bladerf_range const *range = NULL; + + CHECK_STATUS( + dev->board->get_gain_stage_range(dev, ch, "dsa", &range)); + + CHECK_STATUS(txmute_set_cached(phy, ch, -__scale_int(range, val))); + } else { + CHECK_STATUS(rfic->set_gain_stage(dev, ch, "dsa", val)); + } + } else { + CHECK_STATUS(rfic->set_gain_stage(dev, ch, "full", val)); + } + + return 0; +} + + +/******************************************************************************/ +/* Gain stage */ +/* These functions handle scaling */ +/******************************************************************************/ + +static int _rfic_host_get_gain_stage(struct bladerf *dev, + bladerf_channel ch, + char const *stage, + int *gain) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + struct bladerf_range const *range = NULL; + uint8_t const rfic_ch = ch >> 1; + int val; + + CHECK_STATUS(dev->board->get_gain_stage_range(dev, ch, stage, &range)); + + if (BLADERF_CHANNEL_IS_TX(ch)) { + if (strcmp(stage, "dsa") == 0) { + uint32_t atten; + + CHECK_AD936X(ad9361_get_tx_attenuation(phy, rfic_ch, &atten)); + + val = -atten; + } else { + log_error("%s: gain stage '%s' invalid\n", __FUNCTION__, stage); + return BLADERF_ERR_INVAL; + } + } else { + struct rf_rx_gain rx_gain; + CHECK_AD936X(ad9361_get_rx_gain(phy, rfic_ch + 1, &rx_gain)); + + if (strcmp(stage, "full") == 0) { + val = rx_gain.gain_db; + } else if (strcmp(stage, "digital") == 0) { + val = rx_gain.digital_gain; + } else { + log_error("%s: gain stage '%s' invalid\n", __FUNCTION__, stage); + return BLADERF_ERR_INVAL; + } + } + + *gain = __unscale_int(range, val); + + return 0; +} + +static int _rfic_host_set_gain_stage(struct bladerf *dev, + bladerf_channel ch, + char const *stage, + int gain) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + struct bladerf_range const *range = NULL; + uint8_t const rfic_ch = ch >> 1; + int val; + + CHECK_STATUS(dev->board->get_gain_stage_range(dev, ch, stage, &range)); + + /* Scale/round/clamp as required */ + if (BLADERF_CHANNEL_IS_TX(ch) && gain < -89) { + val = -89750; + } else { + val = __scale_int(range, clamp_to_range(range, gain)); + } + + if (BLADERF_CHANNEL_IS_TX(ch)) { + if (strcmp(stage, "dsa") == 0) { + CHECK_AD936X(ad9361_set_tx_attenuation(phy, rfic_ch, -val)); + } else { + log_warning("%s: gain stage '%s' invalid\n", __FUNCTION__, stage); + return BLADERF_ERR_INVAL; + } + } else { + if (strcmp(stage, "full") == 0) { + CHECK_AD936X(ad9361_set_rx_rf_gain(phy, rfic_ch, val)); + } else { + log_warning("%s: gain stage '%s' invalid\n", __FUNCTION__, stage); + return BLADERF_ERR_INVAL; + } + } + + return 0; +} + + +/******************************************************************************/ +/* RSSI */ +/******************************************************************************/ + +static int _rfic_host_get_rssi(struct bladerf *dev, + bladerf_channel ch, + int *pre_rssi, + int *sym_rssi) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + uint8_t const rfic_ch = ch >> 1; + + int pre, sym; + + if (BLADERF_CHANNEL_IS_TX(ch)) { + uint32_t rssi = 0; + + CHECK_AD936X(ad9361_get_tx_rssi(phy, rfic_ch, &rssi)); + + pre = __round_int(rssi / 1000.0); + sym = __round_int(rssi / 1000.0); + } else { + struct rf_rssi rssi; + + CHECK_AD936X(ad9361_get_rx_rssi(phy, rfic_ch, &rssi)); + + pre = __round_int(rssi.preamble / (float)rssi.multiplier); + sym = __round_int(rssi.symbol / (float)rssi.multiplier); + } + + if (NULL != pre_rssi) { + *pre_rssi = -pre; + } + + if (NULL != sym_rssi) { + *sym_rssi = -sym; + } + + return 0; +} + + +/******************************************************************************/ +/* Filter */ +/******************************************************************************/ + +static int _rfic_host_get_filter(struct bladerf *dev, + bladerf_channel ch, + bladerf_rfic_rxfir *rxfir, + bladerf_rfic_txfir *txfir) +{ + struct bladerf2_board_data *board_data = dev->board_data; + + if (NULL != rxfir) { + *rxfir = board_data->rxfir; + } + + if (NULL != txfir) { + *txfir = board_data->txfir; + } + + return 0; +} + +static int _rfic_host_set_filter(struct bladerf *dev, + bladerf_channel ch, + bladerf_rfic_rxfir rxfir, + bladerf_rfic_txfir txfir) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + + if (BLADERF_CHANNEL_IS_TX(ch)) { + AD9361_TXFIRConfig *fir_config = NULL; + uint8_t enable; + + if (BLADERF_RFIC_TXFIR_CUSTOM == txfir) { + log_warning("custom FIR not implemented, assuming default\n"); + txfir = BLADERF_RFIC_TXFIR_DEFAULT; + } + + switch (txfir) { + case BLADERF_RFIC_TXFIR_BYPASS: + fir_config = &bladerf2_rfic_tx_fir_config; + enable = 0; + break; + case BLADERF_RFIC_TXFIR_INT1: + fir_config = &bladerf2_rfic_tx_fir_config; + enable = 1; + break; + case BLADERF_RFIC_TXFIR_INT2: + fir_config = &bladerf2_rfic_tx_fir_config_int2; + enable = 1; + break; + case BLADERF_RFIC_TXFIR_INT4: + fir_config = &bladerf2_rfic_tx_fir_config_int4; + enable = 1; + break; + default: + MUTEX_UNLOCK(&dev->lock); + assert(!"Bug: unhandled txfir selection"); + return BLADERF_ERR_UNEXPECTED; + } + + CHECK_AD936X(ad9361_set_tx_fir_config(phy, *fir_config)); + CHECK_AD936X(ad9361_set_tx_fir_en_dis(phy, enable)); + + board_data->txfir = txfir; + } else { + AD9361_RXFIRConfig *fir_config = NULL; + uint8_t enable; + + if (BLADERF_RFIC_RXFIR_CUSTOM == rxfir) { + log_warning("custom FIR not implemented, assuming default\n"); + rxfir = BLADERF_RFIC_RXFIR_DEFAULT; + } + + switch (rxfir) { + case BLADERF_RFIC_RXFIR_BYPASS: + fir_config = &bladerf2_rfic_rx_fir_config; + enable = 0; + break; + case BLADERF_RFIC_RXFIR_DEC1: + fir_config = &bladerf2_rfic_rx_fir_config; + enable = 1; + break; + case BLADERF_RFIC_RXFIR_DEC2: + fir_config = &bladerf2_rfic_rx_fir_config_dec2; + enable = 1; + break; + case BLADERF_RFIC_RXFIR_DEC4: + fir_config = &bladerf2_rfic_rx_fir_config_dec4; + enable = 1; + break; + default: + MUTEX_UNLOCK(&dev->lock); + assert(!"Bug: unhandled rxfir selection"); + return BLADERF_ERR_UNEXPECTED; + } + + CHECK_AD936X(ad9361_set_rx_fir_config(phy, *fir_config)); + CHECK_AD936X(ad9361_set_rx_fir_en_dis(phy, enable)); + + board_data->rxfir = rxfir; + } + + return 0; +} + + +/******************************************************************************/ +/* TX Mute */ +/******************************************************************************/ + +static int _rfic_host_get_txmute(struct bladerf *dev, + bladerf_channel ch, + bool *state) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + + if (BLADERF_CHANNEL_IS_TX(ch)) { + return txmute_get(phy, ch, state); + } + + return BLADERF_ERR_UNSUPPORTED; +} + +static int _rfic_host_set_txmute(struct bladerf *dev, + bladerf_channel ch, + bool state) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + + if (BLADERF_CHANNEL_IS_TX(ch)) { + return txmute_set(phy, ch, state); + } + + return BLADERF_ERR_UNSUPPORTED; +} + + +/******************************************************************************/ +/* Fastlock */ +/******************************************************************************/ + +static int _rfic_host_store_fastlock_profile(struct bladerf *dev, + bladerf_channel ch, + uint32_t profile) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + + if (BLADERF_CHANNEL_IS_TX(ch)) { + CHECK_AD936X(ad9361_tx_fastlock_store(phy, profile)); + } else { + CHECK_AD936X(ad9361_rx_fastlock_store(phy, profile)); + } + + return 0; +} + +static int _rfic_host_save_fastlock_profile(struct bladerf *dev, + bladerf_channel ch, + uint32_t profile, + uint8_t *values) +{ + struct bladerf2_board_data *board_data = dev->board_data; + struct ad9361_rf_phy *phy = board_data->phy; + + if (BLADERF_CHANNEL_IS_TX(ch)) { + CHECK_AD936X(ad9361_tx_fastlock_save(phy, profile, values)); + } else { + CHECK_AD936X(ad9361_rx_fastlock_save(phy, profile, values)); + } + + return 0; +} + + +/******************************************************************************/ +/* Function pointers */ +/******************************************************************************/ + +struct controller_fns const rfic_host_control = { + FIELD_INIT(.is_present, _rfic_host_is_present), + FIELD_INIT(.is_standby, _rfic_host_is_standby), + FIELD_INIT(.is_initialized, _rfic_host_is_initialized), + FIELD_INIT(.get_init_state, _rfic_host_get_init_state), + + FIELD_INIT(.initialize, _rfic_host_initialize), + FIELD_INIT(.standby, _rfic_host_standby), + FIELD_INIT(.deinitialize, _rfic_host_deinitialize), + + FIELD_INIT(.enable_module, _rfic_host_enable_module), + + FIELD_INIT(.get_sample_rate, _rfic_host_get_sample_rate), + FIELD_INIT(.set_sample_rate, _rfic_host_set_sample_rate), + + FIELD_INIT(.get_frequency, _rfic_host_get_frequency), + FIELD_INIT(.set_frequency, _rfic_host_set_frequency), + FIELD_INIT(.select_band, _rfic_host_select_band), + + FIELD_INIT(.get_bandwidth, _rfic_host_get_bandwidth), + FIELD_INIT(.set_bandwidth, _rfic_host_set_bandwidth), + + FIELD_INIT(.get_gain_mode, _rfic_host_get_gain_mode), + FIELD_INIT(.set_gain_mode, _rfic_host_set_gain_mode), + + FIELD_INIT(.get_gain, _rfic_host_get_gain), + FIELD_INIT(.set_gain, _rfic_host_set_gain), + + FIELD_INIT(.get_gain_stage, _rfic_host_get_gain_stage), + FIELD_INIT(.set_gain_stage, _rfic_host_set_gain_stage), + + FIELD_INIT(.get_rssi, _rfic_host_get_rssi), + + FIELD_INIT(.get_filter, _rfic_host_get_filter), + FIELD_INIT(.set_filter, _rfic_host_set_filter), + + FIELD_INIT(.get_txmute, _rfic_host_get_txmute), + FIELD_INIT(.set_txmute, _rfic_host_set_txmute), + + FIELD_INIT(.store_fastlock_profile, _rfic_host_store_fastlock_profile), + FIELD_INIT(.save_fastlock_profile, _rfic_host_save_fastlock_profile), + + FIELD_INIT(.command_mode, RFIC_COMMAND_HOST), +}; diff --git a/Radio/HW/BladeRF/src/board/board.c b/Radio/HW/BladeRF/src/board/board.c new file mode 100644 index 0000000..9b49242 --- /dev/null +++ b/Radio/HW/BladeRF/src/board/board.c @@ -0,0 +1,13 @@ +#include "host_config.h" + +#include "board.h" + +extern const struct board_fns bladerf1_board_fns; +extern const struct board_fns bladerf2_board_fns; + +const struct board_fns *bladerf_boards[] = { + &bladerf1_board_fns, + &bladerf2_board_fns, +}; + +const unsigned int bladerf_boards_len = ARRAY_SIZE(bladerf_boards); diff --git a/Radio/HW/BladeRF/src/board/board.h b/Radio/HW/BladeRF/src/board/board.h new file mode 100644 index 0000000..279a0c1 --- /dev/null +++ b/Radio/HW/BladeRF/src/board/board.h @@ -0,0 +1,495 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2017 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef BOARD_BOARD_H_ +#define BOARD_BOARD_H_ + +#include <stdint.h> + +#include <libbladeRF.h> + +#include "host_config.h" +#include "thread.h" + +#include "backend/backend.h" + +/* Device capabilities are stored in a 64-bit mask. + * + * FPGA-oriented capabilities start at bit 0. + * FX3-oriented capabilities start at bit 24. + * Other/mixed capabilities start at bit 48. + */ + +/** + * Prior to FPGA 0.0.4, the DAC register were located at a different address + */ +#define BLADERF_CAP_UPDATED_DAC_ADDR (1 << 0) + +/** + * FPGA version 0.0.5 introduced XB-200 support + */ +#define BLADERF_CAP_XB200 (1 << 1) + +/** + * FPGA version 0.1.0 introduced timestamp support + */ +#define BLADERF_CAP_TIMESTAMPS (1 << 2) + +/** + * FPGA v0.2.0 introduced scheduled retune support on the bladeRF 1, and FPGA + * v0.10.0 introduced it on the bladeRF 2. + */ +#define BLADERF_CAP_SCHEDULED_RETUNE (1 << 3) + +/** + * FPGA version 0.3.0 introduced new packet handler formats that pack + * operations into a single requests. + */ +#define BLADERF_CAP_PKT_HANDLER_FMT (1 << 4) + +/** + * A bug fix in FPGA version 0.3.2 allowed support for reading back + * the current VCTCXO trim dac value. + */ +#define BLADERF_CAP_VCTCXO_TRIMDAC_READ (1 << 5) + +/** + * FPGA v0.4.0 introduced the ability to write LMS6002D TX/RX PLL + * NINT & NFRAC regiters atomically. + */ +#define BLADERF_CAP_ATOMIC_NINT_NFRAC_WRITE (1 << 6) + +/** + * FPGA v0.4.1 fixed an issue with masked writes to the expansion GPIO + * and expansion GPIO direction registers. + * + * To help users avoid getting bitten by this bug, we'll mark this + * as a capability and disallow masked writes unless an FPGA with the + * fix is being used. + */ +#define BLADERF_CAP_MASKED_XBIO_WRITE (1 << 7) + +/** + * FPGA v0.5.0 introduces the ability to tame the VCTCXO via a 1 pps or 10 MHz + * input source on the mini expansion header. + */ +#define BLADERF_CAP_VCTCXO_TAMING_MODE (1 << 8) + +/** + * FPGA v0.6.0 introduced trx synchronization trigger via J71-4 + */ +#define BLADERF_CAP_TRX_SYNC_TRIG (1 << 9) + +/** + * FPGA v0.7.0 introduced AGC DC correction Look-Up-Table + */ +#define BLADERF_CAP_AGC_DC_LUT (1 << 10) + +/** + * FPGA v0.2.0 introduced NIOS-based tuning support on the bladeRF 1, and FPGA + * v0.10.1 introduced it on the bladeRF 2. + */ +#define BLADERF_CAP_FPGA_TUNING (1 << 11) + +/** + * FPGA v0.12.0 introduces the packet meta format, allowing for variable length + * packets, with core target IDs. + */ +#define BLADERF_CAP_FPGA_PACKET_META (1 << 12) + +/** + * Firmware 1.7.1 introduced firmware-based loopback + */ +#define BLADERF_CAP_FW_LOOPBACK (((uint64_t)1) << 32) + +/** + * FX3 firmware version 1.8.0 introduced the ability to query when the + * device has become ready for use by the host. This was done to avoid + * opening and attempting to use the device while flash-based FPGA autoloading + * was occurring. + */ +#define BLADERF_CAP_QUERY_DEVICE_READY (((uint64_t)1) << 33) + +/** + * FX3 firmware v1.9.0 introduced a vendor request by which firmware log + * events could be retrieved. + */ +#define BLADERF_CAP_READ_FW_LOG_ENTRY (((uint64_t)1) << 34) + +/** + * FX3 firmware v2.1.0 introduced support for bladeRF 2 + */ +#define BLADERF_CAP_FW_SUPPORTS_BLADERF2 (((uint64_t)1) << 35) + +/** + * FX3 firmware v2.3.0 introduced support for reporting the SPI Flash + * manufacturer ID and device ID. + */ +#define BLADERF_CAP_FW_FLASH_ID (((uint64_t)1) << 36) + +/** + * FX3 firmware v2.3.1 introduced support for querying the source of the + * currently-configured FPGA (e.g. flash autoload, host, etc) + */ +#define BLADERF_CAP_FW_FPGA_SOURCE (((uint64_t)1) << 37) + +/** + * FX3 firmware v2.4.0 introduced support for short packets. + */ +#define BLADERF_CAP_FW_SHORT_PACKET (((uint64_t)1) << 38) + +/** + * FPGA v0.15.0 introduces support for 8bit mode. + */ +#define BLADERF_CAP_FPGA_8BIT_SAMPLES (((uint64_t)1) << 39) + +/** + * Max number of gain calibration tables associated to max number of channels + */ +#define NUM_GAIN_CAL_TBLS 4 + +struct bladerf { + /* Handle lock - to ensure atomic access to control and configuration + * operations */ + MUTEX lock; + + /* Identifying information */ + struct bladerf_devinfo ident; + + /* Backend-specific implementations */ + const struct backend_fns *backend; + + /* Backend's private data */ + void *backend_data; + + /* Board-specific implementations */ + const struct board_fns *board; + + /* Flash architecture */ + struct bladerf_flash_arch *flash_arch; + + /* Board's private data */ + void *board_data; + + /* XB attached */ + bladerf_xb xb; + + /* XB's private data */ + void *xb_data; + + /* Enabled feature */ + bladerf_feature feature; + + /* Calibration */ + struct bladerf_gain_cal_tbl gain_tbls[NUM_GAIN_CAL_TBLS]; +}; + +struct board_fns { + /* Board is compatible with backend */ + bool (*matches)(struct bladerf *dev); + + /* Open/close */ + int (*open)(struct bladerf *dev, struct bladerf_devinfo *devinfo); + void (*close)(struct bladerf *dev); + + /* Properties */ + bladerf_dev_speed (*device_speed)(struct bladerf *dev); + int (*get_serial)(struct bladerf *dev, char *serial); + int (*get_fpga_size)(struct bladerf *dev, bladerf_fpga_size *size); + int (*get_fpga_bytes)(struct bladerf *dev, size_t *size); + int (*get_flash_size)(struct bladerf *dev, uint32_t *size, bool *is_guess); + int (*is_fpga_configured)(struct bladerf *dev); + int (*get_fpga_source)(struct bladerf *dev, bladerf_fpga_source *source); + uint64_t (*get_capabilities)(struct bladerf *dev); + size_t (*get_channel_count)(struct bladerf *dev, bladerf_direction dir); + + /* Versions */ + int (*get_fpga_version)(struct bladerf *dev, + struct bladerf_version *version); + int (*get_fw_version)(struct bladerf *dev, struct bladerf_version *version); + + /* Gain */ + int (*set_gain)(struct bladerf *dev, bladerf_channel ch, bladerf_gain gain); + int (*get_gain)(struct bladerf *dev, + bladerf_channel ch, + bladerf_gain *gain); + int (*set_gain_mode)(struct bladerf *dev, + bladerf_channel ch, + bladerf_gain_mode mode); + int (*get_gain_mode)(struct bladerf *dev, + bladerf_channel ch, + bladerf_gain_mode *mode); + int (*get_gain_modes)(struct bladerf *dev, + bladerf_channel ch, + const struct bladerf_gain_modes **modes); + int (*get_gain_range)(struct bladerf *dev, + bladerf_channel ch, + const struct bladerf_range **range); + int (*set_gain_stage)(struct bladerf *dev, + bladerf_channel ch, + const char *stage, + bladerf_gain gain); + int (*get_gain_stage)(struct bladerf *dev, + bladerf_channel ch, + const char *stage, + bladerf_gain *gain); + int (*get_gain_stage_range)(struct bladerf *dev, + bladerf_channel ch, + const char *stage, + const struct bladerf_range **range); + int (*get_gain_stages)(struct bladerf *dev, + bladerf_channel ch, + const char **stages, + size_t count); + + /* Sample Rate */ + int (*set_sample_rate)(struct bladerf *dev, + bladerf_channel ch, + bladerf_sample_rate rate, + bladerf_sample_rate *actual); + int (*set_rational_sample_rate)(struct bladerf *dev, + bladerf_channel ch, + struct bladerf_rational_rate *rate, + struct bladerf_rational_rate *actual); + int (*get_sample_rate)(struct bladerf *dev, + bladerf_channel ch, + bladerf_sample_rate *rate); + int (*get_sample_rate_range)(struct bladerf *dev, + bladerf_channel ch, + const struct bladerf_range **range); + int (*get_rational_sample_rate)(struct bladerf *dev, + bladerf_channel ch, + struct bladerf_rational_rate *rate); + + /* Bandwidth */ + int (*set_bandwidth)(struct bladerf *dev, + bladerf_channel ch, + bladerf_bandwidth bandwidth, + bladerf_bandwidth *actual); + int (*get_bandwidth)(struct bladerf *dev, + bladerf_channel ch, + bladerf_bandwidth *bandwidth); + int (*get_bandwidth_range)(struct bladerf *dev, + bladerf_channel ch, + const struct bladerf_range **range); + + /* Frequency */ + int (*get_frequency)(struct bladerf *dev, + bladerf_channel ch, + bladerf_frequency *frequency); + int (*set_frequency)(struct bladerf *dev, + bladerf_channel ch, + bladerf_frequency frequency); + int (*get_frequency_range)(struct bladerf *dev, + bladerf_channel ch, + const struct bladerf_range **range); + int (*select_band)(struct bladerf *dev, + bladerf_channel ch, + bladerf_frequency frequency); + + /* RF Ports */ + int (*set_rf_port)(struct bladerf *dev, + bladerf_channel ch, + const char *port); + int (*get_rf_port)(struct bladerf *dev, + bladerf_channel ch, + const char **port); + int (*get_rf_ports)(struct bladerf *dev, + bladerf_channel ch, + const char **ports, + unsigned int count); + + /* Scheduled Tuning */ + int (*get_quick_tune)(struct bladerf *dev, + bladerf_channel ch, + struct bladerf_quick_tune *quick_tune); + int (*schedule_retune)(struct bladerf *dev, + bladerf_channel ch, + bladerf_timestamp timestamp, + bladerf_frequency frequency, + struct bladerf_quick_tune *quick_tune); + int (*cancel_scheduled_retunes)(struct bladerf *dev, bladerf_channel ch); + + /* DC/Phase/Gain Correction */ + int (*get_correction)(struct bladerf *dev, + bladerf_channel ch, + bladerf_correction corr, + int16_t *value); + int (*set_correction)(struct bladerf *dev, + bladerf_channel ch, + bladerf_correction corr, + int16_t value); + + /* Trigger */ + int (*trigger_init)(struct bladerf *dev, + bladerf_channel ch, + bladerf_trigger_signal signal, + struct bladerf_trigger *trigger); + int (*trigger_arm)(struct bladerf *dev, + const struct bladerf_trigger *trigger, + bool arm, + uint64_t resv1, + uint64_t resv2); + int (*trigger_fire)(struct bladerf *dev, + const struct bladerf_trigger *trigger); + int (*trigger_state)(struct bladerf *dev, + const struct bladerf_trigger *trigger, + bool *is_armed, + bool *has_fired, + bool *fire_requested, + uint64_t *resv1, + uint64_t *resv2); + + /* Streaming */ + int (*enable_module)(struct bladerf *dev, bladerf_channel ch, bool enable); + int (*init_stream)(struct bladerf_stream **stream, + struct bladerf *dev, + bladerf_stream_cb callback, + void ***buffers, + size_t num_buffers, + bladerf_format format, + size_t samples_per_buffer, + size_t num_transfers, + void *user_data); + int (*stream)(struct bladerf_stream *stream, bladerf_channel_layout layout); + int (*submit_stream_buffer)(struct bladerf_stream *stream, + void *buffer, + unsigned int timeout_ms, + bool nonblock); + void (*deinit_stream)(struct bladerf_stream *stream); + int (*set_stream_timeout)(struct bladerf *dev, + bladerf_direction dir, + unsigned int timeout); + int (*get_stream_timeout)(struct bladerf *dev, + bladerf_direction dir, + unsigned int *timeout); + int (*sync_config)(struct bladerf *dev, + bladerf_channel_layout layout, + bladerf_format format, + unsigned int num_buffers, + unsigned int buffer_size, + unsigned int num_transfers, + unsigned int stream_timeout); + int (*sync_tx)(struct bladerf *dev, + const void *samples, + unsigned int num_samples, + struct bladerf_metadata *metadata, + unsigned int timeout_ms); + int (*sync_rx)(struct bladerf *dev, + void *samples, + unsigned int num_samples, + struct bladerf_metadata *metadata, + unsigned int timeout_ms); + int (*get_timestamp)(struct bladerf *dev, + bladerf_direction dir, + bladerf_timestamp *timestamp); + + /* FPGA/Firmware Loading/Flashing */ + int (*load_fpga)(struct bladerf *dev, const uint8_t *buf, size_t length); + int (*flash_fpga)(struct bladerf *dev, const uint8_t *buf, size_t length); + int (*erase_stored_fpga)(struct bladerf *dev); + int (*flash_firmware)(struct bladerf *dev, + const uint8_t *buf, + size_t length); + int (*device_reset)(struct bladerf *dev); + + /* Tuning mode */ + int (*set_tuning_mode)(struct bladerf *dev, bladerf_tuning_mode mode); + int (*get_tuning_mode)(struct bladerf *dev, bladerf_tuning_mode *mode); + + /* Loopback */ + int (*get_loopback_modes)(struct bladerf *dev, + const struct bladerf_loopback_modes **modes); + int (*set_loopback)(struct bladerf *dev, bladerf_loopback l); + int (*get_loopback)(struct bladerf *dev, bladerf_loopback *l); + + /* Sample RX FPGA Mux */ + int (*get_rx_mux)(struct bladerf *dev, bladerf_rx_mux *mode); + int (*set_rx_mux)(struct bladerf *dev, bladerf_rx_mux mode); + + /* Low-level VCTCXO Tamer Mode */ + int (*set_vctcxo_tamer_mode)(struct bladerf *dev, + bladerf_vctcxo_tamer_mode mode); + int (*get_vctcxo_tamer_mode)(struct bladerf *dev, + bladerf_vctcxo_tamer_mode *mode); + + /* Low-level VCTCXO Trim DAC access */ + int (*get_vctcxo_trim)(struct bladerf *dev, uint16_t *trim); + int (*trim_dac_read)(struct bladerf *dev, uint16_t *trim); + int (*trim_dac_write)(struct bladerf *dev, uint16_t trim); + + /* Low-level Trigger control access */ + int (*read_trigger)(struct bladerf *dev, + bladerf_channel ch, + bladerf_trigger_signal trigger, + uint8_t *val); + int (*write_trigger)(struct bladerf *dev, + bladerf_channel ch, + bladerf_trigger_signal trigger, + uint8_t val); + + /* Low-level Wishbone Master access */ + int (*wishbone_master_read)(struct bladerf *dev, uint32_t addr, uint32_t *data); + int (*wishbone_master_write)(struct bladerf *dev, uint32_t addr, uint32_t data); + + /* Low-level Configuration GPIO access */ + int (*config_gpio_read)(struct bladerf *dev, uint32_t *val); + int (*config_gpio_write)(struct bladerf *dev, uint32_t val); + + /* Low-level SPI flash access */ + int (*erase_flash)(struct bladerf *dev, + uint32_t erase_block, + uint32_t count); + int (*read_flash)(struct bladerf *dev, + uint8_t *buf, + uint32_t page, + uint32_t count); + int (*write_flash)(struct bladerf *dev, + const uint8_t *buf, + uint32_t page, + uint32_t count); + + /* Expansion support */ + int (*expansion_attach)(struct bladerf *dev, bladerf_xb xb); + int (*expansion_get_attached)(struct bladerf *dev, bladerf_xb *xb); + + /* Board name */ + const char *name; +}; + +/* Information about the (SPI) flash architecture */ +struct bladerf_flash_arch { + enum { STATUS_FLASH_UNINITIALIZED, STATUS_SUCCESS, STATUS_ASSUMED } status; + + uint8_t manufacturer_id; /**< Raw manufacturer ID */ + uint8_t device_id; /**< Raw device ID */ + uint32_t tsize_bytes; /**< Total size of flash, in bytes */ + uint32_t psize_bytes; /**< Flash page size, in bytes */ + uint32_t ebsize_bytes; /**< Flash erase block size, in bytes */ + uint32_t num_pages; /**< Size of flash, in pages */ + uint32_t num_ebs; /**< Size of flash, in erase blocks */ +}; + +/* Boards */ +extern const struct board_fns *bladerf_boards[]; +extern const unsigned int bladerf_boards_len; + +#endif |