summaryrefslogtreecommitdiff
path: root/Radio/HW/BladeRF/src/board/bladerf2/common.c
diff options
context:
space:
mode:
Diffstat (limited to 'Radio/HW/BladeRF/src/board/bladerf2/common.c')
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf2/common.c415
1 files changed, 415 insertions, 0 deletions
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;
+}