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