summaryrefslogtreecommitdiff
path: root/Radio/HW/BladeRF
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
parentca50c0f64f1b2fce46b4cb83ed111854bac13852 (diff)
downloadPrySDR-cf4444e7390365df43ecbd3d130015c1e06ef88f.tar.gz
PrySDR-cf4444e7390365df43ecbd3d130015c1e06ef88f.zip
BladeRF library compiles
Diffstat (limited to 'Radio/HW/BladeRF')
-rw-r--r--Radio/HW/BladeRF/BladeRF.swift1
-rw-r--r--Radio/HW/BladeRF/common/include/conversions.h409
-rw-r--r--Radio/HW/BladeRF/common/include/dc_calibration.h151
-rw-r--r--Radio/HW/BladeRF/common/include/devcfg.h151
-rw-r--r--Radio/HW/BladeRF/common/include/host_config.h333
-rw-r--r--Radio/HW/BladeRF/common/include/host_config.h.in333
-rw-r--r--Radio/HW/BladeRF/common/include/iterators.h59
-rw-r--r--Radio/HW/BladeRF/common/include/log.h144
-rw-r--r--Radio/HW/BladeRF/common/include/logger_id.h61
-rw-r--r--Radio/HW/BladeRF/common/include/minmax.h65
-rw-r--r--Radio/HW/BladeRF/common/include/osx/clock_gettime.h52
-rw-r--r--Radio/HW/BladeRF/common/include/parse.h112
-rw-r--r--Radio/HW/BladeRF/common/include/range.h54
-rw-r--r--Radio/HW/BladeRF/common/include/rel_assert.h14
-rw-r--r--Radio/HW/BladeRF/common/include/sha256.h61
-rw-r--r--Radio/HW/BladeRF/common/include/str_queue.h93
-rw-r--r--Radio/HW/BladeRF/common/include/thread.h74
-rw-r--r--Radio/HW/BladeRF/common/src/conversions.c730
-rw-r--r--Radio/HW/BladeRF/common/src/dc_calibration.c1745
-rw-r--r--Radio/HW/BladeRF/common/src/devcfg.c638
-rw-r--r--Radio/HW/BladeRF/common/src/log.c98
-rw-r--r--Radio/HW/BladeRF/common/src/osx/clock_gettime.c59
-rw-r--r--Radio/HW/BladeRF/common/src/parse.c455
-rw-r--r--Radio/HW/BladeRF/common/src/range.c74
-rw-r--r--Radio/HW/BladeRF/common/src/sha256.c343
-rw-r--r--Radio/HW/BladeRF/common/src/str_queue.c193
-rw-r--r--Radio/HW/BladeRF/common/src/windows/clock_gettime.c58
-rw-r--r--Radio/HW/BladeRF/common/src/windows/getopt_long.c326
-rw-r--r--Radio/HW/BladeRF/common/src/windows/gettimeofday.c49
-rw-r--r--Radio/HW/BladeRF/common/src/windows/mkdtemp.c250
-rw-r--r--Radio/HW/BladeRF/common/src/windows/nanosleep.c41
-rw-r--r--Radio/HW/BladeRF/common/src/windows/setenv.c50
-rw-r--r--Radio/HW/BladeRF/common/thirdparty/ad936x/ad9361.c7218
-rw-r--r--Radio/HW/BladeRF/common/thirdparty/ad936x/ad9361.h3521
-rw-r--r--Radio/HW/BladeRF/common/thirdparty/ad936x/ad9361_api.c2126
-rw-r--r--Radio/HW/BladeRF/common/thirdparty/ad936x/ad9361_api.h506
-rw-r--r--Radio/HW/BladeRF/common/thirdparty/ad936x/ad9361_conv.c751
-rw-r--r--Radio/HW/BladeRF/common/thirdparty/ad936x/common.h100
-rw-r--r--Radio/HW/BladeRF/common/thirdparty/ad936x/util.c347
-rw-r--r--Radio/HW/BladeRF/common/thirdparty/ad936x/util.h186
-rw-r--r--Radio/HW/BladeRF/firmware_common/bladeRF.h133
-rw-r--r--Radio/HW/BladeRF/firmware_common/logger_entry.h74
-rw-r--r--Radio/HW/BladeRF/firmware_common/misc.h45
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/ad936x.h858
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/ad936x_helpers.h127
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/band_select.h50
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/bladerf2_common.h749
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/lms.h838
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/nios_pkt_16x64.h218
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/nios_pkt_32x32.h208
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x16.h213
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x32.h231
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x64.h206
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x8.h197
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/nios_pkt_formats.h37
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/nios_pkt_legacy.h231
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/nios_pkt_retune.h334
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/nios_pkt_retune2.h273
-rw-r--r--Radio/HW/BladeRF/fpga_common/src/ad936x_helpers.c196
-rw-r--r--Radio/HW/BladeRF/fpga_common/src/ad936x_params.c1024
-rw-r--r--Radio/HW/BladeRF/fpga_common/src/band_select.c59
-rw-r--r--Radio/HW/BladeRF/fpga_common/src/bladerf2_common.c194
-rw-r--r--Radio/HW/BladeRF/fpga_common/src/lms.c3637
-rw-r--r--Radio/HW/BladeRF/include/CMakeLists.txt12
-rw-r--r--Radio/HW/BladeRF/include/bladeRF1.h1569
-rw-r--r--Radio/HW/BladeRF/include/bladeRF2.h561
-rw-r--r--Radio/HW/BladeRF/include/device_calibration.h128
-rw-r--r--Radio/HW/BladeRF/include/libbladeRF.h4501
-rw-r--r--Radio/HW/BladeRF/src/backend/backend.c172
-rw-r--r--Radio/HW/BladeRF/src/backend/backend.h364
-rw-r--r--Radio/HW/BladeRF/src/backend/backend_config.h88
-rw-r--r--Radio/HW/BladeRF/src/backend/backend_config.h.in88
-rw-r--r--Radio/HW/BladeRF/src/backend/dummy/dummy.c554
-rw-r--r--Radio/HW/BladeRF/src/backend/dummy/dummy.h32
-rw-r--r--Radio/HW/BladeRF/src/backend/usb/cyapi.c854
-rw-r--r--Radio/HW/BladeRF/src/backend/usb/libusb.c1504
-rw-r--r--Radio/HW/BladeRF/src/backend/usb/nios_access.c1322
-rw-r--r--Radio/HW/BladeRF/src/backend/usb/nios_access.h582
-rw-r--r--Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.c744
-rw-r--r--Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.h488
-rw-r--r--Radio/HW/BladeRF/src/backend/usb/usb.c1430
-rw-r--r--Radio/HW/BladeRF/src/backend/usb/usb.h168
-rw-r--r--Radio/HW/BladeRF/src/bladerf.c2387
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/bladerf1.c4166
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/calibration.c518
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/calibration.h107
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/capabilities.c118
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/capabilities.h52
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/compatibility.c75
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/compatibility.h9
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/flash.c543
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/flash.h172
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/image.c592
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf2/bladerf2.c3744
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf2/capabilities.c106
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf2/capabilities.h52
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf2/common.c415
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf2/common.h494
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf2/compatibility.c50
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf2/compatibility.h9
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf2/rfic_fpga.c767
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf2/rfic_host.c1058
-rw-r--r--Radio/HW/BladeRF/src/board/board.c13
-rw-r--r--Radio/HW/BladeRF/src/board/board.h495
-rw-r--r--Radio/HW/BladeRF/src/device_calibration.c522
-rw-r--r--Radio/HW/BladeRF/src/devinfo.c420
-rw-r--r--Radio/HW/BladeRF/src/devinfo.h131
-rw-r--r--Radio/HW/BladeRF/src/driver/dac161s055.c65
-rw-r--r--Radio/HW/BladeRF/src/driver/dac161s055.h51
-rw-r--r--Radio/HW/BladeRF/src/driver/fpga_trigger.c196
-rw-r--r--Radio/HW/BladeRF/src/driver/fpga_trigger.h128
-rw-r--r--Radio/HW/BladeRF/src/driver/fx3_fw.c343
-rw-r--r--Radio/HW/BladeRF/src/driver/fx3_fw.h79
-rw-r--r--Radio/HW/BladeRF/src/driver/ina219.c154
-rw-r--r--Radio/HW/BladeRF/src/driver/ina219.h83
-rw-r--r--Radio/HW/BladeRF/src/driver/si5338.c669
-rw-r--r--Radio/HW/BladeRF/src/driver/si5338.h134
-rw-r--r--Radio/HW/BladeRF/src/driver/smb_clock.c211
-rw-r--r--Radio/HW/BladeRF/src/driver/smb_clock.h47
-rw-r--r--Radio/HW/BladeRF/src/driver/spi_flash.c134
-rw-r--r--Radio/HW/BladeRF/src/driver/spi_flash.h99
-rw-r--r--Radio/HW/BladeRF/src/expansion/xb100.c98
-rw-r--r--Radio/HW/BladeRF/src/expansion/xb100.h47
-rw-r--r--Radio/HW/BladeRF/src/expansion/xb200.c543
-rw-r--r--Radio/HW/BladeRF/src/expansion/xb200.h105
-rw-r--r--Radio/HW/BladeRF/src/expansion/xb300.c299
-rw-r--r--Radio/HW/BladeRF/src/expansion/xb300.h94
-rw-r--r--Radio/HW/BladeRF/src/helpers/configfile.c342
-rw-r--r--Radio/HW/BladeRF/src/helpers/configfile.h27
-rw-r--r--Radio/HW/BladeRF/src/helpers/file.c511
-rw-r--r--Radio/HW/BladeRF/src/helpers/file.h97
-rw-r--r--Radio/HW/BladeRF/src/helpers/have_cap.h43
-rw-r--r--Radio/HW/BladeRF/src/helpers/interleave.c182
-rw-r--r--Radio/HW/BladeRF/src/helpers/interleave.h44
-rw-r--r--Radio/HW/BladeRF/src/helpers/timeout.c52
-rw-r--r--Radio/HW/BladeRF/src/helpers/timeout.h40
-rw-r--r--Radio/HW/BladeRF/src/helpers/version.c177
-rw-r--r--Radio/HW/BladeRF/src/helpers/version.h142
-rw-r--r--Radio/HW/BladeRF/src/helpers/wallclock.c47
-rw-r--r--Radio/HW/BladeRF/src/helpers/wallclock.h29
-rw-r--r--Radio/HW/BladeRF/src/init_fini.c103
-rw-r--r--Radio/HW/BladeRF/src/libbladerf-Bridging-Header.h8
-rw-r--r--Radio/HW/BladeRF/src/streaming/async.c294
-rw-r--r--Radio/HW/BladeRF/src/streaming/async.h111
-rw-r--r--Radio/HW/BladeRF/src/streaming/format.h109
-rw-r--r--Radio/HW/BladeRF/src/streaming/metadata.h180
-rw-r--r--Radio/HW/BladeRF/src/streaming/sync.c1339
-rw-r--r--Radio/HW/BladeRF/src/streaming/sync.h193
-rw-r--r--Radio/HW/BladeRF/src/streaming/sync_worker.c532
-rw-r--r--Radio/HW/BladeRF/src/streaming/sync_worker.h130
-rw-r--r--Radio/HW/BladeRF/src/test.swift8
-rw-r--r--Radio/HW/BladeRF/src/version.h12
-rw-r--r--Radio/HW/BladeRF/src/version.h.in12
-rw-r--r--Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/adc_core.c261
-rw-r--r--Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/adc_core.h169
-rw-r--r--Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/config.h67
-rw-r--r--Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/dac_core.c717
-rw-r--r--Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/dac_core.h199
-rw-r--r--Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/platform.c295
-rw-r--r--Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/platform.h169
160 files changed, 74559 insertions, 0 deletions
diff --git a/Radio/HW/BladeRF/BladeRF.swift b/Radio/HW/BladeRF/BladeRF.swift
index 1b72f62..7876b96 100644
--- a/Radio/HW/BladeRF/BladeRF.swift
+++ b/Radio/HW/BladeRF/BladeRF.swift
@@ -5,6 +5,7 @@
// Created by Jacky Jack on 25/10/2024.
//
+//import libbladerrf
/// Wrapper and routines for BladeRF
class BladeRF {
diff --git a/Radio/HW/BladeRF/common/include/conversions.h b/Radio/HW/BladeRF/common/include/conversions.h
new file mode 100644
index 0000000..5555a29
--- /dev/null
+++ b/Radio/HW/BladeRF/common/include/conversions.h
@@ -0,0 +1,409 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014-2018 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef CONVERSIONS_H_
+#define CONVERSIONS_H_
+
+#include <errno.h>
+#include <libbladeRF.h>
+#include <limits.h>
+#include <math.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "host_config.h"
+#include "rel_assert.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Convert a string to a bladerf_version
+ *
+ * Accepted inputs are in the form: X.Y.Z or X.Y.Z-<extra text>
+ *
+ * @param[in] string Version string to convert
+ * @param[out] version Version structure to populate. For a valid string,
+ * the describe member will point to the provided
+ * string argument. The contents of this structure
+ * are undefined when this function returns -1.
+ *
+ * @return 0 on success, -1 if provided string is invalid
+ */
+int str2version(const char *str, struct bladerf_version *version);
+
+/**
+ * Convert a bladerf_dev_speed to a string suitable for printing
+ *
+ * @note The caller should not attempt to modify or free() the returned string.
+ *
+ * @param speed Device speed
+ * @return Const string describing the provided speed
+ */
+const char *devspeed2str(bladerf_dev_speed speed);
+
+/**
+ * Convert a string to libbladeRF log verbosity level
+ *
+ * @param[in] str Input string
+ * @param[out] ok Value is updated to true if the input string was valid
+ *
+ * @return Log level if ok is true, undefined otherwise
+ */
+bladerf_log_level str2loglevel(const char *str, bool *ok);
+
+/**
+ * Convert a module enumeration to a string
+ *
+ * @note The caller should not attempt to modify or free() the returned string.
+ *
+ * @param module Module to convert to string
+ * @return String representation of module
+ */
+const char *module2str(bladerf_module m);
+
+/**
+ * Convert a string to a module enumeration value.
+ *
+ * This is case-insensitive.
+ *
+ * @param str Module as a string. Should be "rx" or "tx".
+ *
+ * @return BLADERF_MODULE_RX, BLADERF_MODULE_TX, or BLADERF_MODULE_INVALID
+ */
+bladerf_module str2module(const char *str);
+
+/**
+ * Convert a channel index to a string
+ *
+ * @note The caller should not attempt to modify or free() the returned string.
+ *
+ * @param ch Channel
+ * @return String representation of channel
+ */
+const char *channel2str(bladerf_channel ch);
+
+/**
+ * Convert a string to a channel index
+ *
+ * This is case-insensitive.
+ *
+ * @param str Channel as a string.
+ * @return BLADERF_CHANNEL_RX(n) or BLADERF_CHANNEL_TX(n),
+ * or BLADERF_CHANNEL_INVALID if not recognized
+ */
+bladerf_channel str2channel(char const *str);
+
+/**
+ * Convert a direction enumeration to a string
+ *
+ * @note The caller should not attempt to modify or free() the returned string.
+ *
+ * @param dir direction
+ * @return String representation of direction
+ */
+const char *direction2str(bladerf_direction dir);
+
+/**
+ * Convert a channel enumeration to a direction
+ *
+ * @param ch channel
+ * @return Direction of the channel
+ */
+bladerf_direction channel2direction(bladerf_channel ch);
+
+/**
+ * Convert a trigger signal enumeration value to a string
+ *
+ * @param trigger Trigger item
+ *
+ * @return String representation or "Unknown"
+ */
+const char *trigger2str(bladerf_trigger_signal trigger);
+
+/**
+ * Conver a string to a trigger signal enumeration value.
+ *
+ * This is case-insensitive.
+ *
+ * @param str Trigger as a string. Valid values include `Miniexp-1`,
+ * `J51-1`, `J71-4`, or `User-0` through `User-7`.
+ *
+ * @return valid bladerf_trigger_signal value, or BLADERF_TRIGGER_INVALID
+ */
+bladerf_trigger_signal str2trigger(const char *str);
+
+/**
+ * Convert a trigger role enumeration value to a string
+ *
+ * @param role Role value
+ *
+ * @return String representation or "Unknown"
+ */
+const char *triggerrole2str(bladerf_trigger_role role);
+
+/**
+ * Convert a string to a trigger role enumeration value
+ *
+ * @param role Role value
+ *
+ * @return String representation or "Unknown"
+ */
+bladerf_trigger_role str2triggerrole(const char *str);
+
+/**
+ * Convert a string to a loopback mode
+ *
+ * @param[in] str String to convert
+ * @param[out] loopback Corresponding loopback mode. Only valid when
+ * this function returns successfully
+ *
+ * @return 0 on success, -1 on invalid string
+ */
+int str2loopback(const char *str, bladerf_loopback *loopback);
+
+/**
+ * @brief Convert a loopback mode to a string const
+ *
+ * @param[in] loopback The loopback mode
+ *
+ * @return NUL-terminated string
+ */
+char const *loopback2str(bladerf_loopback loopback);
+
+/**
+ * Convert RX LNA gain strings to their associated enum values
+ *
+ * @param[in] str Gain string to convert
+ * @param[out] gain Associated LNA gain string. Set to
+ * BLADERF_LNA_GAIN_UNKNOWN on error.
+ *
+ * @return 0 on success, -1 on invalid string
+ */
+int str2lnagain(const char *str, bladerf_lna_gain *gain);
+
+/**
+ * @brief Convert a tuning mode to a string const
+ *
+ * @param[in] mode The tuning mode
+ *
+ * @return NUL-terminated string
+ */
+char const *tuningmode2str(bladerf_tuning_mode mode);
+
+/**
+ * Get a string description of the specified bladeRF backend
+ *
+ * @param b Backend to get a string for
+ *
+ * @return NUL-terminated string
+ */
+const char *backend_description(bladerf_backend b);
+
+/**
+ * Convert bladeRF SC16Q11 DAC/ADC samples to floats
+ *
+ * Note that the both the input and output buffers contain interleaved, where
+ * 1 sample is associated with two array elements:
+ * [I, Q, I, Q, ... I, Q]
+ *
+ * Therefore, the caller must ensure the output buffer large enough to contain
+ * 2*n floats (or 2*n*sizeof(float) bytes).
+ *
+ * @param[in] in Input buffer containing SC16Q11 samples
+ * @param[out] out Output buffer of float values
+ * @param[in] n Number of samples to convert
+ */
+void sc16q11_to_float(const int16_t *in, float *out, unsigned int n);
+
+/**
+ * Convert float samples to bladeRF SC16Q11 DAC/ADC format
+ *
+ * Note that the both the input and output buffers contain interleaved, where
+ * 1 sample is associated with two array elements:
+ * [I, Q, I, Q, ... I, Q]
+ *
+ * Therefore, the caller must ensure the output buffer large enough to contain
+ * 2*n int16_t's (or 2*n*sizeof(int16_t) bytes).
+ *
+ * @param[in] in Input buffer containing float samples
+ * @param[out] out Output buffer of int16_t values
+ * @param[in] n Number of samples to convert
+ */
+void float_to_sc16q11(const float *in, int16_t *out, unsigned int n);
+
+/**
+ * Convert a string to a bladerf_cal_module value
+ *
+ * @param[in] str String to convert
+ *
+ * @return A bladerf_cal_module value. This will be set to
+ * BLADERF_DC_CAL_INVALID if the provided string is invalid.
+ */
+bladerf_cal_module str_to_bladerf_cal_module(const char *str);
+
+/**
+ * Convert a bladerf_smb_mode enumeration value to a string
+ *
+ * @param[in] mode Mode enum value
+ *
+ * @return String representation of enumeration, or "Unknown" for an
+ * invalid value.
+ */
+const char *smb_mode_to_str(bladerf_smb_mode mode);
+
+/**
+ * Convert a string to bladerf_smb_mode value
+ *
+ * @param[in] str String to convert
+ *
+ * @return A BLADERF_SMB_MODE_* value. BLADERF_SMB_MODE_INVALID will
+ * returned for an invalid string.
+ */
+bladerf_smb_mode str_to_smb_mode(const char *str);
+
+/**
+ * Convert a string to bladerf_tuning_mode value
+ *
+ * @param[in] str String to convert
+ *
+ * @return A BLADERF_TUNING_MODE_* value. BLADERF_TUNING_MODE_INVALID will
+ * returned for an invalid string.
+ */
+bladerf_tuning_mode str_to_tuning_mode(const char *str);
+
+/**
+ * Convert an ASCII char string to an unsigned integer and check its bounds
+ *
+ * @param[in] str String to convert
+ * @param[in] min Value below this is bad
+ * @param[in] max Value above this is bad
+ * @param[out] ok True if conversion and bounds check did not fail
+ *
+ * @return An unsigned integer converted from an ASCII string
+ */
+unsigned int str2uint(const char *str,
+ unsigned int min,
+ unsigned int max,
+ bool *ok);
+
+/**
+ * Convert an ASCII char string to an integer and check its bounds
+ *
+ * @param[in] str String to convert
+ * @param[in] min Value below this is bad
+ * @param[in] max Value above this is bad
+ * @param[out] ok True if conversion and bounds check did not fail
+ *
+ * @return A signed integer converted from an ASCII string
+ */
+int str2int(const char *str, int min, int max, bool *ok);
+
+/**
+ * Convert an ASCII char string to a 64bit unsigned long long integer and check
+ * its bounds
+ *
+ * @param[in] str String to convert
+ * @param[in] min Value below this is bad
+ * @param[in] max Value above this is bad
+ * @param[out] ok True if conversion and bounds check did not fail
+ *
+ * @return An unsigned long long integer converted from an ASCII string
+ */
+uint64_t str2uint64(const char *str, uint64_t min, uint64_t max, bool *ok);
+
+/**
+ * Convert an ASCII char string to a double and check its bounds
+ *
+ * @param[in] str String to convert
+ * @param[in] min Value below this is bad
+ * @param[in] max Value above this is bad
+ * @param[out] ok True if conversion and bounds check did not fail
+ *
+ * @return A double converted from an ASCII string
+ */
+double str2double(const char *str, double min, double max, bool *ok);
+struct numeric_suffix {
+ const char *suffix;
+ uint64_t multiplier;
+};
+typedef struct numeric_suffix numeric_suffix;
+
+/**
+ * Convert an ASCII char string that has a suffix multipler to an unsigned
+ * integer and check its bounds
+ *
+ * @param[in] str String to convert
+ * @param[in] min Value below this is bad
+ * @param[in] max Value above this is bad
+ * @param[in] suffixes Array of numeric_suffix
+ * @param[in] num_suff Total number of numeric_suffix in suffixes array
+ * @param[out] ok True if conversion and bounds check did not fail
+ *
+ * @return An unsigned integer converted from an ASCII string
+ */
+unsigned int str2uint_suffix(const char *str,
+ unsigned int min,
+ unsigned int max,
+ const struct numeric_suffix *suffixes,
+ const size_t num_suff,
+ bool *ok);
+
+/**
+ * Convert an ASCII char string that has a suffix multipler to an unsigned
+ * integer and check its bounds
+ *
+ * @param[in] str String to convert
+ * @param[in] min Value below this is bad
+ * @param[in] max Value above this is bad
+ * @param[in] suffixes Array of numeric_suffix
+ * @param[in] num_suff Total number of numeric_suffix in suffixes array
+ * @param[out] ok True if conversion and bounds check did not fail
+ *
+ * @return An unsigned long long integer converted from an ASCII string
+ */
+uint64_t str2uint64_suffix(const char *str,
+ uint64_t min,
+ uint64_t max,
+ const struct numeric_suffix *suffixes,
+ const size_t num_suff,
+ bool *ok);
+
+/**
+ * Convert a string to a boolean
+ *
+ * @param[in] str String to convert
+ * @param[out] val Boolean value
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+int str2bool(const char *str, bool *val);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/Radio/HW/BladeRF/common/include/dc_calibration.h b/Radio/HW/BladeRF/common/include/dc_calibration.h
new file mode 100644
index 0000000..7259292
--- /dev/null
+++ b/Radio/HW/BladeRF/common/include/dc_calibration.h
@@ -0,0 +1,151 @@
+/**
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* This file provides DC calibration routines that utilize libbladeRF */
+
+#ifndef DC_CALIBRATION_H_
+#define DC_CALIBRATION_H_
+
+#include <libbladeRF.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct dc_calibration_params {
+ uint64_t frequency;
+ int16_t corr_i;
+ int16_t corr_q;
+ float error_i;
+ float error_q;
+
+ int16_t max_dc_i;
+ int16_t max_dc_q;
+ int16_t mid_dc_i;
+ int16_t mid_dc_q;
+ int16_t min_dc_i;
+ int16_t min_dc_q;
+};
+
+/**
+ * Calibrate the specified LMS6002D module.
+ *
+ * Generally, one should run all of these calibrations prior to executing the RX
+ * and TX auto calibration routines.
+ *
+ * Streams should not be running when attempting to call this function.
+ *
+ * Below are the case-insensitive options for the LMS6002D `module` string:
+ * - LPF Tuning: "lpf_tuning", "lpftuning", "tuning"
+ * - TX LPF: "tx_lpf", "txlpf"
+ * - RX LPF: "rx_lpf", "rxlpf"
+ * - RX VGA2 "rx_vga2", "rxvga2"
+ * - All of the above: "all"
+ *
+ * @param dev bladeRF device handle
+ * @param module LMS6002D module to calibrate. Passing NULL is synonymous
+ * with specifying "ALL".
+ *
+ * @return 0 on success or libbladeRF return value on failure.
+ * BLADERF_ERR_INVAL is returned if `module` is invalid.
+ */
+int dc_calibration_lms6(struct bladerf *dev, const char *module);
+
+/**
+ * Perform DC offset calbration of the RX path. Neither RX or TX should be
+ * used during this procedure, as the TX frequency may be adjusted to ensure
+ * good results are obtained.
+ *
+ * The RX input should be disconected from any input sources or antennas when
+ * performing this calibration.
+ *
+ * @pre dc_calibration_lms6() should have been called for all modules prior to
+ * using this function.
+ *
+ * @param[in] dev bladeRF device handle
+ *
+ * @param[inout] params DC calibration input and output parameters. This
+ * list should be populated to describe the
+ * frequencies over which to calibrate. The
+ * `corr_i` and `corr_q` fields will be filled in
+ * for each entry.
+ *
+ * @param[in] num_params Number of entries in the `params` list.
+ *
+ * @param[in] show_status Print status information to stdout
+ *
+ * @return 0 on success or libbladeRF return value on failure.
+ */
+int dc_calibration_rx(struct bladerf *dev,
+ struct dc_calibration_params *params,
+ size_t num_params, bool show_status);
+
+/**
+ * Perform DC offset calbration of the TX path.
+ *
+ * This requires use of both RX and TX modules in an internal loopback mode;
+ * neither should be in use when this function is called. It should not
+ * be neccessary to disconnect the RX and TX ports, but it is still recommended,
+ * just to err on the side of caution.
+ *
+ * @pre dc_calibration_lms6() should have been called for all modules prior to
+ * using this function.
+ *
+ * @param dev bladeRF device handle
+ *
+ * @return 0 on success or libbladeRF return value on failure.
+ */
+int dc_calibration_tx(struct bladerf *dev,
+ struct dc_calibration_params *params,
+ size_t num_params, bool show_status);
+
+/**
+ * Convenience wrapper around dc_cal_rx() and dc_cal_tx()
+ *
+ * @pre dc_calibration_lms6() should have been called for all modules prior to
+ * using this function.
+ *
+ * @param[in] dev Device handle
+ * @param[in] module BLADERF_MODULE_RX or BLADERF_MODULE_TX
+ * @param[inout] params DC calibration input and output parameters. This
+ * list should be populated to describe the
+ * frequencies over which to calibrate. The
+ * `corr_i` and `corr_q` fields will be filled in
+ * for each entry.
+ *
+ * @param[in] num_params Number of entries in the `params` list.
+ * @param[in] show_status Print status information to stdout
+ *
+ * @return 0 on success or libbladeRF return value on failure.
+ */
+int dc_calibration(struct bladerf *dev, bladerf_module module,
+ struct dc_calibration_params *params,
+ size_t num_params, bool show_status);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/Radio/HW/BladeRF/common/include/devcfg.h b/Radio/HW/BladeRF/common/include/devcfg.h
new file mode 100644
index 0000000..3ebf837
--- /dev/null
+++ b/Radio/HW/BladeRF/common/include/devcfg.h
@@ -0,0 +1,151 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014-2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef DEVCFG_H_
+#define DEVCFG_H_
+
+#include <stdint.h>
+#include <libbladeRF.h>
+
+#include "rel_assert.h"
+#include "host_config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DEVCFG_OPTIONS_BASE "hd:l:v:s:b:f:"
+
+/**
+ * Device configuration parameters
+ */
+struct devcfg {
+ char *device_specifier;
+
+ unsigned int tx_frequency;
+ unsigned int tx_bandwidth;
+ unsigned int tx_samplerate;
+ int txvga1;
+ int txvga2;
+
+ unsigned int rx_frequency;
+ unsigned int rx_bandwidth;
+ unsigned int rx_samplerate;
+ bladerf_lna_gain lnagain;
+ int rxvga1;
+ int rxvga2;
+
+ bladerf_loopback loopback;
+
+ bladerf_log_level verbosity;
+
+ bool enable_xb200;
+
+ unsigned int samples_per_buffer;
+ unsigned int num_buffers;
+ unsigned int num_transfers;
+ unsigned int stream_timeout_ms;
+ unsigned int sync_timeout_ms;
+};
+
+/**
+ * Initialize the specified device_config structure to default values
+ *
+ * @param c Configuration to initialize
+ */
+void devcfg_init(struct devcfg *c);
+
+/**
+ * Deinitialize any items allocated in the provided device configuration
+ *
+ * @param c Configuration to deinitialize
+ */
+void devcfg_deinit(struct devcfg *c);
+
+/**
+ * Creates an option array with the app-specific options appended to the common
+ * devcfg options. The app_options should be 0-terminated.
+ *
+ * The caller is responsible for freeing the resturned structure.
+ *
+ * @return Heap-allocated array on success, NULL on failure
+ */
+struct option *devcfg_get_long_options(const struct option *app_options);
+
+/**
+ * Handle command line arguments and update the specified device configuration
+ * accordingly.
+ *
+ * @param argc # of arguments in argv
+ * @param argv Array of arguments
+ *
+ * @param option_str Options string to pass to getopt_long(). This should
+ * include DEVCFG_OPTIONS_BASE, with any
+ * application-handled options appended.
+ *
+ * @param long_options List of long options created by caller
+ *
+ * @param c Device configuration to update. It should have
+ * already been initialized via devcfg_init()
+ *
+ * @return 0 on success, -1 on failure, 1 if help is requested
+ */
+int devcfg_handle_args(int argc, char **argv,
+ const char *options, const struct option *long_options,
+ struct devcfg *c);
+
+/**
+ * Wrapper around bladerf_sync_config, using parameters in the specified
+ * devcfg. Optionally, enable the associated module.
+ */
+int devcfg_perform_sync_config(struct bladerf *dev,
+ bladerf_module module,
+ bladerf_format format,
+ const struct devcfg *config,
+ bool enable_module);
+
+
+/**
+ * Apply the provided device_config to the specified device
+ *
+ * @param dev Device to configure
+ * @param c Configuration parameters
+ *
+ * @return 0 on succes, -1 on failure
+ */
+int devcfg_apply(struct bladerf *dev, const struct devcfg *c);
+
+/**
+ * Print help info about arguments handled by devcfg_handle_args()
+ *
+ * @param title Optional title to print before usage information
+ */
+void devcfg_print_common_help(const char *title);
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/Radio/HW/BladeRF/common/include/host_config.h b/Radio/HW/BladeRF/common/include/host_config.h
new file mode 100644
index 0000000..15dded3
--- /dev/null
+++ b/Radio/HW/BladeRF/common/include/host_config.h
@@ -0,0 +1,333 @@
+/**
+ * @file host_config.h.in
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2013-2018 Nuand LLC.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef HOST_CONFIG_H__
+#define HOST_CONFIG_H__
+
+#define BLADERF_OS_LINUX 0
+#define BLADERF_OS_FREEBSD 0
+#define BLADERF_OS_OSX 1
+#define BLADERF_OS_WINDOWS 0
+#define BLADERF_BIG_ENDIAN 0
+
+#if !(BLADERF_OS_LINUX || BLADERF_OS_OSX || BLADERF_OS_WINDOWS || BLADERF_OS_FREEBSD)
+# error "Build not configured for any supported operating systems"
+#endif
+
+#if 1 < (BLADERF_OS_LINUX + BLADERF_OS_OSX + BLADERF_OS_WINDOWS + BLADERF_OS_FREEBSD)
+#error "Build configured for multiple operating systems"
+#endif
+
+#define HAVE_CLOCK_GETTIME 1
+
+/*******************************************************************************
+ * Endianness conversions
+ *
+ * HOST_TO_LE16 16-bit host endianness to little endian conversion
+ * LE16_TO_HOST 16-bit little endian to host endianness conversion
+ * HOST_TO_BE16 16-bit host endianness to big endian conversion
+ * BE16_TO_HOST 16-bit big endian to host endianness conversion
+ * HOST_TO_LE32 32-bit host endianness to little endian conversion
+ * LE32_TO_HOST 32-bit little endian to host endianness conversion
+ * HOST_TO_BE32 32-bit host endianness to big endian conversion
+ * BE32_TO_HOST 32-bit big endian to host endianness conversion
+ * HOST_TO_BE64 64-bit host endianness to big endian conversion
+ * BE64_TO_HOST 64-bit big endian to host endianness conversion
+ ******************************************************************************/
+
+/*-----------------------
+ * Linux
+ *---------------------*/
+#if BLADERF_OS_LINUX
+#include <endian.h>
+
+#define HOST_TO_LE16(val) htole16(val)
+#define LE16_TO_HOST(val) le16toh(val)
+#define HOST_TO_BE16(val) htobe16(val)
+#define BE16_TO_HOST(val) be16toh(val)
+
+#define HOST_TO_LE32(val) htole32(val)
+#define LE32_TO_HOST(val) le32toh(val)
+#define HOST_TO_BE32(val) htobe32(val)
+#define BE32_TO_HOST(val) be32toh(val)
+
+#define HOST_TO_LE64(val) htole64(val)
+#define LE64_TO_HOST(val) le64toh(val)
+#define HOST_TO_BE64(val) be64toh(val)
+#define BE64_TO_HOST(val) be64toh(val)
+
+/*-----------------------
+ * FREEBSD
+ *---------------------*/
+#elif BLADERF_OS_FREEBSD
+#include <sys/endian.h>
+
+#define HOST_TO_LE16(val) htole16(val)
+#define LE16_TO_HOST(val) le16toh(val)
+#define HOST_TO_BE16(val) htobe16(val)
+#define BE16_TO_HOST(val) be16toh(val)
+
+#define HOST_TO_LE32(val) htole32(val)
+#define LE32_TO_HOST(val) le32toh(val)
+#define HOST_TO_BE32(val) htobe32(val)
+#define BE32_TO_HOST(val) be32toh(val)
+
+#define HOST_TO_LE64(val) htole64(val)
+#define LE64_TO_HOST(val) le64toh(val)
+#define HOST_TO_BE64(val) be64toh(val)
+#define BE64_TO_HOST(val) be64toh(val)
+
+/*-----------------------
+ * Apple OSX
+ *---------------------*/
+#elif BLADERF_OS_OSX
+#include <libkern/OSByteOrder.h>
+
+#define HOST_TO_LE16(val) OSSwapHostToLittleInt16(val)
+#define LE16_TO_HOST(val) OSSwapLittleToHostInt16(val)
+#define HOST_TO_BE16(val) OSSwapHostToBigInt16(val)
+#define BE16_TO_HOST(val) OSSwapBigToHostInt16(val)
+
+#define HOST_TO_LE32(val) OSSwapHostToLittleInt32(val)
+#define LE32_TO_HOST(val) OSSwapLittleToHostInt32(val)
+#define HOST_TO_BE32(val) OSSwapHostToBigInt32(val)
+#define BE32_TO_HOST(val) OSSwapBigToHostInt32(val)
+
+#define HOST_TO_LE64(val) OSSwapHostToLittleInt64(val)
+#define LE64_TO_HOST(val) OSSwapLittleToHostInt64(val)
+#define HOST_TO_BE64(val) OSSwapHostToBigInt64(val)
+#define BE64_TO_HOST(val) OSSwapBigToHostInt64(val)
+
+/*-----------------------
+ * Windows
+ *---------------------*/
+#elif BLADERF_OS_WINDOWS
+#include <intrin.h>
+
+/* We'll assume little endian for Windows platforms:
+ * http://blogs.msdn.com/b/larryosterman/archive/2005/06/07/426334.aspx
+ */
+#define HOST_TO_LE16(val) (val)
+#define LE16_TO_HOST(val) (val)
+#define HOST_TO_BE16(val) _byteswap_ushort(val)
+#define BE16_TO_HOST(val) _byteswap_ushort(val)
+
+#define HOST_TO_LE32(val) (val)
+#define LE32_TO_HOST(val) (val)
+#define HOST_TO_BE32(val) _byteswap_ulong(val)
+#define BE32_TO_HOST(val) _byteswap_ulong(val)
+
+
+#define HOST_TO_LE64(val) (val)
+#define LE64_TO_HOST(val) (val)
+#define HOST_TO_BE64(val) _byteswap_uint64(val)
+#define BE64_TO_HOST(val) _byteswap_uint64(val)
+
+/*-----------------------
+ * Unsupported or bug
+ *---------------------*/
+#else
+#error "Encountered an OS that we do not have endian wrappers for?"
+#endif
+
+/*******************************************************************************
+ * Endianness conversions for constants
+ *
+ * We shouldn't be using the above items for assigning constants because the
+ * implementations may be functions [1].
+ *
+ * [1] https://sourceware.org/bugzilla/show_bug.cgi?id=17679#c7
+ ******************************************************************************/
+
+#define BLADERF_BYTESWAP16(x) ((((x) & 0xff00) >> 8) | (((x) & 0x00ff) << 8))
+
+#define BLADERF_BYTESWAP32(x) ((((x) & 0xff000000) >> 24) | \
+ (((x) & 0x00ff0000) >> 8) | \
+ (((x) & 0x0000ff00) << 8) | \
+ (((x) & 0x000000ff) << 24) )
+
+#define BLADERF_BYTESWAP64(x) ((((x) & 0xff00000000000000llu) >> 56) | \
+ (((x) & 0x00ff000000000000llu) >> 40) | \
+ (((x) & 0x0000ff0000000000llu) >> 24) | \
+ (((x) & 0x000000ff00000000llu) >> 8) | \
+ (((x) & 0x00000000ff000000llu) << 8) | \
+ (((x) & 0x0000000000ff0000llu) << 24) | \
+ (((x) & 0x000000000000ff00llu) << 40) | \
+ (((x) & 0x00000000000000ffllu) << 56) | \
+
+#if BLADERF_BIG_ENDIAN
+# define HOST_TO_LE16_CONST(x) (BLADERF_BYTESWAP16(x))
+# define LE16_TO_HOST_CONST(x) (BLADERF_BYTESWAP16(x))
+# define HOST_TO_BE16_CONST(x) (x)
+# define BE16_TO_HOST_CONST(x) (x)
+
+# define HOST_TO_LE32_CONST(x) (BLADERF_BYTESWAP32(x))
+# define LE32_TO_HOST_CONST(x) (BLADERF_BYTESWAP32(x))
+# define HOST_TO_BE32_CONST(x) (x)
+# define BE32_TO_HOST_CONST(x) (x)
+
+# define HOST_TO_LE64_CONST(x) (BLADERF_BYTESWAP64(x))
+# define LE64_TO_HOST_CONST(x) (BLADERF_BYTESWAP64(x))
+# define HOST_TO_BE64_CONST(x) (x)
+# define BE64_TO_HOST_CONST(x) (x)
+#else
+# define HOST_TO_LE16_CONST(x) (x)
+# define LE16_TO_HOST_CONST(x) (x)
+# define HOST_TO_BE16_CONST(x) (BLADERF_BYTESWAP16(x))
+# define BE16_TO_HOST_CONST(x) (BLADERF_BYTESWAP16(x))
+
+# define HOST_TO_LE32_CONST(x) (x)
+# define LE32_TO_HOST_CONST(x) (x)
+# define HOST_TO_BE32_CONST(x) (BLADERF_BYTESWAP32(x))
+# define BE32_TO_HOST_CONST(x) (BLADERF_BYTESWAP32(x))
+
+# define HOST_TO_LE64_CONST(x) (x)
+# define LE64_TO_HOST_CONST(x) (x)
+# define HOST_TO_BE64_CONST(x) (BLADERF_BYTESWAP64(x))
+# define BE64_TO_HOST_CONST(x) (BLADERF_BYTESWAP64(x))
+#endif
+
+/*******************************************************************************
+ * Miscellaneous Linux fixups
+ ******************************************************************************/
+#if BLADERF_OS_LINUX
+
+/* Added here just to keep this out of the other source files. We'll have
+ * a few Windows replacements for unistd.h items. */
+#include <unistd.h>
+#include <pwd.h>
+
+/*******************************************************************************
+ * Miscellaneous FREEBSD fixups
+ ******************************************************************************/
+#elif BLADERF_OS_FREEBSD
+
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/sysctl.h>
+
+/*******************************************************************************
+ * Miscellaneous OSX fixups
+ ******************************************************************************/
+#elif BLADERF_OS_OSX
+
+#include <unistd.h>
+#include <pwd.h>
+
+/*******************************************************************************
+ * Miscellaneous Windows fixups
+ ******************************************************************************/
+#elif BLADERF_OS_WINDOWS
+
+/* Rename a few functions and types */
+#include <windows.h>
+#include <io.h>
+#include <direct.h>
+#define usleep(x) Sleep((x+999)/1000)
+
+/* Changes specific to Microsoft Visual Studio and its C89-compliant ways */
+#if _MSC_VER
+# define strtok_r strtok_s
+# define strtoull _strtoui64
+#if _MSC_VER < 1900
+# define snprintf _snprintf
+# define vsnprintf _vsnprintf
+#else
+#define STDC99
+#endif
+# define strcasecmp _stricmp
+# define strncasecmp _strnicmp
+# define fileno _fileno
+# define strdup _strdup
+# define access _access
+# define chdir _chdir
+# define getcwd _getcwd
+# define rmdir _rmdir
+# define unlink _unlink
+# define mkdir(n, m) _mkdir(n)
+
+/* ssize_t lives elsewhere */
+# include <BaseTsd.h>
+# define ssize_t SSIZE_T
+
+/* With msvc, inline is only available for C++. Otherwise we need to use __inline:
+ * http://msdn.microsoft.com/en-us/library/z8y1yy88.aspx */
+# if !defined(__cplusplus)
+# define inline __inline
+# endif
+
+/* Visual studio 2012 compiler (v17.00.XX) doesn't have round functions */
+# if _MSC_VER <= 1700
+# include <math.h>
+ static inline float roundf(float x)
+ {
+ return x >= 0.0f ? floorf(x + 0.5f) : ceilf(x - 0.5f);
+ }
+ static inline double round(double x)
+ {
+ return x >= 0.0 ? floor(x + 0.5) : ceil(x - 0.5);
+ }
+# endif
+
+#endif // _MSC_VER
+
+#endif
+
+/* Windows (msvc) does not support C99 designated initializers.
+ *
+ * Therefore, the following macro should be used. However, note that you'll
+ * need to be sure to keep your elements in order to avoid breaking Windows
+ * portability!
+ *
+ * http://stackoverflow.com/questions/5440611/how-to-rewrite-c-struct-designated-initializers-to-c89-resp-msvc-c-compiler
+ *
+ * Here's a sample regexep you could use in vim to clean these up in your code:
+ * @\(\s\+\)\(\.[a-zA-Z0-9_]\+\)\s*=\s*\([a-zA-Z0-9_]\+\)\(,\)\?@\1FIELD_INIT(\2,\3)\4@gc
+ */
+#if _MSC_VER
+# define FIELD_INIT(field, ...) __VA_ARGS__
+#else
+# define FIELD_INIT(field, ...) field = __VA_ARGS__
+#endif // _MSC_VER
+
+/*******************************************************************************
+ * Miscellaneous utility macros
+ ******************************************************************************/
+#define ARRAY_SIZE(n) (sizeof(n) / sizeof(n[0]))
+
+/**
+ * GCC 7+ warns on implicit fallthroughs in switch statements
+ * (-Wimplicit-fallthrough), which we use in a couple places for simplicity.
+ * The statement attribute syntax used to suppress this warning is not
+ * supported by anything else.
+ */
+#if __GNUC__ >= 7
+#define EXPLICIT_FALLTHROUGH __attribute__((fallthrough))
+#else
+#define EXPLICIT_FALLTHROUGH
+#endif // __GNUC__ >= 7
+
+#endif
diff --git a/Radio/HW/BladeRF/common/include/host_config.h.in b/Radio/HW/BladeRF/common/include/host_config.h.in
new file mode 100644
index 0000000..844946d
--- /dev/null
+++ b/Radio/HW/BladeRF/common/include/host_config.h.in
@@ -0,0 +1,333 @@
+/**
+ * @file host_config.h.in
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2013-2018 Nuand LLC.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef HOST_CONFIG_H__
+#define HOST_CONFIG_H__
+
+#cmakedefine01 BLADERF_OS_LINUX
+#cmakedefine01 BLADERF_OS_FREEBSD
+#cmakedefine01 BLADERF_OS_OSX
+#cmakedefine01 BLADERF_OS_WINDOWS
+#cmakedefine01 BLADERF_BIG_ENDIAN
+
+#if !(BLADERF_OS_LINUX || BLADERF_OS_OSX || BLADERF_OS_WINDOWS || BLADERF_OS_FREEBSD)
+# error "Build not configured for any supported operating systems"
+#endif
+
+#if 1 < (BLADERF_OS_LINUX + BLADERF_OS_OSX + BLADERF_OS_WINDOWS + BLADERF_OS_FREEBSD)
+#error "Build configured for multiple operating systems"
+#endif
+
+#cmakedefine01 HAVE_CLOCK_GETTIME
+
+/*******************************************************************************
+ * Endianness conversions
+ *
+ * HOST_TO_LE16 16-bit host endianness to little endian conversion
+ * LE16_TO_HOST 16-bit little endian to host endianness conversion
+ * HOST_TO_BE16 16-bit host endianness to big endian conversion
+ * BE16_TO_HOST 16-bit big endian to host endianness conversion
+ * HOST_TO_LE32 32-bit host endianness to little endian conversion
+ * LE32_TO_HOST 32-bit little endian to host endianness conversion
+ * HOST_TO_BE32 32-bit host endianness to big endian conversion
+ * BE32_TO_HOST 32-bit big endian to host endianness conversion
+ * HOST_TO_BE64 64-bit host endianness to big endian conversion
+ * BE64_TO_HOST 64-bit big endian to host endianness conversion
+ ******************************************************************************/
+
+/*-----------------------
+ * Linux
+ *---------------------*/
+#if BLADERF_OS_LINUX
+#include <endian.h>
+
+#define HOST_TO_LE16(val) htole16(val)
+#define LE16_TO_HOST(val) le16toh(val)
+#define HOST_TO_BE16(val) htobe16(val)
+#define BE16_TO_HOST(val) be16toh(val)
+
+#define HOST_TO_LE32(val) htole32(val)
+#define LE32_TO_HOST(val) le32toh(val)
+#define HOST_TO_BE32(val) htobe32(val)
+#define BE32_TO_HOST(val) be32toh(val)
+
+#define HOST_TO_LE64(val) htole64(val)
+#define LE64_TO_HOST(val) le64toh(val)
+#define HOST_TO_BE64(val) be64toh(val)
+#define BE64_TO_HOST(val) be64toh(val)
+
+/*-----------------------
+ * FREEBSD
+ *---------------------*/
+#elif BLADERF_OS_FREEBSD
+#include <sys/endian.h>
+
+#define HOST_TO_LE16(val) htole16(val)
+#define LE16_TO_HOST(val) le16toh(val)
+#define HOST_TO_BE16(val) htobe16(val)
+#define BE16_TO_HOST(val) be16toh(val)
+
+#define HOST_TO_LE32(val) htole32(val)
+#define LE32_TO_HOST(val) le32toh(val)
+#define HOST_TO_BE32(val) htobe32(val)
+#define BE32_TO_HOST(val) be32toh(val)
+
+#define HOST_TO_LE64(val) htole64(val)
+#define LE64_TO_HOST(val) le64toh(val)
+#define HOST_TO_BE64(val) be64toh(val)
+#define BE64_TO_HOST(val) be64toh(val)
+
+/*-----------------------
+ * Apple OSX
+ *---------------------*/
+#elif BLADERF_OS_OSX
+#include <libkern/OSByteOrder.h>
+
+#define HOST_TO_LE16(val) OSSwapHostToLittleInt16(val)
+#define LE16_TO_HOST(val) OSSwapLittleToHostInt16(val)
+#define HOST_TO_BE16(val) OSSwapHostToBigInt16(val)
+#define BE16_TO_HOST(val) OSSwapBigToHostInt16(val)
+
+#define HOST_TO_LE32(val) OSSwapHostToLittleInt32(val)
+#define LE32_TO_HOST(val) OSSwapLittleToHostInt32(val)
+#define HOST_TO_BE32(val) OSSwapHostToBigInt32(val)
+#define BE32_TO_HOST(val) OSSwapBigToHostInt32(val)
+
+#define HOST_TO_LE64(val) OSSwapHostToLittleInt64(val)
+#define LE64_TO_HOST(val) OSSwapLittleToHostInt64(val)
+#define HOST_TO_BE64(val) OSSwapHostToBigInt64(val)
+#define BE64_TO_HOST(val) OSSwapBigToHostInt64(val)
+
+/*-----------------------
+ * Windows
+ *---------------------*/
+#elif BLADERF_OS_WINDOWS
+#include <intrin.h>
+
+/* We'll assume little endian for Windows platforms:
+ * http://blogs.msdn.com/b/larryosterman/archive/2005/06/07/426334.aspx
+ */
+#define HOST_TO_LE16(val) (val)
+#define LE16_TO_HOST(val) (val)
+#define HOST_TO_BE16(val) _byteswap_ushort(val)
+#define BE16_TO_HOST(val) _byteswap_ushort(val)
+
+#define HOST_TO_LE32(val) (val)
+#define LE32_TO_HOST(val) (val)
+#define HOST_TO_BE32(val) _byteswap_ulong(val)
+#define BE32_TO_HOST(val) _byteswap_ulong(val)
+
+
+#define HOST_TO_LE64(val) (val)
+#define LE64_TO_HOST(val) (val)
+#define HOST_TO_BE64(val) _byteswap_uint64(val)
+#define BE64_TO_HOST(val) _byteswap_uint64(val)
+
+/*-----------------------
+ * Unsupported or bug
+ *---------------------*/
+#else
+#error "Encountered an OS that we do not have endian wrappers for?"
+#endif
+
+/*******************************************************************************
+ * Endianness conversions for constants
+ *
+ * We shouldn't be using the above items for assigning constants because the
+ * implementations may be functions [1].
+ *
+ * [1] https://sourceware.org/bugzilla/show_bug.cgi?id=17679#c7
+ ******************************************************************************/
+
+#define BLADERF_BYTESWAP16(x) ((((x) & 0xff00) >> 8) | (((x) & 0x00ff) << 8))
+
+#define BLADERF_BYTESWAP32(x) ((((x) & 0xff000000) >> 24) | \
+ (((x) & 0x00ff0000) >> 8) | \
+ (((x) & 0x0000ff00) << 8) | \
+ (((x) & 0x000000ff) << 24) )
+
+#define BLADERF_BYTESWAP64(x) ((((x) & 0xff00000000000000llu) >> 56) | \
+ (((x) & 0x00ff000000000000llu) >> 40) | \
+ (((x) & 0x0000ff0000000000llu) >> 24) | \
+ (((x) & 0x000000ff00000000llu) >> 8) | \
+ (((x) & 0x00000000ff000000llu) << 8) | \
+ (((x) & 0x0000000000ff0000llu) << 24) | \
+ (((x) & 0x000000000000ff00llu) << 40) | \
+ (((x) & 0x00000000000000ffllu) << 56) | \
+
+#if BLADERF_BIG_ENDIAN
+# define HOST_TO_LE16_CONST(x) (BLADERF_BYTESWAP16(x))
+# define LE16_TO_HOST_CONST(x) (BLADERF_BYTESWAP16(x))
+# define HOST_TO_BE16_CONST(x) (x)
+# define BE16_TO_HOST_CONST(x) (x)
+
+# define HOST_TO_LE32_CONST(x) (BLADERF_BYTESWAP32(x))
+# define LE32_TO_HOST_CONST(x) (BLADERF_BYTESWAP32(x))
+# define HOST_TO_BE32_CONST(x) (x)
+# define BE32_TO_HOST_CONST(x) (x)
+
+# define HOST_TO_LE64_CONST(x) (BLADERF_BYTESWAP64(x))
+# define LE64_TO_HOST_CONST(x) (BLADERF_BYTESWAP64(x))
+# define HOST_TO_BE64_CONST(x) (x)
+# define BE64_TO_HOST_CONST(x) (x)
+#else
+# define HOST_TO_LE16_CONST(x) (x)
+# define LE16_TO_HOST_CONST(x) (x)
+# define HOST_TO_BE16_CONST(x) (BLADERF_BYTESWAP16(x))
+# define BE16_TO_HOST_CONST(x) (BLADERF_BYTESWAP16(x))
+
+# define HOST_TO_LE32_CONST(x) (x)
+# define LE32_TO_HOST_CONST(x) (x)
+# define HOST_TO_BE32_CONST(x) (BLADERF_BYTESWAP32(x))
+# define BE32_TO_HOST_CONST(x) (BLADERF_BYTESWAP32(x))
+
+# define HOST_TO_LE64_CONST(x) (x)
+# define LE64_TO_HOST_CONST(x) (x)
+# define HOST_TO_BE64_CONST(x) (BLADERF_BYTESWAP64(x))
+# define BE64_TO_HOST_CONST(x) (BLADERF_BYTESWAP64(x))
+#endif
+
+/*******************************************************************************
+ * Miscellaneous Linux fixups
+ ******************************************************************************/
+#if BLADERF_OS_LINUX
+
+/* Added here just to keep this out of the other source files. We'll have
+ * a few Windows replacements for unistd.h items. */
+#include <unistd.h>
+#include <pwd.h>
+
+/*******************************************************************************
+ * Miscellaneous FREEBSD fixups
+ ******************************************************************************/
+#elif BLADERF_OS_FREEBSD
+
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/sysctl.h>
+
+/*******************************************************************************
+ * Miscellaneous OSX fixups
+ ******************************************************************************/
+#elif BLADERF_OS_OSX
+
+#include <unistd.h>
+#include <pwd.h>
+
+/*******************************************************************************
+ * Miscellaneous Windows fixups
+ ******************************************************************************/
+#elif BLADERF_OS_WINDOWS
+
+/* Rename a few functions and types */
+#include <windows.h>
+#include <io.h>
+#include <direct.h>
+#define usleep(x) Sleep((x+999)/1000)
+
+/* Changes specific to Microsoft Visual Studio and its C89-compliant ways */
+#if _MSC_VER
+# define strtok_r strtok_s
+# define strtoull _strtoui64
+#if _MSC_VER < 1900
+# define snprintf _snprintf
+# define vsnprintf _vsnprintf
+#else
+#define STDC99
+#endif
+# define strcasecmp _stricmp
+# define strncasecmp _strnicmp
+# define fileno _fileno
+# define strdup _strdup
+# define access _access
+# define chdir _chdir
+# define getcwd _getcwd
+# define rmdir _rmdir
+# define unlink _unlink
+# define mkdir(n, m) _mkdir(n)
+
+/* ssize_t lives elsewhere */
+# include <BaseTsd.h>
+# define ssize_t SSIZE_T
+
+/* With msvc, inline is only available for C++. Otherwise we need to use __inline:
+ * http://msdn.microsoft.com/en-us/library/z8y1yy88.aspx */
+# if !defined(__cplusplus)
+# define inline __inline
+# endif
+
+/* Visual studio 2012 compiler (v17.00.XX) doesn't have round functions */
+# if _MSC_VER <= 1700
+# include <math.h>
+ static inline float roundf(float x)
+ {
+ return x >= 0.0f ? floorf(x + 0.5f) : ceilf(x - 0.5f);
+ }
+ static inline double round(double x)
+ {
+ return x >= 0.0 ? floor(x + 0.5) : ceil(x - 0.5);
+ }
+# endif
+
+#endif // _MSC_VER
+
+#endif
+
+/* Windows (msvc) does not support C99 designated initializers.
+ *
+ * Therefore, the following macro should be used. However, note that you'll
+ * need to be sure to keep your elements in order to avoid breaking Windows
+ * portability!
+ *
+ * http://stackoverflow.com/questions/5440611/how-to-rewrite-c-struct-designated-initializers-to-c89-resp-msvc-c-compiler
+ *
+ * Here's a sample regexep you could use in vim to clean these up in your code:
+ * @\(\s\+\)\(\.[a-zA-Z0-9_]\+\)\s*=\s*\([a-zA-Z0-9_]\+\)\(,\)\?@\1FIELD_INIT(\2,\3)\4@gc
+ */
+#if _MSC_VER
+# define FIELD_INIT(field, ...) __VA_ARGS__
+#else
+# define FIELD_INIT(field, ...) field = __VA_ARGS__
+#endif // _MSC_VER
+
+/*******************************************************************************
+ * Miscellaneous utility macros
+ ******************************************************************************/
+#define ARRAY_SIZE(n) (sizeof(n) / sizeof(n[0]))
+
+/**
+ * GCC 7+ warns on implicit fallthroughs in switch statements
+ * (-Wimplicit-fallthrough), which we use in a couple places for simplicity.
+ * The statement attribute syntax used to suppress this warning is not
+ * supported by anything else.
+ */
+#if __GNUC__ >= 7
+#define EXPLICIT_FALLTHROUGH __attribute__((fallthrough))
+#else
+#define EXPLICIT_FALLTHROUGH
+#endif // __GNUC__ >= 7
+
+#endif
diff --git a/Radio/HW/BladeRF/common/include/iterators.h b/Radio/HW/BladeRF/common/include/iterators.h
new file mode 100644
index 0000000..65127a4
--- /dev/null
+++ b/Radio/HW/BladeRF/common/include/iterators.h
@@ -0,0 +1,59 @@
+/**
+ * @file iterators.h
+ *
+ * @brief Useful iterators for working with channels, etc
+ *
+ * Copyright (c) 2018 Nuand LLC.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * @brief Translate a bladerf_direction and channel number to a
+ * bladerf_channel
+ *
+ * @param _dir Direction
+ * @param _idx Channel number
+ *
+ * @return BLADERF_CHANNEL_TX(_idx) or BLADERF_CHANNEL_RX(_idx)
+ */
+#define CHANNEL_IDX(_dir, _idx) \
+ (BLADERF_TX == _dir) ? BLADERF_CHANNEL_TX(_idx) : BLADERF_CHANNEL_RX(_idx)
+
+/**
+ * @brief Iterate over all bladerf_directions
+ *
+ * @param _dir Direction
+ */
+#define FOR_EACH_DIRECTION(_dir) \
+ for (_dir = BLADERF_RX; _dir <= BLADERF_TX; ++_dir)
+
+/**
+ * @brief Iterate over all channels in a given direction
+ *
+ * @param _dir Direction
+ * @param _count Number of channels
+ * @param[out] _index Index variable (size_t)
+ * @param[out] _channel Channel variable (bladerf_channel)
+ *
+ * @return { description_of_the_return_value }
+ */
+#define FOR_EACH_CHANNEL(_dir, _count, _index, _channel) \
+ for (_index = 0, _channel = CHANNEL_IDX(_dir, _index); _index < _count; \
+ ++_index, _channel = CHANNEL_IDX(_dir, _index))
diff --git a/Radio/HW/BladeRF/common/include/log.h b/Radio/HW/BladeRF/common/include/log.h
new file mode 100644
index 0000000..0b635b5
--- /dev/null
+++ b/Radio/HW/BladeRF/common/include/log.h
@@ -0,0 +1,144 @@
+/**
+ * @file log.h
+ *
+ * @brief Simple log message
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2013 Nuand LLC.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef LOG_H__
+#define LOG_H__
+
+#include <stdio.h>
+#include <inttypes.h>
+
+#include "libbladeRF.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef SHORT_FILE_
+# define THIS_FILE SHORT_FILE_
+#else
+#if 0
+# warning "SHORT_FILE_ was not defined. Using __FILE__ instead."
+#endif
+# define THIS_FILE __FILE__
+#endif
+
+#define LOG_STRINGIFY__(x) #x
+#define LOG_STRINGIFY_(x) LOG_STRINGIFY__(x)
+
+#define LOG_EXPAND__(x) #x
+#define LOG_EXPAND_(x) LOG_EXPAND__(x)
+
+/**
+ * @defgroup LOG_MACROS Logging macros
+ * @{
+ */
+
+/** Logs a verbose message. Does not include function/line information */
+#define log_verbose(...) \
+ LOG_WRITE(BLADERF_LOG_LEVEL_VERBOSE, "[VERBOSE", __VA_ARGS__)
+
+/** Logs a debug message. Does not include function/line information */
+#define log_debug(...) \
+ LOG_WRITE(BLADERF_LOG_LEVEL_DEBUG, "[DEBUG", __VA_ARGS__)
+
+/** Logs an info message. Does not include function/line information*/
+#define log_info(...) \
+ LOG_WRITE(BLADERF_LOG_LEVEL_INFO, "[INFO", __VA_ARGS__)
+
+/** Logs a warning message. Includes function/line information */
+#define log_warning(...) \
+ LOG_WRITE(BLADERF_LOG_LEVEL_WARNING, "[WARNING", __VA_ARGS__)
+
+/** Logs an error message. Includes function/line information */
+#define log_error(...) \
+ LOG_WRITE(BLADERF_LOG_LEVEL_ERROR, "[ERROR", __VA_ARGS__)
+
+/** Logs a critical error message. Includes function/line information */
+#define log_critical(...) \
+ LOG_WRITE(BLADERF_LOG_LEVEL_CRITICAL, "[CRITICAL", __VA_ARGS__)
+
+/** @} */
+
+#ifdef LOG_INCLUDE_FILE_INFO
+# define LOG_WRITE(LEVEL, LEVEL_STRING, ...) \
+ do { log_write(LEVEL, LEVEL_STRING \
+ " @ " LOG_EXPAND_(THIS_FILE) \
+ ":" LOG_STRINGIFY_(__LINE__) "] " \
+ __VA_ARGS__); \
+ } while (0)
+#else
+# define LOG_WRITE(LEVEL, LEVEL_STRING, ...) \
+ do { log_write(LEVEL, LEVEL_STRING "] " __VA_ARGS__); } while (0)
+#endif
+
+/**
+ * Writes a message to the logger with the specified log level. This function
+ * should only be invoked indirectly through one of the log_*
+ * convenience macros above to ensure consistent formatting of log messages.
+ *
+ * @param level The severity level of the message
+ * @param format The printf-style format string
+ * @param ... Optional values for format specifier substitution
+ */
+#ifdef LOGGING_ENABLED
+void log_write(bladerf_log_level level, const char *format, ...);
+#else
+#define log_write(level, format, ...) do {} while(0)
+#endif
+
+
+/**
+ * Sets the filter level for displayed log messages. Messages that are at
+ * or above the specified log level will be written to the log output, while
+ * messages with a lower log level will be suppressed. This function returns
+ * the previous log level.
+ *
+ * @param level The new log level filter value
+ */
+#ifdef LOGGING_ENABLED
+void log_set_verbosity(bladerf_log_level level);
+#else
+#define log_set_verbosity(level) do {} while (0)
+#endif
+
+/**
+ * @brief Gets the current filter level for displayed log messages.
+ *
+ * @return bladerf_log_level
+ */
+#ifdef LOGGING_ENABLED
+bladerf_log_level log_get_verbosity();
+#else
+#define log_get_verbosity(...) BLADERF_LOG_LEVEL_SILENT
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/Radio/HW/BladeRF/common/include/logger_id.h b/Radio/HW/BladeRF/common/include/logger_id.h
new file mode 100644
index 0000000..f37da81
--- /dev/null
+++ b/Radio/HW/BladeRF/common/include/logger_id.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef LOGGER_ID_H_
+#define LOGGER_ID_H_
+
+#define LOGGER_ID_NONE 0
+#define LOGGER_ID_BLADERF_C 1
+#define LOGGER_ID_FLASH_C 2
+#define LOGGER_ID_FPGA_C 3
+#define LOGGER_ID_GPIF_C 4
+#define LOGGER_ID_LOGGER_C 5
+#define LOGGER_ID_RF_C 6
+#define LOGGER_ID_SPI_FLASH_LIB_C 7
+
+#ifdef LOGGER_ID_STRING
+static inline const char * logger_id_string(uint8_t file_id)
+{
+ switch (file_id) {
+ case LOGGER_ID_NONE:
+ return "<None>";
+ case LOGGER_ID_BLADERF_C:
+ return "bladeRF.c";
+ case LOGGER_ID_FLASH_C:
+ return "flash.c";
+ case LOGGER_ID_FPGA_C:
+ return "fpga.c";
+ case LOGGER_ID_GPIF_C:
+ return "gpif.c";
+ case LOGGER_ID_LOGGER_C:
+ return "logger.c";
+ case LOGGER_ID_RF_C:
+ return "rf.c";
+ case LOGGER_ID_SPI_FLASH_LIB_C:
+ return "spi_flash_lib.c";
+ default:
+ return "<Unknown>";
+ }
+}
+#endif
+
+#endif
diff --git a/Radio/HW/BladeRF/common/include/minmax.h b/Radio/HW/BladeRF/common/include/minmax.h
new file mode 100644
index 0000000..9195250
--- /dev/null
+++ b/Radio/HW/BladeRF/common/include/minmax.h
@@ -0,0 +1,65 @@
+/**
+ * @file minmax.h
+ *
+ * @brief These static inline routines are preferred over the use of macros,
+ * as they provide type safety.
+ */
+
+#ifndef MINMAX_H__
+#define MINMAX_H__
+
+#include <stdlib.h>
+#include <stdint.h>
+#include "host_config.h"
+
+static inline size_t min_sz(size_t x, size_t y)
+{
+ return x < y ? x : y;
+}
+
+static inline size_t max_sz(size_t x, size_t y)
+{
+ return x > y ? x : y;
+}
+
+static inline unsigned int uint_min(unsigned int x, unsigned int y)
+{
+ return x < y ? x : y;
+}
+
+static inline unsigned int uint_max(unsigned int x, unsigned int y)
+{
+ return x > y ? x : y;
+}
+
+static inline uint32_t u32_min(uint32_t x, uint32_t y)
+{
+ return x < y ? x : y;
+}
+
+static inline uint32_t u32_max(uint32_t x, uint32_t y)
+{
+ return x > y ? x : y;
+}
+
+static inline int64_t i64_min(int64_t x, int64_t y)
+{
+ return x < y ? x : y;
+}
+
+static inline uint64_t u64_min(uint64_t x, uint64_t y)
+{
+ return x < y ? x : y;
+}
+
+static inline int64_t i64_max(int64_t x, int64_t y)
+{
+ return x > y ? x : y;
+}
+
+static inline uint64_t u64_max(uint64_t x, uint64_t y)
+{
+ return x > y ? x : y;
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/common/include/osx/clock_gettime.h b/Radio/HW/BladeRF/common/include/osx/clock_gettime.h
new file mode 100644
index 0000000..f2ee0ce
--- /dev/null
+++ b/Radio/HW/BladeRF/common/include/osx/clock_gettime.h
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2013-2017 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef OSX_CLOCK_GETTIME_H_
+#define OSX_CLOCK_GETTIME_H_
+
+#include "host_config.h"
+#include <time.h>
+
+#if HAVE_CLOCK_GETTIME == 0
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+#ifndef __MACH__
+# error "This file is intended for use with OSX systems only."
+#endif // __MACH__
+
+typedef int clockid_t;
+#define CLOCK_REALTIME 0
+
+int clock_gettime(clockid_t clk_id, struct timespec *tp);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif // __cplusplus
+
+#endif // !HAVE_CLOCK_GETTIME
+
+#endif // OSX_CLOCK_GETTIME_H
diff --git a/Radio/HW/BladeRF/common/include/parse.h b/Radio/HW/BladeRF/common/include/parse.h
new file mode 100644
index 0000000..a397e6c
--- /dev/null
+++ b/Radio/HW/BladeRF/common/include/parse.h
@@ -0,0 +1,112 @@
+/**
+ * @file parse.h
+ *
+ * @brief String and file parsing functions
+ *
+ * Copyright (c) 2017 Nuand LLC.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef PARSE_H_
+#define PARSE_H_
+
+#include "libbladeRF.h"
+#include <stdio.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Convert ASCII string into arguments
+ *
+ * @param[in] line Command line string
+ * @param[in] comment_char Everything after this charater is discarded
+ * @param[out] argv A pointer to a double pointer of successfully
+ * extracted arguments
+ *
+ * @return -2 on unterminated quote, -1 on error, otherwise number of arguments
+ */
+int str2args(const char *line, char comment_char, char ***argv);
+
+/**
+ * Free arguments returned by str2args()
+ *
+ * @param[in] argc Number of arguments
+ * @param[in] argv Pointer to table of pointers to arguments
+ */
+void free_args(int argc, char **argv);
+
+struct config_options {
+ char *key;
+ char *value;
+ int lineno;
+};
+
+/**
+ * Convert a bladeRF options file to an array of strings
+ *
+ * @param[in] dev Pointer to bladerf device
+ * @param[in] buf String buffer containing entire file
+ * @param[in] buf_sz String buffer containing entire file
+ * @param[out] opts A pointer to an array of strings containing options for
+ * this device
+ *
+ * @return number of options on success, BLADERF_ERR_* values on other failures
+ */
+int str2options(struct bladerf *dev,
+ const char *buf,
+ size_t buf_sz,
+ struct config_options **opts);
+
+/**
+ * Free an array of previously returned options
+ *
+ * @param[in] optv Pointer to array of config_options
+ * @param[in] optc Number of config_options
+ */
+void free_opts(struct config_options *optv, int optc);
+
+/**
+ * Convert comma-delimited string into integer arguments
+ *
+ * @param[in] line Line to split
+ * @param[out] args Pointer to double-pointer of successfully extracted
+ * integers
+ *
+ * @see free_csv2int()
+ *
+ * @return -1 on error, otherwise number of arguments
+ */
+int csv2int(const char *line, int ***args);
+
+/**
+ * Free array of previously-returned csv2int arguments
+ *
+ * @param[in] argc Number of arguments
+ * @param[in] args Pointer to array of pointers to ints
+ */
+void free_csv2int(int argc, int **args);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/Radio/HW/BladeRF/common/include/range.h b/Radio/HW/BladeRF/common/include/range.h
new file mode 100644
index 0000000..c15a005
--- /dev/null
+++ b/Radio/HW/BladeRF/common/include/range.h
@@ -0,0 +1,54 @@
+/* This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2018 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef RANGE_H_
+#define RANGE_H_
+
+#if !defined(BLADERF_NIOS_BUILD) && !defined(BLADERF_NIOS_PC_SIMULATION)
+#include <libbladeRF.h>
+#else
+#include "libbladeRF_nios_compat.h"
+#endif
+
+#include "bladerf2_common.h"
+
+#ifdef BLADERF_NIOS_BUILD
+// don't do scaling on the Nios, since multiplication and division suck
+#define __scale(r, v) (v)
+#define __unscale(r, v) (v)
+#else
+#define __scale(r, v) ((float)(v) / (r)->scale)
+#define __unscale(r, v) ((float)(v) * (r)->scale)
+#endif // BLADERF_NIOS_BUILD
+
+#define __scale_int(r, v) (__round_int(__scale(r, v)))
+#define __scale_int64(r, v) (__round_int64(__scale(r, v)))
+
+#define __unscale_int(r, v) (__round_int(__unscale(r, v)))
+#define __unscale_int64(r, v) (__round_int64(__unscale(r, v)))
+
+bool is_within_range(struct bladerf_range const *range, int64_t value);
+int64_t clamp_to_range(struct bladerf_range const *range, int64_t value);
+
+#endif // RANGE_H_
diff --git a/Radio/HW/BladeRF/common/include/rel_assert.h b/Radio/HW/BladeRF/common/include/rel_assert.h
new file mode 100644
index 0000000..4727710
--- /dev/null
+++ b/Radio/HW/BladeRF/common/include/rel_assert.h
@@ -0,0 +1,14 @@
+#ifndef _REL_ASSERT_H_
+#define _REL_ASSERT_H_
+
+#ifdef NDEBUG
+#define NDEBUG_UNDEF
+#endif
+#undef NDEBUG
+#include <assert.h>
+#ifdef NDEBUG_UNDEF
+#define NDEBUG
+#undef NDEBUG_UNDEF
+#endif
+
+#endif /* _REL_ASSERT_H_ */ \ No newline at end of file
diff --git a/Radio/HW/BladeRF/common/include/sha256.h b/Radio/HW/BladeRF/common/include/sha256.h
new file mode 100644
index 0000000..7517ecd
--- /dev/null
+++ b/Radio/HW/BladeRF/common/include/sha256.h
@@ -0,0 +1,61 @@
+/*-
+ * Copyright 2005 Colin Percival
+ * Copyright 2013 Daniel Gröber
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _SHA256_H_
+#define _SHA256_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SHA256_DIGEST_SIZE 32
+
+typedef struct SHA256Context {
+ uint32_t state[8];
+ uint32_t count[2];
+ unsigned char buf[64];
+} SHA256_CTX;
+
+void SHA256_Init(SHA256_CTX *);
+void SHA256_Update(SHA256_CTX *, const void *, size_t);
+void SHA256_Final(unsigned char [32], SHA256_CTX *);
+/* char *SHA256_End(SHA256_CTX *, char *); */
+/* char *SHA256_File(const char *, char *); */
+/* char *SHA256_FileChunk(const char *, char *, off_t, off_t); */
+/* char *SHA256_Data(const void *, unsigned int, char *); */
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* !_SHA256_H_ */
diff --git a/Radio/HW/BladeRF/common/include/str_queue.h b/Radio/HW/BladeRF/common/include/str_queue.h
new file mode 100644
index 0000000..9ed1769
--- /dev/null
+++ b/Radio/HW/BladeRF/common/include/str_queue.h
@@ -0,0 +1,93 @@
+/**
+ * @file str_queue.h
+ *
+ * @brief Simple string queue
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2014 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef STR_QUEUE_H_
+#define STR_QUEUE_H_
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct str_queue_entry;
+
+struct str_queue {
+ struct str_queue_entry *head;
+ struct str_queue_entry *tail;
+};
+
+/**
+ * Allocate and initialize a string queue
+ *
+ * @param[in] q Queue to initialize
+ */
+void str_queue_init(struct str_queue *q);
+
+/**
+ * Deallocate all resources used by the specified string queue
+ *
+ * @param[in] q Queue to deinitialize
+ */
+void str_queue_deinit(struct str_queue *q);
+
+/** Deinitialization simply clears out the queue - same operation */
+#define str_queue_clear str_queue_deinit
+
+/**
+ * Add a string to the end of the provided string queue
+ *
+ * @param q String queue to append to
+ * @param cmd_str String to append. This string will be copied
+ * to a heap-allocated buffer.
+ *
+ * @return 0 on success, -1 on memory allocation failure
+ */
+int str_queue_enq(struct str_queue *q, const char *str);
+
+/**
+ * Remove a string from the front of the specified string queue.
+ *
+ * @param[in] q Queue to remove from
+ *
+ * @return Heap-allocated string removed from queue, or NULL if the queue is
+ * empty. The caller is responsible for free()-ing the returned string.
+ */
+char * str_queue_deq(struct str_queue *q);
+
+/**
+ * @return true if the queue is empty
+ */
+bool str_queue_empty(struct str_queue *q);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+
+#endif
diff --git a/Radio/HW/BladeRF/common/include/thread.h b/Radio/HW/BladeRF/common/include/thread.h
new file mode 100644
index 0000000..855a24b
--- /dev/null
+++ b/Radio/HW/BladeRF/common/include/thread.h
@@ -0,0 +1,74 @@
+/**
+ * @file thread.h
+ *
+ * @brief Threading portability and debug wrappers
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2014 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef BLADERF_THREAD_H_
+#define BLADERF_THREAD_H_
+
+/* Currently, only pthreads is supported. In the future, native windows threads
+ * may be used; one of the objectives of this file is to ease that transistion.
+ */
+#include <pthread.h>
+#include "rel_assert.h"
+
+#define MUTEX pthread_mutex_t
+
+#ifdef ENABLE_LOCK_CHECKS
+# define MUTEX_INIT(m) do { \
+ int status; \
+ pthread_mutexattr_t mutex_attr; \
+ status = pthread_mutexattr_init(&mutex_attr); \
+ assert(status == 0 && "Mutex attr init failure"); \
+ status = pthread_mutexattr_settype(&mutex_attr, \
+ PTHREAD_MUTEX_ERRORCHECK); \
+ assert(status == 0 && "Mutex attr setype failure"); \
+ status = pthread_mutex_init(m, &mutex_attr); \
+ assert(status == 0 && "Mutex init failure"); \
+ } while (0)
+
+# define MUTEX_LOCK(m) do { \
+ int status = pthread_mutex_lock(m); \
+ assert(status == 0 && "Mutex lock failure");\
+ } while (0)
+
+# define MUTEX_UNLOCK(m) do { \
+ int status = pthread_mutex_unlock(m); \
+ assert(status == 0 && "Mutex unlock failure");\
+ } while (0)
+
+# define MUTEX_DESTROY(m) do { \
+ int status = pthread_mutex_destroy(m); \
+ assert(status == 0 && "Mutex destroy failure");\
+ } while (0)
+#else
+# define MUTEX_INIT(m) pthread_mutex_init(m, NULL)
+# define MUTEX_LOCK(m) pthread_mutex_lock(m)
+# define MUTEX_UNLOCK(m) pthread_mutex_unlock(m)
+# define MUTEX_DESTROY(m) pthread_mutex_destroy(m)
+#endif
+
+#endif
diff --git a/Radio/HW/BladeRF/common/src/conversions.c b/Radio/HW/BladeRF/common/src/conversions.c
new file mode 100644
index 0000000..ad08520
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/conversions.c
@@ -0,0 +1,730 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2017-2018 Nuand LLC.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "conversions.h"
+
+int str2version(const char *str, struct bladerf_version *version)
+{
+ unsigned long tmp;
+ const char *start = str;
+ char *end;
+
+ /* Major version */
+ errno = 0;
+ tmp = strtoul(start, &end, 10);
+ if (errno != 0 || tmp > UINT16_MAX || end == start || *end != '.') {
+ return -1;
+ }
+ version->major = (uint16_t)tmp;
+
+ /* Minor version */
+ if (end[0] == '\0' || end[1] == '\0') {
+ return -1;
+ }
+ errno = 0;
+ start = &end[1];
+ tmp = strtoul(start, &end, 10);
+ if (errno != 0 || tmp > UINT16_MAX || end == start || *end != '.') {
+ return -1;
+ }
+ version->minor = (uint16_t)tmp;
+
+ /* Patch version */
+ if (end[0] == '\0' || end[1] == '\0') {
+ return -1;
+ }
+ errno = 0;
+ start = &end[1];
+ tmp = strtoul(start, &end, 10);
+ if (errno != 0 || tmp > UINT16_MAX || end == start ||
+ (*end != '-' && *end != '\0')) {
+ return -1;
+ }
+ version->patch = (uint16_t)tmp;
+
+ version->describe = str;
+
+ return 0;
+}
+
+const char *devspeed2str(bladerf_dev_speed speed)
+{
+ switch (speed) {
+ case BLADERF_DEVICE_SPEED_HIGH:
+ /* Yeah, the USB IF actually spelled it "Hi" instead of "High".
+ * I know. It hurts me too. */
+ return "Hi-Speed";
+
+ case BLADERF_DEVICE_SPEED_SUPER:
+ /* ...and no hyphen :( */
+ return "SuperSpeed";
+
+ default:
+ return "Unknown";
+ }
+}
+
+bladerf_log_level str2loglevel(const char *str, bool *ok)
+{
+ bladerf_log_level level = BLADERF_LOG_LEVEL_ERROR;
+ bool valid = true;
+
+ if (!strcasecmp(str, "critical")) {
+ level = BLADERF_LOG_LEVEL_CRITICAL;
+ } else if (!strcasecmp(str, "error")) {
+ level = BLADERF_LOG_LEVEL_ERROR;
+ } else if (!strcasecmp(str, "warning")) {
+ level = BLADERF_LOG_LEVEL_WARNING;
+ } else if (!strcasecmp(str, "info")) {
+ level = BLADERF_LOG_LEVEL_INFO;
+ } else if (!strcasecmp(str, "debug")) {
+ level = BLADERF_LOG_LEVEL_DEBUG;
+ } else if (!strcasecmp(str, "verbose")) {
+ level = BLADERF_LOG_LEVEL_VERBOSE;
+ } else {
+ valid = false;
+ }
+
+ *ok = valid;
+ return level;
+}
+
+const char *module2str(bladerf_module m)
+{
+ switch (m) {
+ case BLADERF_MODULE_RX:
+ return "RX";
+ case BLADERF_MODULE_TX:
+ return "TX";
+ default:
+ return "Unknown";
+ }
+}
+
+bladerf_module str2module(const char *str)
+{
+ if (!strcasecmp(str, "RX")) {
+ return BLADERF_MODULE_RX;
+ } else if (!strcasecmp(str, "TX")) {
+ return BLADERF_MODULE_TX;
+ } else {
+ return BLADERF_MODULE_INVALID;
+ }
+}
+
+const char *channel2str(bladerf_channel ch)
+{
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ return "RX1";
+ case BLADERF_CHANNEL_TX(0):
+ return "TX1";
+ case BLADERF_CHANNEL_RX(1):
+ return "RX2";
+ case BLADERF_CHANNEL_TX(1):
+ return "TX2";
+ default:
+ return "Unknown";
+ }
+}
+
+bladerf_channel str2channel(char const *str)
+{
+ bladerf_channel rv;
+
+ if (strcasecmp(str, "rx") == 0 || strcasecmp(str, "rx1") == 0) {
+ rv = BLADERF_CHANNEL_RX(0);
+ } else if (strcasecmp(str, "rx2") == 0) {
+ rv = BLADERF_CHANNEL_RX(1);
+ } else if (strcasecmp(str, "tx") == 0 || strcasecmp(str, "tx1") == 0) {
+ rv = BLADERF_CHANNEL_TX(0);
+ } else if (strcasecmp(str, "tx2") == 0) {
+ rv = BLADERF_CHANNEL_TX(1);
+ } else {
+ rv = BLADERF_CHANNEL_INVALID;
+ }
+
+ return rv;
+}
+
+bladerf_direction channel2direction (bladerf_channel ch) {
+ if (ch == BLADERF_CHANNEL_RX(0) || ch == BLADERF_CHANNEL_RX(1)) {
+ return BLADERF_RX;
+ } else {
+ return BLADERF_TX;
+ }
+}
+
+const char *direction2str(bladerf_direction dir)
+{
+ switch (dir) {
+ case BLADERF_RX:
+ return "RX";
+ case BLADERF_TX:
+ return "TX";
+ default:
+ return "Unknown";
+ }
+}
+
+const char *trigger2str(bladerf_trigger_signal trigger)
+{
+ switch (trigger) {
+ case BLADERF_TRIGGER_J71_4:
+ return "J71-4";
+
+ case BLADERF_TRIGGER_J51_1:
+ return "J51-1";
+
+ case BLADERF_TRIGGER_MINI_EXP_1:
+ return "MiniExp-1";
+
+ case BLADERF_TRIGGER_USER_0:
+ return "User-0";
+
+ case BLADERF_TRIGGER_USER_1:
+ return "User-1";
+
+ case BLADERF_TRIGGER_USER_2:
+ return "User-2";
+
+ case BLADERF_TRIGGER_USER_3:
+ return "User-3";
+
+ case BLADERF_TRIGGER_USER_4:
+ return "User-4";
+
+ case BLADERF_TRIGGER_USER_5:
+ return "User-5";
+
+ case BLADERF_TRIGGER_USER_6:
+ return "User-6";
+
+ case BLADERF_TRIGGER_USER_7:
+ return "User-7";
+
+ default:
+ return "Unknown";
+ }
+}
+
+bladerf_trigger_signal str2trigger(const char *str)
+{
+ if (!strcasecmp("J71-4", str)) {
+ return BLADERF_TRIGGER_J71_4;
+ } else if (!strcasecmp("J51-1", str)) {
+ return BLADERF_TRIGGER_J51_1;
+ } else if (!strcasecmp("Miniexp-1", str)) {
+ return BLADERF_TRIGGER_MINI_EXP_1;
+ } else if (!strcasecmp("User-0", str)) {
+ return BLADERF_TRIGGER_USER_0;
+ } else if (!strcasecmp("User-1", str)) {
+ return BLADERF_TRIGGER_USER_1;
+ } else if (!strcasecmp("User-2", str)) {
+ return BLADERF_TRIGGER_USER_2;
+ } else if (!strcasecmp("User-3", str)) {
+ return BLADERF_TRIGGER_USER_3;
+ } else if (!strcasecmp("User-4", str)) {
+ return BLADERF_TRIGGER_USER_4;
+ } else if (!strcasecmp("User-5", str)) {
+ return BLADERF_TRIGGER_USER_5;
+ } else if (!strcasecmp("User-6", str)) {
+ return BLADERF_TRIGGER_USER_6;
+ } else if (!strcasecmp("User-7", str)) {
+ return BLADERF_TRIGGER_USER_7;
+ } else {
+ return BLADERF_TRIGGER_INVALID;
+ }
+}
+
+const char *triggerrole2str(bladerf_trigger_role role)
+{
+ switch (role) {
+ case BLADERF_TRIGGER_ROLE_MASTER:
+ return "Master";
+ case BLADERF_TRIGGER_ROLE_SLAVE:
+ return "Slave";
+ case BLADERF_TRIGGER_ROLE_DISABLED:
+ return "Disabled";
+ default:
+ return "Unknown";
+ }
+}
+
+bladerf_trigger_role str2triggerrole(const char *str)
+{
+ if (!strcasecmp("Master", str)) {
+ return BLADERF_TRIGGER_ROLE_MASTER;
+ } else if (!strcasecmp("Slave", str)) {
+ return BLADERF_TRIGGER_ROLE_SLAVE;
+ } else if (!strcasecmp("Disabled", str) || !strcasecmp("Off", str)) {
+ return BLADERF_TRIGGER_ROLE_DISABLED;
+ } else {
+ return BLADERF_TRIGGER_ROLE_INVALID;
+ }
+}
+
+int str2loopback(const char *str, bladerf_loopback *loopback)
+{
+ int status = 0;
+
+ if (!strcasecmp("firmware", str)) {
+ *loopback = BLADERF_LB_FIRMWARE;
+ } else if (!strcasecmp("bb_txlpf_rxvga2", str)) {
+ *loopback = BLADERF_LB_BB_TXLPF_RXVGA2;
+ } else if (!strcasecmp("bb_txlpf_rxlpf", str)) {
+ *loopback = BLADERF_LB_BB_TXLPF_RXLPF;
+ } else if (!strcasecmp("bb_txvga1_rxvga2", str)) {
+ *loopback = BLADERF_LB_BB_TXVGA1_RXVGA2;
+ } else if (!strcasecmp("bb_txvga1_rxlpf", str)) {
+ *loopback = BLADERF_LB_BB_TXVGA1_RXLPF;
+ } else if (!strcasecmp("rf_lna1", str)) {
+ *loopback = BLADERF_LB_RF_LNA1;
+ } else if (!strcasecmp("rf_lna2", str)) {
+ *loopback = BLADERF_LB_RF_LNA2;
+ } else if (!strcasecmp("rf_lna3", str)) {
+ *loopback = BLADERF_LB_RF_LNA3;
+ } else if (!strcasecmp("rfic_bist", str)) {
+ *loopback = BLADERF_LB_RFIC_BIST;
+ } else if (!strcasecmp("none", str)) {
+ *loopback = BLADERF_LB_NONE;
+ } else {
+ status = -1;
+ }
+
+ return status;
+}
+
+char const *loopback2str(bladerf_loopback loopback)
+{
+ switch (loopback) {
+ case BLADERF_LB_BB_TXLPF_RXVGA2:
+ return "bb_txlpf_rxvga2";
+
+ case BLADERF_LB_BB_TXLPF_RXLPF:
+ return "bb_txlpf_rxlpf";
+
+ case BLADERF_LB_BB_TXVGA1_RXVGA2:
+ return "bb_txvga1_rxvga2";
+
+ case BLADERF_LB_BB_TXVGA1_RXLPF:
+ return "bb_txvga1_rxlpf";
+
+ case BLADERF_LB_RF_LNA1:
+ return "rf_lna1";
+
+ case BLADERF_LB_RF_LNA2:
+ return "rf_lna2";
+
+ case BLADERF_LB_RF_LNA3:
+ return "rf_lna3";
+
+ case BLADERF_LB_FIRMWARE:
+ return "firmware";
+
+ case BLADERF_LB_NONE:
+ return "none";
+
+ case BLADERF_LB_RFIC_BIST:
+ return "rfic_bist";
+
+ default:
+ return "unknown";
+ }
+}
+
+int str2lnagain(const char *str, bladerf_lna_gain *gain)
+{
+ *gain = BLADERF_LNA_GAIN_MAX;
+
+ if (!strcasecmp("max", str) || !strcasecmp("BLADERF_LNA_GAIN_MAX", str)) {
+ *gain = BLADERF_LNA_GAIN_MAX;
+ return 0;
+ } else if (!strcasecmp("mid", str) ||
+ !strcasecmp("BLADERF_LNA_GAIN_MID", str)) {
+ *gain = BLADERF_LNA_GAIN_MID;
+ return 0;
+ } else if (!strcasecmp("bypass", str) ||
+ !strcasecmp("BLADERF_LNA_GAIN_BYPASS", str)) {
+ *gain = BLADERF_LNA_GAIN_BYPASS;
+ return 0;
+ } else {
+ *gain = BLADERF_LNA_GAIN_UNKNOWN;
+ return -1;
+ }
+}
+
+char const *tuningmode2str(bladerf_tuning_mode mode)
+{
+ switch (mode) {
+ case BLADERF_TUNING_MODE_HOST:
+ return "Host";
+ case BLADERF_TUNING_MODE_FPGA:
+ return "FPGA";
+ default:
+ return "Unknown";
+ }
+}
+
+const char *backend_description(bladerf_backend b)
+{
+ switch (b) {
+ case BLADERF_BACKEND_ANY:
+ return "Any";
+
+ case BLADERF_BACKEND_LINUX:
+ return "Linux kernel driver";
+
+ case BLADERF_BACKEND_LIBUSB:
+ return "libusb";
+
+ case BLADERF_BACKEND_CYPRESS:
+ return "Cypress driver";
+
+ case BLADERF_BACKEND_DUMMY:
+ return "Dummy";
+
+ default:
+ return "Unknown";
+ }
+}
+
+void sc16q11_to_float(const int16_t *in, float *out, unsigned int n)
+{
+ unsigned int i;
+
+ for (i = 0; i < (2 * n); i += 2) {
+ out[i] = (float)in[i] * (1.0f / 2048.0f);
+ out[i + 1] = (float)in[i + 1] * (1.0f / 2048.0f);
+ }
+}
+
+void float_to_sc16q11(const float *in, int16_t *out, unsigned int n)
+{
+ unsigned int i;
+
+ for (i = 0; i < (2 * n); i += 2) {
+ out[i] = (int16_t)(in[i] * 2048.0f);
+ out[i + 1] = (int16_t)(in[i + 1] * 2048.0f);
+ }
+}
+
+bladerf_cal_module str_to_bladerf_cal_module(const char *str)
+{
+ bladerf_cal_module module = BLADERF_DC_CAL_INVALID;
+
+ if (!strcasecmp(str, "lpf_tuning") || !strcasecmp(str, "lpftuning") ||
+ !strcasecmp(str, "tuning")) {
+ module = BLADERF_DC_CAL_LPF_TUNING;
+ } else if (!strcasecmp(str, "tx_lpf") || !strcasecmp(str, "txlpf")) {
+ module = BLADERF_DC_CAL_TX_LPF;
+ } else if (!strcasecmp(str, "rx_lpf") || !strcasecmp(str, "rxlpf")) {
+ module = BLADERF_DC_CAL_RX_LPF;
+ } else if (!strcasecmp(str, "rx_vga2") || !strcasecmp(str, "rxvga2")) {
+ module = BLADERF_DC_CAL_RXVGA2;
+ }
+
+ return module;
+}
+
+const char *smb_mode_to_str(bladerf_smb_mode mode)
+{
+ switch (mode) {
+ case BLADERF_SMB_MODE_DISABLED:
+ return "Disabled";
+
+ case BLADERF_SMB_MODE_OUTPUT:
+ return "Output";
+
+ case BLADERF_SMB_MODE_INPUT:
+ return "Input";
+
+ case BLADERF_SMB_MODE_UNAVAILBLE:
+ return "Unavailable";
+
+ default:
+ return "Unknown";
+ }
+};
+
+bladerf_smb_mode str_to_smb_mode(const char *str)
+{
+ if (!strcasecmp(str, "disabled") || !strcasecmp(str, "off")) {
+ return BLADERF_SMB_MODE_DISABLED;
+ } else if (!strcasecmp(str, "output")) {
+ return BLADERF_SMB_MODE_OUTPUT;
+ } else if (!strcasecmp(str, "input")) {
+ return BLADERF_SMB_MODE_INPUT;
+ } else if (!strcasecmp(str, "unavailable")) {
+ return BLADERF_SMB_MODE_UNAVAILBLE;
+ } else {
+ return BLADERF_SMB_MODE_INVALID;
+ }
+}
+
+bladerf_tuning_mode str_to_tuning_mode(const char *str)
+{
+ if (!strcasecmp(str, "fpga")) {
+ return BLADERF_TUNING_MODE_FPGA;
+ } else if (!strcasecmp(str, "host")) {
+ return BLADERF_TUNING_MODE_HOST;
+ } else {
+ return BLADERF_TUNING_MODE_INVALID;
+ }
+}
+
+unsigned int str2uint(const char *str,
+ unsigned int min,
+ unsigned int max,
+ bool *ok)
+{
+ unsigned int ret;
+ char *optr;
+
+ errno = 0;
+ ret = strtoul(str, &optr, 0);
+
+ if (errno == ERANGE || (errno != 0 && ret == 0)) {
+ *ok = false;
+ return 0;
+ }
+
+ if (str == optr) {
+ *ok = false;
+ return 0;
+ }
+
+ if (ret >= min && ret <= max) {
+ *ok = true;
+ return (unsigned int)ret;
+ }
+
+ *ok = false;
+ return 0;
+}
+
+int str2int(const char *str, int min, int max, bool *ok)
+{
+ long int ret;
+ char *optr;
+
+ errno = 0;
+ ret = strtol(str, &optr, 0);
+
+ if ((errno == ERANGE && (ret == LONG_MAX || ret == LONG_MIN)) ||
+ (errno != 0 && ret == 0)) {
+ *ok = false;
+ return 0;
+ }
+
+ if (str == optr) {
+ *ok = false;
+ return 0;
+ }
+
+ if (ret >= min && ret <= max) {
+ *ok = true;
+ return (int)ret;
+ }
+
+ *ok = false;
+ return 0;
+}
+
+uint64_t str2uint64(const char *str, uint64_t min, uint64_t max, bool *ok)
+{
+ uint64_t ret;
+ char *optr;
+
+ errno = 0;
+ ret = (uint64_t)strtod(str, &optr);
+
+ if ((errno == ERANGE && ret == ULONG_MAX) || (errno != 0 && ret == 0)) {
+ *ok = false;
+ return 0;
+ }
+
+ if (str == optr) {
+ *ok = false;
+ return 0;
+ }
+
+ if (ret >= min && ret <= max) {
+ *ok = true;
+ return ret;
+ }
+
+ *ok = false;
+ return 0;
+}
+
+double str2double(const char *str, double min, double max, bool *ok)
+{
+ double ret;
+ char *optr;
+
+ errno = 0;
+ ret = strtod(str, &optr);
+
+ if (errno == ERANGE || (errno != 0 && ret == 0)) {
+ *ok = false;
+ return NAN;
+ }
+
+ if (str == optr) {
+ *ok = false;
+ return NAN;
+ }
+
+ if (ret >= min && ret <= max) {
+ *ok = true;
+ return ret;
+ }
+
+ *ok = false;
+ return NAN;
+}
+
+static uint64_t suffix_multiplier(const char *str,
+ const struct numeric_suffix *suffixes,
+ const size_t num_suff,
+ bool *ok)
+{
+ unsigned i;
+
+ *ok = true;
+
+ if (!strlen(str))
+ return 1;
+
+ for (i = 0; i < num_suff; i++) {
+ if (!strcasecmp(suffixes[i].suffix, str)) {
+ return suffixes[i].multiplier;
+ }
+ }
+
+ *ok = false;
+ return 0;
+}
+
+unsigned int str2uint_suffix(const char *str,
+ unsigned int min,
+ unsigned int max,
+ const struct numeric_suffix *suffixes,
+ const size_t num_suff,
+ bool *ok)
+{
+ uint64_t mult;
+ unsigned int rv;
+ double val;
+ char *optr;
+
+ errno = 0;
+ val = strtod(str, &optr);
+
+ if (errno == ERANGE || (errno != 0 && val == 0)) {
+ *ok = false;
+ return 0;
+ }
+
+ if (str == optr) {
+ *ok = false;
+ return 0;
+ }
+
+ mult = suffix_multiplier(optr, suffixes, num_suff, ok);
+ if (!*ok)
+ return false;
+
+ rv = (unsigned int)(val * mult);
+
+ if (rv >= min && rv <= max) {
+ return rv;
+ }
+
+ *ok = false;
+ return 0;
+}
+
+uint64_t str2uint64_suffix(const char *str,
+ uint64_t min,
+ uint64_t max,
+ const struct numeric_suffix *suffixes,
+ const size_t num_suff,
+ bool *ok)
+{
+ uint64_t mult, rv;
+ long double val;
+ char *optr;
+
+ errno = 0;
+ val = strtold(str, &optr);
+
+ if (errno == ERANGE && (errno != 0 && val == 0)) {
+ *ok = false;
+ return 0;
+ }
+
+ if (str == optr) {
+ *ok = false;
+ return 0;
+ }
+
+ mult = suffix_multiplier(optr, suffixes, num_suff, ok);
+ if (!*ok)
+ return false;
+
+ rv = (uint64_t)(val * mult);
+ if (rv >= min && rv <= max) {
+ return rv;
+ }
+
+ *ok = false;
+ return 0;
+}
+
+int str2bool(const char *str, bool *val)
+{
+ unsigned int i;
+
+ char *str_true[] = { "true", "t", "one", "1", "enable", "en", "on" };
+ char *str_false[] = { "false", "f", "zero", "0", "disable", "dis", "off" };
+
+ for (i = 0; i < ARRAY_SIZE(str_true); i++) {
+ if (!strcasecmp(str_true[i], str)) {
+ *val = true;
+ return 0;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(str_false); i++) {
+ if (!strcasecmp(str_false[i], str)) {
+ *val = false;
+ return 0;
+ }
+ }
+
+ return BLADERF_ERR_INVAL;
+}
diff --git a/Radio/HW/BladeRF/common/src/dc_calibration.c b/Radio/HW/BladeRF/common/src/dc_calibration.c
new file mode 100644
index 0000000..c746c3c
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/dc_calibration.c
@@ -0,0 +1,1745 @@
+/**
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2015 Nuand LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+
+ * This program 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 Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <limits.h>
+
+#define _USE_MATH_DEFINES /* Required for MSVC */
+#include <math.h>
+
+#include <libbladeRF.h>
+
+#include "dc_calibration.h"
+#include "conversions.h"
+
+struct complexf {
+ float i;
+ float q;
+};
+
+struct gain_mode {
+ bladerf_lna_gain lna_gain;
+ int rxvga1, rxvga2;
+};
+/*******************************************************************************
+ * Debug items
+ ******************************************************************************/
+
+/* Enable this to print diagnostic and debug information */
+//#define ENABLE_DC_CALIBRATION_DEBUG
+//#define ENABLE_DC_CALIBRATION_VERBOSE
+
+#ifndef PR_DBG
+# ifdef ENABLE_DC_CALIBRATION_DEBUG
+# define PR_DBG(...) fprintf(stderr, " " __VA_ARGS__)
+# else
+# define PR_DBG(...) do {} while (0)
+# endif
+#endif
+
+#ifndef PR_VERBOSE
+# ifdef ENABLE_DC_CALIBRATION_VERBOSE
+# define PR_VERBOSE(...) fprintf(stderr, " " __VA_ARGS__)
+# else
+# define PR_VERBOSE(...) do {} while (0)
+# endif
+#endif
+
+/*******************************************************************************
+ * Debug routines for saving samples
+ ******************************************************************************/
+
+//#define ENABLE_SAVE_SC16Q11
+#ifdef ENABLE_SAVE_SC16Q11
+static void save_sc16q11(const char *name, int16_t *samples, unsigned int count)
+{
+ FILE *out = fopen(name, "wb");
+ if (!out) {
+ return;
+ }
+
+ fwrite(samples, 2 * sizeof(samples[0]), count, out);
+ fclose(out);
+}
+#else
+# define save_sc16q11(name, samples, count) do {} while (0)
+#endif
+
+//#define ENABLE_SAVE_COMPLEXF
+#ifdef ENABLE_SAVE_COMPLEXF
+static void save_complexf(const char *name, struct complexf *samples,
+ unsigned int count)
+{
+ unsigned int n;
+ FILE *out = fopen(name, "wb");
+ if (!out) {
+ return;
+ }
+
+ for (n = 0; n < count; n++) {
+ fwrite(&samples[n].i, sizeof(samples[n].i), 1, out);
+ fwrite(&samples[n].q, sizeof(samples[n].q), 1, out);
+ }
+
+ fclose(out);
+}
+#else
+# define save_complexf(name, samples, count) do {} while (0)
+#endif
+
+
+/*******************************************************************************
+ * LMS6002D DC offset calibration
+ ******************************************************************************/
+
+/* We've found that running samples through the LMS6 tends to be required
+ * for the TX LPF calibration to converge */
+static inline int tx_lpf_dummy_tx(struct bladerf *dev)
+{
+ int status;
+ int retval = 0;
+ struct bladerf_metadata meta;
+ int16_t zero_sample[] = { 0, 0 };
+
+ bladerf_loopback loopback_backup;
+ struct bladerf_rational_rate sample_rate_backup;
+
+ memset(&meta, 0, sizeof(meta));
+
+ status = bladerf_get_loopback(dev, &loopback_backup);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_rational_sample_rate(dev, BLADERF_MODULE_TX,
+ &sample_rate_backup);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_loopback(dev, BLADERF_LB_BB_TXVGA1_RXVGA2);
+ if (status != 0) {
+ goto out;
+ }
+
+ status = bladerf_set_sample_rate(dev, BLADERF_MODULE_TX, 3000000, NULL);
+ if (status != 0) {
+ goto out;
+ }
+
+ status = bladerf_sync_config(dev, BLADERF_MODULE_TX,
+ BLADERF_FORMAT_SC16_Q11_META,
+ 64, 16384, 16, 1000);
+ if (status != 0) {
+ goto out;
+ }
+
+ status = bladerf_enable_module(dev, BLADERF_MODULE_TX, true);
+ if (status != 0) {
+ goto out;
+ }
+
+ meta.flags = BLADERF_META_FLAG_TX_BURST_START |
+ BLADERF_META_FLAG_TX_BURST_END |
+ BLADERF_META_FLAG_TX_NOW;
+
+ status = bladerf_sync_tx(dev, zero_sample, 1, &meta, 2000);
+ if (status != 0) {
+ goto out;
+ }
+
+out:
+ status = bladerf_enable_module(dev, BLADERF_MODULE_TX, false);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_rational_sample_rate(dev, BLADERF_MODULE_TX,
+ &sample_rate_backup, NULL);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_loopback(dev, loopback_backup);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ return retval;
+}
+
+static int cal_tx_lpf(struct bladerf *dev)
+{
+ int status;
+
+ status = tx_lpf_dummy_tx(dev);
+ if (status == 0) {
+ status = bladerf_calibrate_dc(dev, BLADERF_DC_CAL_TX_LPF);
+ }
+
+ return status;
+}
+
+int dc_calibration_lms6(struct bladerf *dev, const char *module_str)
+{
+ int status;
+ bladerf_cal_module module;
+
+ if (!strcasecmp(module_str, "all")) {
+ status = bladerf_calibrate_dc(dev, BLADERF_DC_CAL_LPF_TUNING);
+ if (status != 0) {
+ return status;
+ }
+
+ status = cal_tx_lpf(dev);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_calibrate_dc(dev, BLADERF_DC_CAL_RX_LPF);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_calibrate_dc(dev, BLADERF_DC_CAL_RXVGA2);
+ } else {
+ module = str_to_bladerf_cal_module(module_str);
+ if (module == BLADERF_DC_CAL_INVALID) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (module == BLADERF_DC_CAL_TX_LPF) {
+ status = cal_tx_lpf(dev);
+ } else {
+ status = bladerf_calibrate_dc(dev, module);
+ }
+ }
+
+ return status;
+}
+
+
+
+/*******************************************************************************
+ * Shared utility routines
+ ******************************************************************************/
+
+/* Round float to int16_t */
+static inline int16_t float_to_int16(float val)
+{
+ if ((val - 0.5) <= INT16_MIN) {
+ return INT16_MIN;
+ }
+ if ((val + 0.5) >= INT16_MAX) {
+ return INT16_MAX;
+ }
+ return val >= 0 ? (int16_t)(val + 0.5) : (int16_t)(val - 0.5);
+}
+
+/* Convert ms to samples */
+#define MS_TO_SAMPLES(ms_, rate_) (\
+ (unsigned int) (ms_ * ((uint64_t) rate_) / 1000) \
+)
+
+/* RX samples, retrying if the machine is struggling to keep up. */
+static int rx_samples(struct bladerf *dev, int16_t *samples,
+ unsigned int count, uint64_t *ts, uint64_t ts_inc)
+{
+ int status = 0;
+ struct bladerf_metadata meta;
+ int retry = 0;
+ const int max_retries = 10;
+ bool overrun = true;
+
+ memset(&meta, 0, sizeof(meta));
+ meta.timestamp = *ts;
+
+ while (status == 0 && overrun && retry < max_retries) {
+ meta.timestamp = *ts;
+ status = bladerf_sync_rx(dev, samples, count, &meta, 2000);
+
+ if (status == BLADERF_ERR_TIME_PAST) {
+ status = bladerf_get_timestamp(dev, BLADERF_MODULE_RX, ts);
+ if (status != 0) {
+ return status;
+ } else {
+ *ts += 20 * ts_inc;
+ retry++;
+ status = 0;
+ }
+ } else if (status == 0) {
+ overrun = (meta.flags & BLADERF_META_STATUS_OVERRUN) != 0;
+ if (overrun) {
+ *ts += count + ts_inc;
+ retry++;
+ }
+ } else {
+ return status;
+ }
+ }
+
+ if (retry >= max_retries) {
+ status = BLADERF_ERR_IO;
+ } else if (status == 0) {
+ *ts += count + ts_inc;
+ }
+
+ return status;
+}
+
+
+
+/*******************************************************************************
+ * RX DC offset calibration
+ ******************************************************************************/
+
+#define RX_CAL_RATE (3000000)
+#define RX_CAL_BW (1500000)
+#define RX_CAL_TS_INC (MS_TO_SAMPLES(15, RX_CAL_RATE))
+#define RX_CAL_COUNT (MS_TO_SAMPLES(5, RX_CAL_RATE))
+
+#define RX_CAL_MAX_SWEEP_LEN (2 * 2048 / 32) /* -2048 : 32 : 2048 */
+
+struct rx_cal {
+ struct bladerf *dev;
+
+ int16_t *samples;
+ unsigned int num_samples;
+
+ int16_t *corr_sweep;
+
+ uint64_t ts;
+
+ uint64_t tx_freq;
+};
+
+struct rx_cal_backup {
+ struct bladerf_rational_rate rational_sample_rate;
+ unsigned int bandwidth;
+ uint64_t tx_freq;
+};
+
+static int get_rx_cal_backup(struct bladerf *dev, struct rx_cal_backup *b)
+{
+ int status;
+
+ status = bladerf_get_rational_sample_rate(dev, BLADERF_MODULE_RX,
+ &b->rational_sample_rate);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_bandwidth(dev, BLADERF_MODULE_RX, &b->bandwidth);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_frequency(dev, BLADERF_MODULE_TX, &b->tx_freq);
+ if (status != 0) {
+ return status;
+ }
+
+ return status;
+}
+
+static int set_rx_cal_backup(struct bladerf *dev, struct rx_cal_backup *b)
+{
+ int status;
+ int retval = 0;
+
+ status = bladerf_set_rational_sample_rate(dev, BLADERF_MODULE_RX,
+ &b->rational_sample_rate, NULL);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_bandwidth(dev, BLADERF_MODULE_RX, b->bandwidth, NULL);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_frequency(dev, BLADERF_MODULE_TX, b->tx_freq);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ return retval;
+}
+
+/* Ensure TX >= 1 MHz away from the RX frequency to avoid any potential
+ * artifacts from the PLLs interfering with one another */
+static int rx_cal_update_frequency(struct rx_cal *cal, uint64_t rx_freq)
+{
+ int status = 0;
+ uint64_t f_diff;
+
+ if (rx_freq < cal->tx_freq) {
+ f_diff = cal->tx_freq - rx_freq;
+ } else {
+ f_diff = rx_freq - cal->tx_freq;
+ }
+
+ PR_DBG("Set F_RX = %u\n", rx_freq);
+ PR_DBG("F_diff(RX, TX) = %u\n", f_diff);
+
+ if (f_diff < 1000000) {
+ if (rx_freq >= (BLADERF_FREQUENCY_MIN + 1000000)) {
+ cal->tx_freq = rx_freq - 1000000;
+ } else {
+ cal->tx_freq = rx_freq + 1000000;
+ }
+
+ status = bladerf_set_frequency(cal->dev, BLADERF_MODULE_TX,
+ cal->tx_freq);
+ if (status != 0) {
+ return status;
+ }
+
+ PR_DBG("Adjusted TX frequency: %u\n", cal->tx_freq);
+ }
+
+ status = bladerf_set_frequency(cal->dev, BLADERF_MODULE_RX, rx_freq);
+ if (status != 0) {
+ return status;
+ }
+
+ cal->ts += RX_CAL_TS_INC;
+
+ return status;
+}
+
+static inline void sample_mean(int16_t *samples, size_t count,
+ float *mean_i, float *mean_q)
+{
+ int64_t accum_i = 0;
+ int64_t accum_q = 0;
+
+ size_t n;
+
+
+ if (count == 0) {
+ assert(!"Invalid count (0) provided to sample_mean()");
+ *mean_i = 0;
+ *mean_q = 0;
+ return;
+ }
+
+ for (n = 0; n < (2 * count); n += 2) {
+ accum_i += samples[n];
+ accum_q += samples[n + 1];
+ }
+
+ *mean_i = ((float) accum_i) / count;
+ *mean_q = ((float) accum_q) / count;
+}
+
+static inline int set_rx_dc_corr(struct bladerf *dev, int16_t i, int16_t q)
+{
+ int status;
+
+ status = bladerf_set_correction(dev, BLADERF_MODULE_RX,
+ BLADERF_CORR_LMS_DCOFF_I, i);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_correction(dev, BLADERF_MODULE_RX,
+ BLADERF_CORR_LMS_DCOFF_Q, q);
+ return status;
+}
+
+/* Get the mean for one of the coarse estimate points. If it seems that this
+ * value might be (or close) causing us to clamp, adjust it and retry */
+static int rx_cal_coarse_means(struct rx_cal *cal, int16_t *corr_value,
+ float *mean_i, float *mean_q)
+{
+ int status;
+ const int16_t mean_limit_high = 2000;
+ const int16_t mean_limit_low = -mean_limit_high;
+ const int16_t corr_limit = 128;
+ bool retry = false;
+
+ do {
+ status = set_rx_dc_corr(cal->dev, *corr_value, *corr_value);
+ if (status != 0) {
+ return status;
+ }
+
+ status = rx_samples(cal->dev, cal->samples, cal->num_samples,
+ &cal->ts, RX_CAL_TS_INC);
+ if (status != 0) {
+ return status;
+ }
+
+ sample_mean(cal->samples, cal->num_samples, mean_i, mean_q);
+
+ if (*mean_i > mean_limit_high || *mean_q > mean_limit_high ||
+ *mean_i < mean_limit_low || *mean_q < mean_limit_low ) {
+
+ if (*corr_value < 0) {
+ retry = (*corr_value <= -corr_limit);
+ } else {
+ retry = (*corr_value >= corr_limit);
+ }
+
+ if (retry) {
+ PR_DBG("Coarse estimate point Corr=%4d yields extreme means: "
+ "(%4f, %4f). Retrying...\n",
+ *corr_value, *mean_i, *mean_q);
+
+ *corr_value = *corr_value / 2;
+ }
+ } else {
+ retry = false;
+ }
+ } while (retry);
+
+ if (retry) {
+ PR_DBG("Non-ideal values are being used.\n");
+ }
+
+ return 0;
+}
+
+/* Estimate the DC correction values that yield zero DC offset via a linear
+ * approximation */
+static int rx_cal_coarse_estimate(struct rx_cal *cal,
+ int16_t *i_est, int16_t *q_est)
+{
+ int status;
+ int16_t x1 = -2048;
+ int16_t x2 = 2048;
+ float y1i, y1q, y2i, y2q;
+ float mi, mq;
+ float bi, bq;
+ float i_guess, q_guess;
+
+ status = rx_cal_coarse_means(cal, &x1, &y1i, &y1q);
+ if (status != 0) {
+ *i_est = 0;
+ *q_est = 0;
+ return status;
+ }
+
+ PR_VERBOSE("Means for x1=%d: y1i=%f, y1q=%f\n", x1, y1i, y1q);
+
+ status = rx_cal_coarse_means(cal, &x2, &y2i, &y2q);
+ if (status != 0) {
+ *i_est = 0;
+ *q_est = 0;
+ return status;
+ }
+
+ PR_VERBOSE("Means for x2: y2i=%f, y2q=%f\n", y2i, y2q);
+
+ mi = (y2i - y1i) / (x2 - x1);
+ mq = (y2q - y1q) / (x2 - x1);
+
+ bi = y1i - mi * x1;
+ bq = y1q - mq * x1;
+
+ PR_VERBOSE("mi=%f, bi=%f, mq=%f, bq=%f\n", mi, bi, mq, bq);
+
+ i_guess = -bi/mi + 0.5f;
+ if (i_guess < -2048) {
+ i_guess = -2048;
+ } else if (i_guess > 2048) {
+ i_guess = 2048;
+ }
+
+ q_guess = -bq/mq + 0.5f;
+ if (q_guess < -2048) {
+ q_guess = -2048;
+ } else if (q_guess > 2048) {
+ q_guess = 2048;
+ }
+
+ *i_est = (int16_t) i_guess;
+ *q_est = (int16_t) q_guess;
+
+ PR_DBG("Coarse estimate: I=%d, Q=%d\n", *i_est, *q_est);
+
+ return 0;
+}
+
+static void init_rx_cal_sweep(int16_t *corr, unsigned int *sweep_len,
+ int16_t i_est, int16_t q_est)
+{
+ unsigned int actual_len = 0;
+ unsigned int i;
+
+ int16_t sweep_min, sweep_max, sweep_val;
+
+ /* LMS6002D RX DC calibrations have a limited range. libbladeRF throws away
+ * the lower 5 bits. */
+ const int16_t sweep_inc = 32;
+
+ const int16_t min_est = (i_est < q_est) ? i_est : q_est;
+ const int16_t max_est = (i_est > q_est) ? i_est : q_est;
+
+ sweep_min = min_est - 12 * 32;
+ if (sweep_min < -2048) {
+ sweep_min = -2048;
+ }
+
+ sweep_max = max_est + 12 * 32;
+ if (sweep_max > 2048) {
+ sweep_max = 2048;
+ }
+
+ /* Given that these lower bits are thrown away, it can be confusing to
+ * see that values change in their LSBs that don't matter. Therefore,
+ * we'll adjust to muliples of sweep_inc */
+ sweep_min = (sweep_min / 32) * 32;
+ sweep_max = (sweep_max / 32) * 32;
+
+
+ PR_DBG("Sweeping [%d : %d : %d]\n", sweep_min, sweep_inc, sweep_max);
+
+ sweep_val = sweep_min;
+ for (i = 0; sweep_val < sweep_max && i < RX_CAL_MAX_SWEEP_LEN; i++) {
+ corr[i] = sweep_val;
+ sweep_val += sweep_inc;
+ actual_len++;
+ }
+
+ *sweep_len = actual_len;
+}
+
+static int save_gains(struct rx_cal *cal, struct gain_mode *gain) {
+ int status;
+
+ status = bladerf_get_lna_gain(cal->dev, &gain->lna_gain);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_rxvga1(cal->dev, &gain->rxvga1);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_rxvga2(cal->dev, &gain->rxvga2);
+ if (status != 0) {
+ return status;
+ }
+
+ return status;
+}
+
+static int load_gains(struct rx_cal *cal, struct gain_mode *gain) {
+ int status;
+
+ status = bladerf_set_lna_gain(cal->dev, gain->lna_gain);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_rxvga1(cal->dev, gain->rxvga1);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_rxvga2(cal->dev, gain->rxvga2);
+ if (status != 0) {
+ return status;
+ }
+
+ return status;
+}
+
+static int rx_cal_dc_off(struct rx_cal *cal, struct gain_mode *gains,
+ int16_t *dc_i, int16_t *dc_q)
+{
+ int status = BLADERF_ERR_UNEXPECTED;
+
+ float mean_i, mean_q;
+
+ status = load_gains(cal, gains);
+ if (status != 0) {
+ return status;
+ }
+
+ status = rx_samples(cal->dev, cal->samples, cal->num_samples,
+ &cal->ts, RX_CAL_TS_INC);
+ if (status != 0) {
+ return status;
+ }
+
+ sample_mean(cal->samples, cal->num_samples, &mean_i, &mean_q);
+ *dc_i = float_to_int16(mean_i);
+ *dc_q = float_to_int16(mean_q);
+
+ return 0;
+}
+
+static int rx_cal_sweep(struct rx_cal *cal,
+ int16_t *corr, unsigned int sweep_len,
+ int16_t *result_i, int16_t *result_q,
+ float *error_i, float *error_q)
+{
+ int status = BLADERF_ERR_UNEXPECTED;
+ unsigned int n;
+
+ int16_t min_corr_i = 0;
+ int16_t min_corr_q = 0;
+
+ float mean_i, mean_q;
+ float min_val_i, min_val_q;
+
+ min_val_i = min_val_q = 2048;
+
+ for (n = 0; n < sweep_len; n++) {
+ status = set_rx_dc_corr(cal->dev, corr[n], corr[n]);
+ if (status != 0) {
+ return status;
+ }
+
+ status = rx_samples(cal->dev, cal->samples, cal->num_samples,
+ &cal->ts, RX_CAL_TS_INC);
+ if (status != 0) {
+ return status;
+ }
+
+ sample_mean(cal->samples, cal->num_samples, &mean_i, &mean_q);
+
+ PR_VERBOSE(" Corr=%4d, Mean_I=%4.2f, Mean_Q=%4.2f\n",
+ corr[n], mean_i, mean_q);
+
+ /* Not using fabs() to avoid adding a -lm dependency */
+ if (mean_i < 0) {
+ mean_i = -mean_i;
+ }
+
+ if (mean_q < 0) {
+ mean_q = -mean_q;
+ }
+
+ if (mean_i < min_val_i) {
+ min_val_i = mean_i;
+ min_corr_i = corr[n];
+ }
+
+ if (mean_q < min_val_q) {
+ min_val_q = mean_q;
+ min_corr_q = corr[n];
+ }
+ }
+
+ *result_i = min_corr_i;
+ *result_q = min_corr_q;
+ *error_i = min_val_i;
+ *error_q = min_val_q;
+
+ return 0;
+}
+
+static int perform_rx_cal(struct rx_cal *cal, struct dc_calibration_params *p)
+{
+ int status;
+ int16_t i_est, q_est;
+ unsigned int sweep_len = RX_CAL_MAX_SWEEP_LEN;
+ struct gain_mode saved_gains;
+
+ struct gain_mode agc_gains[] = {
+ { .lna_gain = BLADERF_LNA_GAIN_MAX, .rxvga1 = 30, .rxvga2 = 15 }, /* AGC Max Gain */
+ { .lna_gain = BLADERF_LNA_GAIN_MID, .rxvga1 = 30, .rxvga2 = 0 }, /* AGC Mid Gain */
+ { .lna_gain = BLADERF_LNA_GAIN_MID, .rxvga1 = 12, .rxvga2 = 0 } /* AGC Min Gain */
+ };
+
+ status = rx_cal_update_frequency(cal, p->frequency);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Get an initial guess at our correction values */
+ status = rx_cal_coarse_estimate(cal, &i_est, &q_est);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Perform a finer sweep of correction values */
+ init_rx_cal_sweep(cal->corr_sweep, &sweep_len, i_est, q_est);
+
+ /* Advance our timestmap just to account for any time we may have lost */
+ cal->ts += RX_CAL_TS_INC;
+
+ status = rx_cal_sweep(cal, cal->corr_sweep, sweep_len,
+ &p->corr_i, &p->corr_q,
+ &p->error_i, &p->error_q);
+
+ if (status != 0) {
+ return status;
+ }
+
+ /* Apply the nominal correction values */
+ status = set_rx_dc_corr(cal->dev, p->corr_i, p->corr_q);
+ if (status != 0) {
+ return status;
+ }
+
+ bladerf_fpga_size fpga_size;
+ status = bladerf_get_fpga_size(cal->dev, &fpga_size);
+ if (status != 0) {
+ return status;
+ }
+
+ if (fpga_size != BLADERF_FPGA_40KLE &&
+ fpga_size != BLADERF_FPGA_115KLE) {
+ return 0;
+ }
+
+ /* Measure DC correction for AGC */
+ status = save_gains(cal, &saved_gains);
+ if (status != 0) {
+ return status;
+ }
+
+ status = rx_cal_dc_off(cal, &agc_gains[2], &p->min_dc_i, &p->min_dc_q);
+ if (status != 0) {
+ return status;
+ }
+
+ status = rx_cal_dc_off(cal, &agc_gains[1], &p->mid_dc_i, &p->mid_dc_q);
+ if (status != 0) {
+ return status;
+ }
+
+ status = rx_cal_dc_off(cal, &agc_gains[0], &p->max_dc_i, &p->max_dc_q);
+ if (status != 0) {
+ return status;
+ }
+
+ status = load_gains(cal, &saved_gains);
+
+ return status;
+}
+
+static int rx_cal_init_state(struct bladerf *dev,
+ const struct rx_cal_backup *backup,
+ struct rx_cal *state)
+{
+ int status;
+
+ state->dev = dev;
+
+ state->num_samples = RX_CAL_COUNT;
+
+ state->samples = malloc(2 * sizeof(state->samples[0]) * RX_CAL_COUNT);
+ if (state->samples == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ state->corr_sweep = malloc(sizeof(state->corr_sweep[0]) * RX_CAL_MAX_SWEEP_LEN);
+ if (state->corr_sweep == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ state->tx_freq = backup->tx_freq;
+
+ status = bladerf_get_timestamp(dev, BLADERF_MODULE_RX, &state->ts);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Schedule first RX well into the future */
+ state->ts += 20 * RX_CAL_TS_INC;
+
+ return status;
+}
+
+static int rx_cal_init(struct bladerf *dev)
+{
+ int status;
+
+ status = bladerf_set_sample_rate(dev, BLADERF_MODULE_RX, RX_CAL_RATE, NULL);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_bandwidth(dev, BLADERF_MODULE_RX, RX_CAL_BW, NULL);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_sync_config(dev, BLADERF_MODULE_RX,
+ BLADERF_FORMAT_SC16_Q11_META,
+ 64, 16384, 16, 1000);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_enable_module(dev, BLADERF_MODULE_RX, true);
+ if (status != 0) {
+ return status;
+ }
+
+ return status;
+}
+
+int dc_calibration_rx(struct bladerf *dev,
+ struct dc_calibration_params *params,
+ size_t params_count, bool print_status)
+{
+ int status = 0;
+ int retval = 0;
+ struct rx_cal state;
+ struct rx_cal_backup backup;
+ size_t i;
+
+ memset(&state, 0, sizeof(state));
+
+ status = get_rx_cal_backup(dev, &backup);
+ if (status != 0) {
+ return status;
+ }
+
+ status = rx_cal_init(dev);
+ if (status != 0) {
+ goto out;
+ }
+
+ status = rx_cal_init_state(dev, &backup, &state);
+ if (status != 0) {
+ goto out;
+ }
+
+ for (i = 0; i < params_count && status == 0; i++) {
+ status = perform_rx_cal(&state, &params[i]);
+
+ if (status == 0 && print_status) {
+# ifdef DEBUG_DC_CALIBRATION
+ const char sol = '\n';
+ const char eol = '\n';
+# else
+ const char sol = '\r';
+ const char eol = '\0';
+# endif
+ printf("%cCalibrated @ %10" PRIu64 " Hz: I=%4d (Error: %4.2f), "
+ "Q=%4d (Error: %4.2f) ",
+ sol,
+ params[i].frequency,
+ params[i].corr_i, params[i].error_i,
+ params[i].corr_q, params[i].error_q);
+ printf("DC-LUT: Max (I=%3d, Q=%3d) Mid (I=%3d, Q=%3d)"
+ " Min (I=%3d, Q=%3d)%c",
+ params[i].max_dc_i, params[i].max_dc_q, params[i].mid_dc_i, params[i].mid_dc_q,
+ params[i].min_dc_i, params[i].min_dc_q, eol);
+ fflush(stdout);
+ }
+ }
+
+ if (print_status) {
+ putchar('\n');
+ }
+
+out:
+ free(state.samples);
+ free(state.corr_sweep);
+
+ retval = status;
+
+ status = bladerf_enable_module(dev, BLADERF_MODULE_RX, false);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = set_rx_cal_backup(dev, &backup);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ return retval;
+}
+
+
+
+/*******************************************************************************
+ * TX DC offset calibration
+ ******************************************************************************/
+
+#define TX_CAL_RATE (4000000)
+
+#define TX_CAL_RX_BW (3000000)
+#define TX_CAL_RX_LNA (BLADERF_LNA_GAIN_MAX)
+#define TX_CAL_RX_VGA1 (25)
+#define TX_CAL_RX_VGA2 (0)
+
+#define TX_CAL_TX_BW (1500000)
+
+#define TX_CAL_TS_INC (MS_TO_SAMPLES(15, TX_CAL_RATE))
+#define TX_CAL_COUNT (MS_TO_SAMPLES(5, TX_CAL_RATE))
+
+#define TX_CAL_CORR_SWEEP_LEN (4096 / 16) /* -2048:16:2048 */
+
+#define TX_CAL_DEFAULT_LB (BLADERF_LB_RF_LNA1)
+
+struct tx_cal_backup {
+ uint64_t rx_freq;
+ struct bladerf_rational_rate rx_sample_rate;
+ unsigned int rx_bandwidth;
+
+ bladerf_lna_gain rx_lna;
+ int rx_vga1;
+ int rx_vga2;
+
+ struct bladerf_rational_rate tx_sample_rate;
+ unsigned int tx_bandwidth;
+
+ bladerf_loopback loopback;
+};
+
+struct tx_cal {
+ struct bladerf *dev;
+ int16_t *samples; /* Raw samples */
+ unsigned int num_samples; /* Number of raw samples */
+ struct complexf *filt; /* Filter state */
+ struct complexf *filt_out; /* Filter output */
+ struct complexf *post_mix; /* Post-filter, mixed to baseband */
+ int16_t *sweep; /* Correction sweep */
+ float *mag; /* Magnitude results from sweep */
+ uint64_t ts; /* Timestamp */
+ bladerf_loopback loopback; /* Current loopback mode */
+ bool rx_low; /* RX tuned lower than TX */
+};
+
+/* Filter used to isolate contribution of TX LO leakage in received
+ * signal. 15th order Equiripple FIR with Fs=4e6, Fpass=1, Fstop=1e6
+ */
+static const float tx_cal_filt[] = {
+ 0.000327949366768f, 0.002460188536582f, 0.009842382390924f,
+ 0.027274728394777f, 0.057835200476419f, 0.098632713294830f,
+ 0.139062540460741f, 0.164562494987592f, 0.164562494987592f,
+ 0.139062540460741f, 0.098632713294830f, 0.057835200476419f,
+ 0.027274728394777f, 0.009842382390924f, 0.002460188536582f,
+ 0.000327949366768f,
+};
+
+static const unsigned int tx_cal_filt_num_taps =
+ (sizeof(tx_cal_filt) / sizeof(tx_cal_filt[0]));
+
+static inline int set_tx_dc_corr(struct bladerf *dev, int16_t i, int16_t q)
+{
+ int status;
+
+ status = bladerf_set_correction(dev, BLADERF_MODULE_TX,
+ BLADERF_CORR_LMS_DCOFF_I, i);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_correction(dev, BLADERF_MODULE_TX,
+ BLADERF_CORR_LMS_DCOFF_Q, q);
+ return status;
+}
+
+static int get_tx_cal_backup(struct bladerf *dev, struct tx_cal_backup *b)
+{
+ int status;
+
+ status = bladerf_get_frequency(dev, BLADERF_MODULE_RX, &b->rx_freq);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_rational_sample_rate(dev, BLADERF_MODULE_RX,
+ &b->rx_sample_rate);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_bandwidth(dev, BLADERF_MODULE_RX, &b->rx_bandwidth);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_lna_gain(dev, &b->rx_lna);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_rxvga1(dev, &b->rx_vga1);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_rxvga2(dev, &b->rx_vga2);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_rational_sample_rate(dev, BLADERF_MODULE_TX,
+ &b->tx_sample_rate);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_loopback(dev, &b->loopback);
+
+ return status;
+}
+
+static int set_tx_cal_backup(struct bladerf *dev, struct tx_cal_backup *b)
+{
+ int status;
+ int retval = 0;
+
+ status = bladerf_set_loopback(dev, b->loopback);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_frequency(dev, BLADERF_MODULE_RX, b->rx_freq);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_rational_sample_rate(dev, BLADERF_MODULE_RX,
+ &b->rx_sample_rate, NULL);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_bandwidth(dev, BLADERF_MODULE_RX,
+ b->rx_bandwidth, NULL);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_lna_gain(dev, b->rx_lna);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_rxvga1(dev, b->rx_vga1);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_rxvga2(dev, b->rx_vga2);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_rational_sample_rate(dev, BLADERF_MODULE_TX,
+ &b->tx_sample_rate, NULL);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ return retval;
+}
+
+static int tx_cal_update_frequency(struct tx_cal *state, uint64_t freq)
+{
+ int status;
+ bladerf_loopback lb;
+ uint64_t rx_freq;
+
+ status = bladerf_set_frequency(state->dev, BLADERF_MODULE_TX, freq);
+ if (status != 0) {
+ return status;
+ }
+
+ rx_freq = freq - 1000000;
+ if (rx_freq < BLADERF_FREQUENCY_MIN) {
+ rx_freq = freq + 1000000;
+ state->rx_low = false;
+ } else {
+ state->rx_low = true;
+ }
+
+ status = bladerf_set_frequency(state->dev, BLADERF_MODULE_RX, rx_freq);
+ if (status != 0) {
+ return status;
+ }
+
+ if (freq < 1500000000) {
+ lb = BLADERF_LB_RF_LNA1;
+ PR_DBG("Switching to RF LNA1 loopback.\n");
+ } else {
+ lb = BLADERF_LB_RF_LNA2;
+ PR_DBG("Switching to RF LNA2 loopback.\n");
+ }
+
+ if (state->loopback != lb) {
+ status = bladerf_set_loopback(state->dev, lb);
+ if (status == 0) {
+ state->loopback = lb;
+ }
+ }
+
+ return status;
+}
+
+static int apply_tx_cal_settings(struct bladerf *dev)
+{
+ int status;
+
+ status = bladerf_set_sample_rate(dev, BLADERF_MODULE_RX, TX_CAL_RATE, NULL);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_bandwidth(dev, BLADERF_MODULE_RX, TX_CAL_RX_BW, NULL);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_lna_gain(dev, TX_CAL_RX_LNA);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_rxvga1(dev, TX_CAL_RX_VGA1);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_rxvga2(dev, TX_CAL_RX_VGA2);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_sample_rate(dev, BLADERF_MODULE_TX, TX_CAL_RATE, NULL);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_loopback(dev, TX_CAL_DEFAULT_LB);
+ if (status != 0) {
+ return status;
+ }
+
+ return status;
+}
+
+/* We just need to flush some zeros through the system to hole the DAC at
+ * 0+0j and remain there while letting it underrun. This alleviates the
+ * need to worry about continuously TX'ing zeros. */
+static int tx_cal_tx_init(struct bladerf *dev)
+{
+ int status;
+ int16_t zero_sample[] = { 0, 0 };
+ struct bladerf_metadata meta;
+
+ memset(&meta, 0, sizeof(meta));
+
+ status = bladerf_sync_config(dev, BLADERF_MODULE_TX,
+ BLADERF_FORMAT_SC16_Q11_META,
+ 4, 16384, 2, 1000);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_enable_module(dev, BLADERF_MODULE_TX, true);
+ if (status != 0) {
+ return status;
+ }
+
+ meta.flags = BLADERF_META_FLAG_TX_BURST_START |
+ BLADERF_META_FLAG_TX_BURST_END |
+ BLADERF_META_FLAG_TX_NOW;
+
+ status = bladerf_sync_tx(dev, &zero_sample, 1, &meta, 2000);
+ return status;
+}
+
+static int tx_cal_rx_init(struct bladerf *dev)
+{
+ int status;
+
+ status = bladerf_sync_config(dev, BLADERF_MODULE_RX,
+ BLADERF_FORMAT_SC16_Q11_META,
+ 64, 16384, 32, 1000);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_enable_module(dev, BLADERF_MODULE_RX, true);
+ return status;
+}
+
+static void tx_cal_state_deinit(struct tx_cal *cal)
+{
+ free(cal->sweep);
+ free(cal->mag);
+ free(cal->samples);
+ free(cal->filt);
+ free(cal->filt_out);
+ free(cal->post_mix);
+}
+
+/* This should be called immediately preceding the cal routines */
+static int tx_cal_state_init(struct bladerf *dev, struct tx_cal *cal)
+{
+ int status;
+
+ cal->dev = dev;
+ cal->num_samples = TX_CAL_COUNT;
+ cal->loopback = TX_CAL_DEFAULT_LB;
+
+ /* Interleaved SC16 Q11 samples */
+ cal->samples = malloc(2 * sizeof(cal->samples[0]) * cal->num_samples);
+ if (cal->samples == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Filter state */
+ cal->filt = malloc(2 * sizeof(cal->filt[0]) * tx_cal_filt_num_taps);
+ if (cal->filt == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Filter output */
+ cal->filt_out = malloc(sizeof(cal->filt_out[0]) * cal->num_samples);
+ if (cal->filt_out == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Post-mix */
+ cal->post_mix = malloc(sizeof(cal->post_mix[0]) * cal->num_samples);
+ if (cal->post_mix == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Correction sweep and results */
+ cal->sweep = malloc(sizeof(cal->sweep[0]) * TX_CAL_CORR_SWEEP_LEN);
+ if (cal->sweep == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ cal->mag = malloc(sizeof(cal->mag[0]) * TX_CAL_CORR_SWEEP_LEN);
+ if (cal->mag == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Set initial RX in the future */
+ status = bladerf_get_timestamp(cal->dev, BLADERF_MODULE_RX, &cal->ts);
+ if (status == 0) {
+ cal->ts += 20 * TX_CAL_TS_INC;
+ }
+
+ return status;
+}
+
+/* Filter samples
+ * Input: state->post_mix
+ * Output: state->filt_out
+ */
+static void tx_cal_filter(struct tx_cal *state)
+{
+ unsigned int n, m;
+ struct complexf *ins1, *ins2;
+ struct complexf *curr; /* Current filter state */
+ const struct complexf *filt_end = &state->filt[2 * tx_cal_filt_num_taps];
+
+ /* Reset filter state */
+ ins1 = &state->filt[0];
+ ins2 = &state->filt[tx_cal_filt_num_taps];
+ memset(state->filt, 0, 2 * sizeof(state->filt[0]) * tx_cal_filt_num_taps);
+
+ for (n = 0; n < state->num_samples; n++) {
+ /* Insert sample */
+ *ins1 = *ins2 = state->post_mix[n];
+
+ /* Convolve */
+ state->filt_out[n].i = 0;
+ state->filt_out[n].q = 0;
+ curr = ins2;
+
+ for (m = 0; m < tx_cal_filt_num_taps; m++, curr--) {
+ state->filt_out[n].i += tx_cal_filt[m] * curr->i;
+ state->filt_out[n].q += tx_cal_filt[m] * curr->q;
+ }
+
+ /* Update insertion points */
+ ins2++;
+ if (ins2 == filt_end) {
+ ins1 = &state->filt[0];
+ ins2 = &state->filt[tx_cal_filt_num_taps];
+ } else {
+ ins1++;
+ }
+
+ }
+}
+
+/* Deinterleave, scale, and mix with an -Fs/4 tone to shift TX DC offset out at
+ * Fs/4 to baseband.
+ * Input: state->samples
+ * Output: state->post_mix
+ */
+static void tx_cal_mix(struct tx_cal *state)
+{
+ unsigned int n, m;
+ int mix_state;
+ float scaled_i, scaled_q;
+
+ /* Mix with -Fs/4 if RX is tuned "lower" than TX, and Fs/4 otherwise */
+ const int mix_state_inc = state->rx_low ? 1 : -1;
+ mix_state = 0;
+
+ for (n = 0, m = 0; n < (2 * state->num_samples); n += 2, m++) {
+ scaled_i = state->samples[n] / 2048.0f;
+ scaled_q = state->samples[n+1] / 2048.0f;
+
+ switch (mix_state) {
+ case 0:
+ state->post_mix[m].i = scaled_i;
+ state->post_mix[m].q = scaled_q;
+ break;
+
+ case 1:
+ state->post_mix[m].i = scaled_q;
+ state->post_mix[m].q = -scaled_i;
+ break;
+
+ case 2:
+ state->post_mix[m].i = -scaled_i;
+ state->post_mix[m].q = -scaled_q;
+ break;
+
+ case 3:
+ state->post_mix[m].i = -scaled_q;
+ state->post_mix[m].q = scaled_i;
+ break;
+ }
+
+ mix_state = (mix_state + mix_state_inc) & 0x3;
+ }
+}
+
+static int tx_cal_avg_magnitude(struct tx_cal *state, float *avg_mag)
+{
+ int status;
+ const unsigned int start = (tx_cal_filt_num_taps + 1) / 2;
+ unsigned int n;
+ float accum;
+
+ /* Fetch samples at the current settings */
+ status = rx_samples(state->dev, state->samples, state->num_samples,
+ &state->ts, TX_CAL_TS_INC);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Deinterleave & mix TX's DC offset contribution to baseband */
+ tx_cal_mix(state);
+
+ /* Filter out everything other than the TX DC offset's contribution */
+ tx_cal_filter(state);
+
+ /* Compute the power (magnitude^2 to alleviate need for square root).
+ * We skip samples here to account for the group delay of the filter;
+ * the initial samples will be ramping up. */
+ accum = 0;
+ for (n = start; n < state->num_samples; n++) {
+ const struct complexf *s = &state->filt_out[n];
+ const float m = (float) sqrt(s->i * s->i + s->q * s->q);
+ accum += m;
+ }
+
+ *avg_mag = (accum / (state->num_samples - start));
+
+ /* Scale this back up to DAC/ADC counts, just for convenience */
+ *avg_mag *= 2048.0;
+
+ return status;
+}
+
+/* Apply the correction value and read the TX DC offset magnitude */
+static int tx_cal_measure_correction(struct tx_cal *state,
+ bladerf_correction c,
+ int16_t value, float *mag)
+{
+ int status;
+
+ status = bladerf_set_correction(state->dev, BLADERF_MODULE_TX, c, value);
+ if (status != 0) {
+ return status;
+ }
+
+ state->ts += TX_CAL_TS_INC;
+
+ status = tx_cal_avg_magnitude(state, mag);
+ if (status == 0) {
+ PR_VERBOSE(" Corr=%5d, Avg_magnitude=%f\n", value, *mag);
+ }
+
+ return status;
+}
+
+static int tx_cal_get_corr(struct tx_cal *state, bool i_ch,
+ int16_t *corr_value, float *error_value)
+{
+ int status;
+ unsigned int n;
+ int16_t corr;
+ float mag[4];
+ float m1, m2, b1, b2;
+ int16_t range_min, range_max;
+ int16_t min_corr;
+ float min_mag;
+
+ const int16_t x[4] = { -1800, -1000, 1000, 1800 };
+
+ const bladerf_correction corr_module =
+ i_ch ? BLADERF_CORR_LMS_DCOFF_I : BLADERF_CORR_LMS_DCOFF_Q;
+
+ PR_DBG("Getting coarse estimate for %c\n", i_ch ? 'I' : 'Q');
+
+ for (n = 0; n < 4; n++) {
+ status = tx_cal_measure_correction(state, corr_module, x[n], &mag[n]);
+ if (status != 0) {
+ return status;
+ }
+
+ }
+
+ m1 = (mag[1] - mag[0]) / (x[1] - x[0]);
+ b1 = mag[0] - m1 * x[0];
+
+ m2 = (mag[3] - mag[2]) / (x[3] - x[2]);
+ b2 = mag[2] - m2 * x[2];
+
+ PR_VERBOSE(" m1=%3.8f, b1=%3.8f, m2=%3.8f, b=%3.8f\n", m1, b1, m2, b2);
+
+ if (m1 < 0 && m2 > 0) {
+ const int16_t tmp = (int16_t)((b2 - b1) / (m1 - m2) + 0.5);
+ const int16_t corr_est = (tmp / 16) * 16;
+
+ /* Number of points to sweep on either side of our estimate */
+ const unsigned int n_sweep = 10;
+
+ PR_VERBOSE(" corr_est=%d\n", corr_est);
+
+ range_min = corr_est - 16 * n_sweep;
+ if (range_min < -2048) {
+ range_min = -2048;
+ }
+
+ range_max = corr_est + 16 * n_sweep;
+ if (range_max > 2048) {
+ range_max = 2048;
+ }
+
+ } else {
+ /* The frequency and gain combination have yielded a set of
+ * points that do not form intersecting lines. This may be indicative
+ * of a case where the LMS6 DC bias settings can't pull the DC offset
+ * to a zero-crossing. We'll just do a slow, full scan to find
+ * a minimum */
+ PR_VERBOSE(" Could not compute estimate. Performing full sweep.\n");
+ range_min = -2048;
+ range_max = 2048;
+ }
+
+
+ PR_DBG("Performing correction value sweep: [%-5d : 16 :%5d]\n",
+ range_min, range_max);
+
+ min_corr = 0;
+ min_mag = 2048;
+
+ for (n = 0, corr = range_min;
+ corr <= range_max && n < TX_CAL_CORR_SWEEP_LEN;
+ n++, corr += 16) {
+
+ float tmp;
+
+ status = tx_cal_measure_correction(state, corr_module, corr, &tmp);
+ if (status != 0) {
+ return status;
+ }
+
+ if (tmp < 0) {
+ tmp = -tmp;
+ }
+
+ if (tmp < min_mag) {
+ min_corr = corr;
+ min_mag = tmp;
+ }
+ }
+
+ /* Leave the device set to the minimum */
+ status = bladerf_set_correction(state->dev, BLADERF_MODULE_TX,
+ corr_module, min_corr);
+ if (status == 0) {
+ *corr_value = min_corr;
+ *error_value = min_mag;
+ }
+
+ return status;
+}
+
+static int perform_tx_cal(struct tx_cal *state, struct dc_calibration_params *p)
+{
+ int status = 0;
+
+ status = tx_cal_update_frequency(state, p->frequency);
+ if (status != 0) {
+ return status;
+ }
+
+ state->ts += TX_CAL_TS_INC;
+
+ /* Perform I calibration */
+ status = tx_cal_get_corr(state, true, &p->corr_i, &p->error_i);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Perform Q calibration */
+ status = tx_cal_get_corr(state, false, &p->corr_q, &p->error_q);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Re-do I calibration to try to further fine-tune result */
+ status = tx_cal_get_corr(state, true, &p->corr_i, &p->error_i);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Apply the resulting nominal values */
+ status = set_tx_dc_corr(state->dev, p->corr_i, p->corr_q);
+
+ return status;
+}
+
+int dc_calibration_tx(struct bladerf *dev,
+ struct dc_calibration_params *params,
+ size_t num_params, bool print_status)
+{
+ int status = 0;
+ int retval = 0;
+ struct tx_cal_backup backup;
+ struct tx_cal state;
+ size_t i;
+
+ memset(&state, 0, sizeof(state));
+
+ /* Backup the device state prior to making changes */
+ status = get_tx_cal_backup(dev, &backup);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Configure the device for our TX cal operation */
+ status = apply_tx_cal_settings(dev);
+ if (status != 0) {
+ goto out;
+ }
+
+ /* Enable TX and run zero samples through the device */
+ status = tx_cal_tx_init(dev);
+ if (status != 0) {
+ goto out;
+ }
+
+ /* Enable RX */
+ status = tx_cal_rx_init(dev);
+ if (status != 0) {
+ goto out;
+ }
+
+ /* Initialize calibration state information and resources */
+ status = tx_cal_state_init(dev, &state);
+ if (status != 0) {
+ goto out;
+ }
+
+ for (i = 0; i < num_params && status == 0; i++) {
+ status = perform_tx_cal(&state, &params[i]);
+
+ if (status == 0 && print_status) {
+# ifdef DEBUG_DC_CALIBRATION
+ const char sol = '\n';
+ const char eol = '\n';
+# else
+ const char sol = '\r';
+ const char eol = '\0';
+# endif
+ printf("%cCalibrated @ %10" PRIu64 " Hz: "
+ "I=%4d (Error: %4.2f), "
+ "Q=%4d (Error: %4.2f) %c",
+ sol,
+ params[i].frequency,
+ params[i].corr_i, params[i].error_i,
+ params[i].corr_q, params[i].error_q,
+ eol);
+ fflush(stdout);
+ }
+ }
+
+ if (print_status) {
+ putchar('\n');
+ }
+
+out:
+ retval = status;
+
+ status = bladerf_enable_module(dev, BLADERF_MODULE_RX, false);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_enable_module(dev, BLADERF_MODULE_TX, false);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ tx_cal_state_deinit(&state);
+
+ status = set_tx_cal_backup(dev, &backup);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ return retval;
+}
+
+int dc_calibration(struct bladerf *dev, bladerf_module module,
+ struct dc_calibration_params *params,
+ size_t num_params, bool show_status)
+{
+ int status;
+
+ switch (module) {
+ case BLADERF_MODULE_RX:
+ status = dc_calibration_rx(dev, params, num_params, show_status);
+ break;
+
+ case BLADERF_MODULE_TX:
+ status = dc_calibration_tx(dev, params, num_params, show_status);
+ break;
+
+ default:
+ status = BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
diff --git a/Radio/HW/BladeRF/common/src/devcfg.c b/Radio/HW/BladeRF/common/src/devcfg.c
new file mode 100644
index 0000000..497c54b
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/devcfg.c
@@ -0,0 +1,638 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014-2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+
+#include "devcfg.h"
+#include "conversions.h"
+
+#define DEVCFG_DFLT_TX_FREQ 1000000000
+#define DEVCFG_DFLT_TXVGA1 (-14)
+#define DEVCFG_DFLT_TXVGA2 0
+
+#define DEVCFG_DFLT_RX_FREQ 1000000000
+#define DEVCFG_DFLT_LNAGAIN BLADERF_LNA_GAIN_MAX
+#define DEVCFG_DFLT_RXVGA1 30
+#define DEVCFG_DFLT_RXVGA2 0
+
+#define DEVCFG_DFLT_SAMPLERATE 2000000
+#define DEVCFG_DFLT_BANDWIDTH 1500000
+
+#define DEVCFG_DFLT_SAMPLES_PER_BUF 8192
+#define DEVCFG_DFLT_NUM_BUFFERS 64
+#define DEVCFG_DFLT_NUM_TRANSFERS 16
+#define DEVCFG_DFLT_STREAM_TIMEMOUT_MS 5000
+#define DEVCFG_DFLT_SYNC_TIMEOUT_MS (2 * DEVCFG_DFLT_STREAM_TIMEMOUT_MS)
+
+#define OPTION_HELP 'h'
+#define OPTION_DEVICE 'd'
+#define OPTION_LOOPBACK 'l'
+#define OPTION_VERBOSITY 'v'
+#define OPTION_SAMPLERATE 's'
+#define OPTION_RX_SAMPLERATE 0x80
+#define OPTION_TX_SAMPLERATE 0x81
+#define OPTION_BANDWIDTH 'b'
+#define OPTION_RX_BANDWIDTH 0x82
+#define OPTION_TX_BANDWIDTH 0x83
+#define OPTION_FREQUENCY 'f'
+#define OPTION_RX_FREQUENCY 0x84
+#define OPTION_TX_FREQUENCY 0x85
+#define OPTION_LNAGAIN 0x86
+#define OPTION_RXVGA1 0x87
+#define OPTION_RXVGA2 0x88
+#define OPTION_TXVGA1 0x89
+#define OPTION_TXVGA2 0x8a
+#define OPTION_SAMPLES_PER_BUF 0x91
+#define OPTION_NUM_BUFFERS 0x92
+#define OPTION_NUM_TRANSFERS 0x93
+#define OPTION_STREAM_TIMEOUT 0x94
+#define OPTION_SYNC_TIMEOUT 0x95
+#define OPTION_ENABLE_XB200 0x96
+
+static const numeric_suffix hz_suffixes[] = {
+ { "K", 1000 },
+ { "KHz", 1000 },
+ { "M", 1000000 },
+ { "MHz", 1000000 },
+ { "G", 1000000000 },
+ { "GHz", 1000000000 },
+};
+
+static const struct option devcfg_long_options[] =
+{
+ { "help", no_argument, 0, OPTION_HELP },
+ { "device", required_argument, 0, OPTION_DEVICE },
+ { "verbosity", required_argument, 0, OPTION_VERBOSITY },
+
+ { "samplerate", required_argument, 0, OPTION_SAMPLERATE },
+ { "rx-samplerate", required_argument, 0, OPTION_RX_SAMPLERATE },
+ { "tx-samplerate", required_argument, 0, OPTION_TX_SAMPLERATE },
+
+ { "bandwidth", required_argument, 0, OPTION_BANDWIDTH },
+ { "rx-bandwidth", required_argument, 0, OPTION_RX_BANDWIDTH },
+ { "tx-bandwidth", required_argument, 0, OPTION_TX_BANDWIDTH },
+
+ { "frequency", required_argument, 0, OPTION_FREQUENCY },
+ { "rx-frequency", required_argument, 0, OPTION_RX_FREQUENCY },
+ { "tx-frequency", required_argument, 0, OPTION_TX_FREQUENCY },
+
+ { "lna-gain", required_argument, 0, OPTION_LNAGAIN },
+ { "rxvga1-gain", required_argument, 0, OPTION_RXVGA1 },
+ { "rxvga2-gain", required_argument, 0, OPTION_RXVGA2 },
+
+ { "txvga1-gain", required_argument, 0, OPTION_TXVGA1 },
+ { "txvga2-gain", required_argument, 0, OPTION_TXVGA2 },
+
+ { "loopback", required_argument, 0, OPTION_LOOPBACK },
+
+ { "xb200", no_argument, 0, OPTION_ENABLE_XB200 },
+
+ { "samples-per-buffer", required_argument, 0, OPTION_SAMPLES_PER_BUF },
+ { "num-buffers", required_argument, 0, OPTION_NUM_BUFFERS },
+ { "num-transfers", required_argument, 0, OPTION_NUM_TRANSFERS },
+ { "stream-timeout", required_argument, 0, OPTION_STREAM_TIMEOUT },
+ { "sync-timeout", required_argument, 0, OPTION_SYNC_TIMEOUT },
+};
+
+void devcfg_init(struct devcfg *c)
+{
+ c->device_specifier = NULL;
+
+ c->tx_frequency = DEVCFG_DFLT_TX_FREQ;
+ c->tx_bandwidth = DEVCFG_DFLT_BANDWIDTH;
+ c->tx_samplerate = DEVCFG_DFLT_SAMPLERATE;
+ c->txvga1 = DEVCFG_DFLT_TXVGA1;
+ c->txvga2 = DEVCFG_DFLT_TXVGA2;
+
+ c->rx_frequency = DEVCFG_DFLT_TX_FREQ;
+ c->rx_bandwidth = DEVCFG_DFLT_BANDWIDTH;
+ c->rx_samplerate = DEVCFG_DFLT_SAMPLERATE;
+ c->lnagain = DEVCFG_DFLT_LNAGAIN;
+ c->rxvga1 = DEVCFG_DFLT_RXVGA1;
+ c->rxvga2 = DEVCFG_DFLT_RXVGA2;
+
+ c->loopback = BLADERF_LB_NONE;
+
+ c->verbosity = BLADERF_LOG_LEVEL_INFO;
+
+ c->enable_xb200 = false;
+
+ c->samples_per_buffer = DEVCFG_DFLT_SAMPLES_PER_BUF;
+ c->num_buffers = DEVCFG_DFLT_NUM_BUFFERS;
+ c->num_transfers = DEVCFG_DFLT_NUM_TRANSFERS;
+ c->stream_timeout_ms = DEVCFG_DFLT_STREAM_TIMEMOUT_MS;
+ c->sync_timeout_ms = DEVCFG_DFLT_SYNC_TIMEOUT_MS;
+}
+
+void devcfg_deinit(struct devcfg *c)
+{
+ free(c->device_specifier);
+}
+
+void devcfg_print_common_help(const char *title)
+{
+ if (title != NULL) {
+ printf("%s", title);
+ }
+
+ printf(" -h, --help Show this help text.\n");
+ printf(" -d, --device <str> Open the specified device.\n");
+ printf(" -v, --verbosity <level> Set the libbladeRF verbosity level.\n");
+ printf(" --xb200 Enable the XB-200. This will remain enabled\n");
+ printf(" until the device is power-cycled.\n");
+ printf("\n");
+ printf(" -f, --frequency <freq> Set RX and TX to the specified frequency.\n");
+ printf(" --rx-frequency <freq> Set RX to the specified frequency.\n");
+ printf(" --tx-frequency <freq> Set TX to the specified frequency.\n");
+ printf("\n");
+ printf(" -s, --samplerate <rate> Set RX and TX to the specified sample rate.\n");
+ printf(" --rx-samplerate <rate> Set RX to the specified sample rate.\n");
+ printf(" --tx-samplerate <rate> Set RX to the specified sample rate.\n");
+ printf("\n");
+ printf(" -b, --bandwidth <bw> Set RX and TX to the specified bandwidth.\n");
+ printf(" --rx-bandwidth <bw> Set RX to the specified bandwidth.\n");
+ printf(" --tx-bandwidth <bw> Set TX to the specified bandwidth.\n");
+ printf("\n");
+ printf(" --lna-gain <gain> Set the RX LNA to the specified gain.\n");
+ printf(" Options are: bypass, mid, max\n");
+ printf(" --rxvga1-gain <gain> Set RX VGA1 to the specified gain.\n");
+ printf(" --rxvga2-gain <gain> Set RX VGA2 to the specified gain.\n");
+ printf(" --txvga1-gain <gain> Set TX VGA1 to the specified gain.\n");
+ printf(" --txvga2-gain <gain> Set TX VGA2 to the specified gain.\n");
+ printf("\n");
+ printf(" --num-buffers <n> Allocate <n> sample buffers.\n");
+ printf(" --samples-per-buffer <n> Allocate <n> samples in each sample buffer.\n");
+ printf(" --num-transfers <n> Utilize up to <n> simultaneous USB transfers.\n");
+ printf(" --stream-timeout <n> Set stream timeout to <n> milliseconds.\n");
+ printf(" --sync-timeout <n> Set sync function timeout to <n> milliseconds.\n");
+
+}
+
+struct option * devcfg_get_long_options(const struct option *app_options)
+{
+ struct option *ret;
+ size_t app_size = 0;
+ const size_t devcfg_size = sizeof(devcfg_long_options);
+
+ while (app_options[app_size].name != NULL) {
+ app_size++;
+ }
+
+ /* Account for 0-entry */
+ app_size = (app_size + 1) * sizeof(app_options[0]);
+
+ ret = malloc(devcfg_size + app_size);
+
+ if (ret != NULL) {
+ memcpy(ret, &devcfg_long_options, devcfg_size);
+ memcpy(ret + ARRAY_SIZE(devcfg_long_options) , app_options, app_size);
+ }
+
+ return ret;
+}
+
+int devcfg_handle_args(int argc, char **argv, const char *option_str,
+ const struct option *long_options, struct devcfg *config)
+{
+ int c;
+ bool ok;
+ int status;
+ unsigned int freq_min;
+
+ while ((c = getopt_long(argc, argv, option_str, long_options, NULL)) >= 0) {
+
+ if (config->enable_xb200) {
+ freq_min = BLADERF_FREQUENCY_MIN_XB200;
+ } else {
+ freq_min = BLADERF_FREQUENCY_MIN;
+ }
+
+ switch (c) {
+
+ case OPTION_HELP:
+ return 1;
+ break;
+
+ case OPTION_DEVICE:
+ if (config->device_specifier == NULL) {
+ config->device_specifier = strdup(optarg);
+ if (config->device_specifier == NULL) {
+ perror("strdup");
+ return -1;
+ }
+ }
+ break;
+
+ case OPTION_LOOPBACK:
+ status = str2loopback(optarg, &config->loopback);
+ if (status != 0) {
+ fprintf(stderr, "Invalid loopback mode: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_VERBOSITY:
+ config->verbosity = str2loglevel(optarg, &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid verbosity level: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_ENABLE_XB200:
+ config->enable_xb200 = true;
+ break;
+
+ case OPTION_FREQUENCY:
+ config->rx_frequency =
+ str2uint_suffix(optarg,
+ freq_min,
+ BLADERF_FREQUENCY_MAX,
+ hz_suffixes, ARRAY_SIZE(hz_suffixes),
+ &ok);
+
+ if (!ok) {
+ fprintf(stderr, "Invalid frequency: %s\n", optarg);
+ return -1;
+ } else {
+ config->tx_frequency = config->rx_frequency;
+ }
+ break;
+
+ case OPTION_RX_FREQUENCY:
+ config->rx_frequency =
+ str2uint_suffix(optarg,
+ freq_min,
+ BLADERF_FREQUENCY_MAX,
+ hz_suffixes, ARRAY_SIZE(hz_suffixes),
+ &ok);
+
+ if (!ok) {
+ fprintf(stderr, "Invalid RX frequency: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_TX_FREQUENCY:
+ config->tx_frequency =
+ str2uint_suffix(optarg,
+ freq_min,
+ BLADERF_FREQUENCY_MAX,
+ hz_suffixes, ARRAY_SIZE(hz_suffixes),
+ &ok);
+
+ if (!ok) {
+ fprintf(stderr, "Invalid TX frequency: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_SAMPLERATE:
+ config->rx_samplerate =
+ str2uint_suffix(optarg,
+ BLADERF_SAMPLERATE_MIN,
+ BLADERF_SAMPLERATE_REC_MAX,
+ hz_suffixes, ARRAY_SIZE(hz_suffixes),
+ &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid sample rate: %s\n", optarg);
+ return -1;
+ } else {
+ config->tx_samplerate = config->rx_samplerate;
+ }
+ break;
+
+ case OPTION_RX_SAMPLERATE:
+ config->rx_samplerate =
+ str2uint_suffix(optarg,
+ BLADERF_SAMPLERATE_MIN,
+ BLADERF_SAMPLERATE_REC_MAX,
+ hz_suffixes, ARRAY_SIZE(hz_suffixes),
+ &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid RX sample rate: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_TX_SAMPLERATE:
+ config->tx_samplerate =
+ str2uint_suffix(optarg,
+ BLADERF_SAMPLERATE_MIN,
+ BLADERF_SAMPLERATE_REC_MAX,
+ hz_suffixes, ARRAY_SIZE(hz_suffixes),
+ &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid TX sample rate: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_BANDWIDTH:
+ config->rx_bandwidth =
+ str2uint_suffix(optarg,
+ BLADERF_BANDWIDTH_MIN,
+ BLADERF_BANDWIDTH_MAX,
+ hz_suffixes, ARRAY_SIZE(hz_suffixes),
+ &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid bandwidth: %s\n", optarg);
+ return -1;
+ } else {
+ config->tx_bandwidth = config->rx_bandwidth;
+ }
+ break;
+
+ case OPTION_RX_BANDWIDTH:
+ config->rx_bandwidth =
+ str2uint_suffix(optarg,
+ BLADERF_BANDWIDTH_MIN,
+ BLADERF_BANDWIDTH_MAX,
+ hz_suffixes, ARRAY_SIZE(hz_suffixes),
+ &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid RX bandwidth: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_TX_BANDWIDTH:
+ config->tx_bandwidth =
+ str2uint_suffix(optarg,
+ BLADERF_BANDWIDTH_MIN,
+ BLADERF_BANDWIDTH_MAX,
+ hz_suffixes, ARRAY_SIZE(hz_suffixes),
+ &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid TX bandwidth: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_LNAGAIN:
+ status = str2lnagain(optarg, &config->lnagain);
+ if (status != 0) {
+ fprintf(stderr, "Invalid RX LNA gain: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_RXVGA1:
+ config->rxvga1 = str2int(optarg,
+ BLADERF_RXVGA1_GAIN_MIN,
+ BLADERF_RXVGA1_GAIN_MAX,
+ &ok);
+
+ if (!ok) {
+ fprintf(stderr, "Invalid RXVGA1 gain: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_RXVGA2:
+ config->rxvga2 = str2int(optarg,
+ BLADERF_RXVGA2_GAIN_MIN,
+ BLADERF_RXVGA2_GAIN_MAX,
+ &ok);
+
+ if (!ok) {
+ fprintf(stderr, "Invalid RXVGA1 gain: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_TXVGA1:
+ config->txvga1 = str2int(optarg,
+ BLADERF_TXVGA1_GAIN_MIN,
+ BLADERF_TXVGA1_GAIN_MAX,
+ &ok);
+
+ if (!ok) {
+ fprintf(stderr, "Invalid TXVGA1 gain: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_TXVGA2:
+ config->txvga2 = str2int(optarg,
+ BLADERF_TXVGA2_GAIN_MIN,
+ BLADERF_TXVGA2_GAIN_MAX,
+ &ok);
+
+ if (!ok) {
+ fprintf(stderr, "Invalid TXVGA2 gain: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_NUM_BUFFERS:
+ config->num_buffers = str2uint(optarg, 1, UINT_MAX, &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid buffer count: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_SAMPLES_PER_BUF:
+ config->samples_per_buffer = str2uint(optarg,
+ 1024, UINT_MAX, &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid buffer size (in samples): %s\n",
+ optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_NUM_TRANSFERS:
+ config->num_transfers = str2uint(optarg, 1, UINT_MAX, &ok);
+
+ if (!ok) {
+ fprintf(stderr, "Invalid transfer count: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_STREAM_TIMEOUT:
+ config->stream_timeout_ms = str2uint(optarg, 0, UINT_MAX, &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid stream timeout: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_SYNC_TIMEOUT:
+ config->sync_timeout_ms = str2uint(optarg, 0, UINT_MAX, &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid sync function timeout: %s\n", optarg);
+ return -1;
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int devcfg_apply(struct bladerf *dev, const struct devcfg *c)
+{
+ int status;
+ const char *board_name;
+
+ board_name = bladerf_get_board_name(dev);
+
+ bladerf_log_set_verbosity(c->verbosity);
+
+ status = bladerf_set_loopback(dev, c->loopback);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set loopback: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ if (c->enable_xb200) {
+ status = bladerf_expansion_attach(dev, BLADERF_XB_200);
+ if (status != 0) {
+ fprintf(stderr, "Failed to attach XB-200.\n");
+ return -1;
+ }
+ }
+
+ status = bladerf_set_frequency(dev, BLADERF_MODULE_RX, c->rx_frequency);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set RX frequency: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ status = bladerf_set_frequency(dev, BLADERF_MODULE_TX, c->tx_frequency);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set TX frequency: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ status = bladerf_set_sample_rate(dev, BLADERF_MODULE_RX, c->rx_samplerate, NULL);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set RX sample rate:%s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ status = bladerf_set_sample_rate(dev, BLADERF_MODULE_TX, c->tx_samplerate, NULL);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set TX sample rate: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ status = bladerf_set_bandwidth(dev, BLADERF_MODULE_RX, c->rx_bandwidth, NULL);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set RX bandwidth: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ status = bladerf_set_bandwidth(dev, BLADERF_MODULE_TX, c->tx_bandwidth, NULL);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set RX bandwidth: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ if (strcasecmp(board_name, "bladerf1") == 0) {
+ status = bladerf_set_lna_gain(dev, c->lnagain);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set RX LNA gain: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ status = bladerf_set_rxvga1(dev, c->rxvga1);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set RX VGA1 gain: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ status = bladerf_set_rxvga2(dev, c->rxvga2);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set RX VGA2 gain: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ status = bladerf_set_txvga1(dev, c->txvga1);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set TX VGA1 gain: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ status = bladerf_set_txvga2(dev, c->txvga2);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set TX VGA2 gain: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int devcfg_perform_sync_config(struct bladerf *dev,
+ bladerf_module module,
+ bladerf_format format,
+ const struct devcfg *config,
+ bool enable_module)
+{
+ int status = bladerf_sync_config(dev, module, format,
+ config->num_buffers,
+ config->samples_per_buffer,
+ config->num_transfers,
+ config->stream_timeout_ms);
+
+ if (status != 0) {
+ fprintf(stderr, "Failed to configure %s: %s\n",
+ module == BLADERF_MODULE_RX ? "RX" : "TX",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ if (enable_module) {
+ status = bladerf_enable_module(dev, module, true);
+ if (status != 0) {
+ fprintf(stderr, "Failed to enable %s module: %s\n",
+ module == BLADERF_MODULE_RX ? "RX" : "TX",
+ bladerf_strerror(status));
+ }
+ }
+
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/common/src/log.c b/Radio/HW/BladeRF/common/src/log.c
new file mode 100644
index 0000000..2872620
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/log.c
@@ -0,0 +1,98 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2013 Nuand LLC.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifdef LOGGING_ENABLED
+#include <log.h>
+#if !defined(WIN32) && !defined(__CYGWIN__) && defined(LOG_SYSLOG_ENABLED)
+#include <syslog.h>
+#endif
+#include <stdio.h>
+#include <stdarg.h>
+
+static bladerf_log_level filter_level = BLADERF_LOG_LEVEL_INFO;
+
+void log_write(bladerf_log_level level, const char *format, ...)
+{
+ /* Only process this message if its level exceeds the current threshold */
+ if (level >= filter_level)
+ {
+ va_list args;
+
+ /* Write the log message */
+ va_start(args, format);
+#if defined(WIN32) || defined(__CYGWIN__)
+ vfprintf(stderr, format, args);
+#else
+# if defined (LOG_SYSLOG_ENABLED)
+ {
+ int syslog_level;
+
+ switch (level) {
+ case BLADERF_LOG_LEVEL_VERBOSE:
+ case BLADERF_LOG_LEVEL_DEBUG:
+ syslog_level = LOG_DEBUG;
+ break;
+
+ case BLADERF_LOG_LEVEL_INFO:
+ syslog_level = LOG_INFO;
+ break;
+
+ case BLADERF_LOG_LEVEL_WARNING:
+ syslog_level = LOG_WARNING;
+ break;
+
+ case BLADERF_LOG_LEVEL_ERROR:
+ syslog_level = LOG_ERR;
+ break;
+
+ case BLADERF_LOG_LEVEL_CRITICAL:
+ syslog_level = LOG_CRIT;
+ break;
+
+ default:
+ /* Shouldn't be used, so just route it to a low level */
+ syslog_level = LOG_DEBUG;
+ break;
+ }
+
+ vsyslog(syslog_level | LOG_USER, format, args);
+ }
+# else
+ vfprintf(stderr, format, args);
+# endif
+#endif
+ va_end(args);
+ }
+}
+
+void log_set_verbosity(bladerf_log_level level)
+{
+ filter_level = level;
+}
+
+bladerf_log_level log_get_verbosity()
+{
+ return filter_level;
+}
+#endif
diff --git a/Radio/HW/BladeRF/common/src/osx/clock_gettime.c b/Radio/HW/BladeRF/common/src/osx/clock_gettime.c
new file mode 100644
index 0000000..5e7200e
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/osx/clock_gettime.c
@@ -0,0 +1,59 @@
+/*
+ * clock_gettime() wrapper for OSX based upon jbenet's github "gist":
+ * https://gist.github.com/jbenet/1087739
+ *
+ * Copyright (c) 2011 Juan Batiz-Benet (https://github.com/jbenet)
+ * Copyright (c) 2013-2017 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "clock_gettime.h"
+#include <errno.h>
+#include <mach/clock.h>
+#include <mach/mach.h>
+
+int clock_gettime(clockid_t clk_id, struct timespec *tp)
+{
+ kern_return_t ret;
+ clock_serv_t cclock;
+ mach_timespec_t mts;
+
+ ret = host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+ if (ret != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = clock_get_time(cclock, &mts);
+ if (ret != 0) {
+ goto clock_gettime_out;
+ }
+
+ tp->tv_sec = mts.tv_sec;
+ tp->tv_nsec = mts.tv_nsec;
+
+clock_gettime_out:
+ if (mach_port_deallocate(mach_task_self(), cclock) != 0 || ret != 0) {
+ errno = EINVAL;
+ return -1;
+ } else {
+ return 0;
+ }
+}
diff --git a/Radio/HW/BladeRF/common/src/parse.c b/Radio/HW/BladeRF/common/src/parse.c
new file mode 100644
index 0000000..dd6abe1
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/parse.c
@@ -0,0 +1,455 @@
+/*
+ * Copyright (c) 2017 Nuand LLC.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "parse.h"
+#include "conversions.h"
+#include "log.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static char **add_arg(
+ char **argv, int argc, const char *buf, int start, int end, int quote_count)
+{
+ char **rv;
+ char *d_ptr;
+ int i;
+ int len;
+
+ char c;
+ char quote_char;
+
+ quote_char = 0;
+
+ rv = (char **)realloc(argv, sizeof(char *) * (argc + 1));
+ if (rv == NULL) {
+ return NULL;
+ }
+
+ rv[argc] = NULL;
+
+ len = end - start + 1;
+
+ d_ptr = (char *)malloc(len + 1 - quote_count * 2);
+ if (d_ptr == NULL) {
+ free(rv);
+ return NULL;
+ }
+
+ rv[argc - 1] = d_ptr;
+
+ for (i = 0; i < len; i++) {
+ c = buf[start + i];
+
+ if (!quote_char) {
+ /* We are not in a quote, copy everything but quote chars */
+ if (c == '"' || c == '\'') {
+ quote_char = c;
+ } else {
+ *d_ptr++ = c;
+ }
+ } else {
+ /* We are in a quote, copy everything but the current quote char */
+ if (c == quote_char) {
+ quote_char = 0;
+ } else {
+ *d_ptr++ = c;
+ }
+ }
+ }
+ *d_ptr = '\0';
+
+ return rv;
+}
+
+int str2args(const char *line, char comment_char, char ***argv)
+{
+ char **rv;
+ int argc;
+
+ unsigned i;
+ size_t len;
+
+ bool in_arg;
+ char c;
+ char quote_char;
+
+ int arg_start;
+ int quote_count;
+
+ rv = NULL;
+ argc = 0;
+
+ quote_char = 0;
+
+ arg_start = 0;
+ quote_count = 0;
+
+ len = strlen(line);
+
+ in_arg = false;
+
+ for (i = 0; i < len; i++) {
+ c = line[i];
+
+ if (in_arg) {
+ /* Found the end of a quote! */
+ if (quote_char) {
+ if (quote_char == c) {
+ quote_char = 0;
+ }
+ continue;
+ }
+
+ /* Found the beginning of a quote! */
+ if (c == '\'' || c == '"') {
+ quote_count++;
+ quote_char = c;
+ continue;
+ }
+
+ /* Found whitespace outside of quote */
+ if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
+ in_arg = false;
+ argc++;
+ rv = add_arg(rv, argc, line, arg_start, i - 1, quote_count);
+ if (rv == NULL)
+ return -1;
+ }
+ } else {
+ if (c == comment_char) {
+ break;
+ }
+ /* Enter a new argument */
+ if (c != ' ' && c != '\t' && c != '\r' && c != '\n') {
+ /* If first argument is a tick it means we're in a quote */
+ if (c == '\'' || c == '"') {
+ quote_char = c;
+ } else {
+ quote_char = 0;
+ }
+ quote_count = 0;
+ arg_start = i;
+ in_arg = true;
+ }
+ /* else this is still whitespace */
+ }
+ }
+
+ /* reached the end of the line, check to see if current arg needs to
+ * be closed */
+ if (in_arg) {
+ if (quote_char) {
+ free_args(argc, rv);
+ return -2;
+ } else {
+ argc++;
+ rv = add_arg(rv, argc, line, arg_start, i - 1, quote_count);
+ if (rv == NULL) {
+ return -1;
+ }
+ }
+ }
+
+ *argv = rv;
+
+ return argc;
+}
+
+void free_args(int argc, char **argv)
+{
+ int i;
+ for (i = 0; i < argc; i++) {
+ free(argv[i]);
+ }
+ free(argv);
+}
+
+static struct config_options *add_opt(
+ struct config_options *optv, int optc, char *key, char *val, int lineno)
+{
+ struct config_options *rv;
+ char *ptr1, *ptr2;
+ rv = (struct config_options *)realloc(optv,
+ sizeof(struct config_options) * optc);
+ if (rv == NULL) {
+ return NULL;
+ }
+
+ ptr1 = (char *)malloc(strlen(key) + 1);
+ if (ptr1 == NULL) {
+ free(rv);
+ return NULL;
+ }
+ strcpy(ptr1, key);
+ rv[optc - 1].key = ptr1;
+
+ ptr2 = (char *)malloc(strlen(val) + 1);
+ if (ptr2 == NULL) {
+ free(ptr1);
+ free(rv);
+ return NULL;
+ }
+ strcpy(ptr2, val);
+ rv[optc - 1].value = ptr2;
+
+ rv[optc - 1].lineno = lineno;
+
+ return rv;
+}
+
+bool update_match(struct bladerf *dev, char *str)
+{
+ size_t len;
+ int status;
+ struct bladerf_devinfo info;
+ bladerf_fpga_size fpga_size;
+
+ status = bladerf_get_devinfo(dev, &info);
+ if (status < 0)
+ return false;
+
+ bladerf_get_fpga_size(dev, &fpga_size);
+ if (status < 0)
+ return false;
+
+ str++;
+ len = strlen(str);
+ if (str[len - 1] == ']')
+ str[len - 1] = '\0';
+
+ if (!strcmp(str, "x40")) {
+ return fpga_size == BLADERF_FPGA_40KLE;
+ } else if (!strcmp(str, "x115")) {
+ return fpga_size == BLADERF_FPGA_115KLE;
+ }
+
+ status = bladerf_devstr_matches(str, &info);
+
+ return status == 1;
+}
+
+int str2options(struct bladerf *dev,
+ const char *buf,
+ size_t buf_sz,
+ struct config_options **opts)
+{
+ char *line;
+ char *d_ptr;
+ int line_num;
+ char c;
+ unsigned i;
+
+ struct config_options *optv;
+ int optc;
+
+ char **line_argv;
+ int line_argc;
+
+ bool match;
+
+ match = true;
+
+ optv = NULL;
+ optc = 0;
+
+ line_num = 1;
+
+ line = malloc(buf_sz + 1);
+ if (!line)
+ return BLADERF_ERR_MEM;
+
+ d_ptr = line;
+
+ for (i = 0; i < buf_sz; i++) {
+ c = buf[i];
+ if (c == '\n') {
+ /* deal with the old line */
+ *d_ptr = 0;
+ line_argc = str2args(line, '#', &line_argv);
+ if (line_argc < 0)
+ goto out;
+
+ /* handle line */
+ if (line_argc > 3) {
+ log_error("Too many arguments in bladeRF.conf on line %d\n",
+ line_num);
+ goto out;
+ } else if (match && line_argc == 2) {
+ optc++;
+ optv =
+ add_opt(optv, optc, line_argv[0], line_argv[1], line_num);
+ if (!optv) {
+ optc = -1;
+ goto out;
+ }
+ } else if (line_argc == 1) {
+ if (*line_argv[0] != '[') {
+ log_error("Expecting scoping line (requires [ and ]) on "
+ "line %d\n",
+ line_num);
+ }
+ match = update_match(dev, line_argv[0]);
+ }
+
+ /* free line */
+ free_args(line_argc, line_argv);
+
+ /* setup to capture the next line */
+ line_num++;
+ d_ptr = line;
+ } else {
+ *d_ptr++ = c;
+ }
+ }
+
+ if (opts)
+ *opts = optv;
+
+out:
+ free(line);
+ return optc;
+}
+
+void free_opts(struct config_options *optv, int optc)
+{
+ int i;
+
+ for (i = 0; i < optc; i++) {
+ free(optv[i].key);
+ free(optv[i].value);
+ }
+ free(optv);
+}
+
+int csv2int(const char *line, int ***args)
+{
+ const char delim[] = " \r\n\t,.:"; /* supported delimiters */
+ const size_t MAXLEN = 128; /* max line length (with newline and null) */
+ static size_t arglen = 2; /* tunable: initial expected column count */
+ char *myline = NULL; /* local copy of 'line' */
+ char *parsestr = NULL; /* ptr to 'myline' on first strtok_r */
+ char *saveptr = NULL; /* strtok_r state pointer */
+ int **argout = NULL; /* array of output values */
+ size_t count = 0; /* count of tokens extracted */
+ size_t i;
+
+ // Validity check
+ if (NULL == line) {
+ log_debug("line is null\n");
+ return 0;
+ }
+
+ if (NULL == args) {
+ log_error("args is null\n");
+ goto fail;
+ }
+
+ // strtok_r doesn't respect const, so make a copy of 'line'
+ myline = calloc(MAXLEN, 1);
+ if (NULL == myline) {
+ log_error("could not calloc myline\n");
+ goto fail;
+ }
+
+ myline = strncpy(myline, line, MAXLEN - 1);
+
+ // Initial allocation of argout
+ argout = malloc(arglen * sizeof(int *));
+ if (NULL == argout) {
+ log_error("could not malloc argout\n");
+ goto fail;
+ }
+
+ // Loop over input until strtok_r returns a NULL
+ for (i = 0, parsestr = myline; true; ++i, parsestr = NULL) {
+ char *token = NULL; /* return token from strtok_r */
+ bool ok;
+
+ token = strtok_r(parsestr, delim, &saveptr);
+ if (NULL == token) {
+ break;
+ }
+
+ // Expand argout if necessary
+ if (i >= arglen) {
+ arglen *= 2;
+ log_verbose("expanding allocation to %zu column(s)\n", arglen);
+ int **newargout = realloc(argout, arglen * sizeof(int *));
+ if (NULL == newargout) {
+ log_error("could not realloc(argout,%zu)\n", arglen);
+ goto fail;
+ }
+ argout = newargout;
+ }
+
+ // Allocate memory for this value
+ argout[i] = malloc(sizeof(int));
+ if (NULL == argout[i]) {
+ log_error("could not malloc argout[%zu]\n", i);
+ goto fail;
+ }
+
+ // Update the count now, in case str2int fails and we need to dealloc
+ ++count;
+
+ // Parse token into an integer value
+ *argout[i] = str2int(token, INT32_MIN, INT32_MAX, &ok);
+ if (!ok) {
+ log_error("str2int failed on '%s'\n", token);
+ goto fail;
+ }
+ }
+
+ // Success!
+ *args = argout;
+ free(myline);
+
+ // If arglen is too big, cut it in half next time...
+ if (count <= (arglen / 2)) {
+ arglen /= 2;
+ log_verbose("decreasing future arglen to %zu\n", arglen);
+ }
+
+ return (int)count;
+
+fail:
+ // Deallocate everything...
+ free(myline);
+ free_csv2int((int)count, argout);
+ return -1;
+}
+
+void free_csv2int(int argc, int **args)
+{
+ int i;
+
+ if (NULL == args) {
+ return;
+ }
+
+ for (i = 0; i < argc; ++i) {
+ free(args[i]);
+ }
+
+ free(args);
+}
diff --git a/Radio/HW/BladeRF/common/src/range.c b/Radio/HW/BladeRF/common/src/range.c
new file mode 100644
index 0000000..c16fe22
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/range.c
@@ -0,0 +1,74 @@
+/*
+ * 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
+ */
+
+#ifdef BLADERF_NIOS_BUILD
+#include "devices.h"
+#endif // BLADERF_NIOS_BUILD
+
+/* Avoid building this in low-memory situations */
+#if !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X)
+
+#include "range.h"
+
+#if !defined(BLADERF_NIOS_BUILD) && !defined(BLADERF_NIOS_PC_SIMULATION)
+#include "log.h"
+#endif
+
+bool is_within_range(struct bladerf_range const *range, int64_t value)
+{
+ if (NULL == range) {
+ log_error("%s: range is null\n", __FUNCTION__);
+ return false;
+ }
+
+ return (__scale(range, value) >= range->min &&
+ __scale(range, value) <= range->max);
+}
+
+int64_t clamp_to_range(struct bladerf_range const *range, int64_t value)
+{
+ if (NULL == range) {
+ log_error("%s: range is null\n", __FUNCTION__);
+ return value;
+ }
+
+ if (__scale(range, value) < range->min) {
+ log_debug("%s: Requested value %" PRIi64
+ " is below range [%g,%g], clamping to %" PRIi64 "\n",
+ __FUNCTION__, value, __unscale(range, range->min),
+ __unscale(range, range->max),
+ __unscale_int64(range, range->min));
+ value = __unscale_int64(range, range->min);
+ }
+
+ if (__scale(range, value) > range->max) {
+ log_debug("%s: Requested value %" PRIi64
+ " is above range [%g,%g], clamping to %" PRIi64 "\n",
+ __FUNCTION__, value, __unscale(range, range->min),
+ __unscale(range, range->max),
+ __unscale_int64(range, range->max));
+ value = __unscale_int64(range, range->max);
+ }
+
+ return value;
+}
+
+#endif // !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X)
diff --git a/Radio/HW/BladeRF/common/src/sha256.c b/Radio/HW/BladeRF/common/src/sha256.c
new file mode 100644
index 0000000..b65c7ff
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/sha256.c
@@ -0,0 +1,343 @@
+/*-
+ * Copyright 2005 Colin Percival
+ * Copyright 2013 Daniel Gröber
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include "sha256.h"
+
+#if BLADERF_BIG_ENDIAN == 1
+
+/* Copy a vector of big-endian uint32_t into a vector of bytes */
+#define be32enc_vect(dst, src, len) \
+ memcpy((void *)dst, (const void *)src, (size_t)len)
+
+/* Copy a vector of bytes into a vector of big-endian uint32_t */
+#define be32dec_vect(dst, src, len) \
+ memcpy((void *)dst, (const void *)src, (size_t)len)
+
+#else
+
+/* From libbsd/include/bsd/sys/endian.h */
+/*
+ * Copyright © 2011 Guillem Jover <guillem@hadrons.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+static __inline uint32_t
+be32dec(const void *pp)
+{
+ uint8_t const *p = (uint8_t const *)pp;
+
+ return (((unsigned)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
+}
+
+static __inline void
+be32enc(void *pp, uint32_t u)
+{
+ uint8_t *p = (uint8_t *)pp;
+
+ p[0] = (u >> 24) & 0xff;
+ p[1] = (u >> 16) & 0xff;
+ p[2] = (u >> 8) & 0xff;
+ p[3] = u & 0xff;
+}
+/* END from libbsd/include/bsd/sys/endian.h */
+
+/*
+ * Encode a length len/4 vector of (uint32_t) into a length len vector of
+ * (unsigned char) in big-endian form. Assumes len is a multiple of 4.
+ */
+static void
+be32enc_vect(unsigned char *dst, const uint32_t *src, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len / 4; i++)
+ be32enc(dst + i * 4, src[i]);
+}
+
+/*
+ * Decode a big-endian length len vector of (unsigned char) into a length
+ * len/4 vector of (uint32_t). Assumes len is a multiple of 4.
+ */
+static void
+be32dec_vect(uint32_t *dst, const unsigned char *src, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len / 4; i++)
+ dst[i] = be32dec(src + i * 4);
+}
+
+#endif
+
+/* Elementary functions used by SHA256 */
+#define Ch(x, y, z) ((x & (y ^ z)) ^ z)
+#define Maj(x, y, z) ((x & (y | z)) | (y & z))
+#define SHR(x, n) (x >> n)
+#define ROTR(x, n) ((x >> n) | (x << (32 - n)))
+#define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
+#define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
+#define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3))
+#define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10))
+
+/* SHA256 round function */
+#define RND(a, b, c, d, e, f, g, h, k) \
+ t0 = h + S1(e) + Ch(e, f, g) + k; \
+ t1 = S0(a) + Maj(a, b, c); \
+ d += t0; \
+ h = t0 + t1;
+
+/* Adjusted round function for rotating state */
+#define RNDr(S, W, i, k) \
+ RND(S[(64 - i) % 8], S[(65 - i) % 8], \
+ S[(66 - i) % 8], S[(67 - i) % 8], \
+ S[(68 - i) % 8], S[(69 - i) % 8], \
+ S[(70 - i) % 8], S[(71 - i) % 8], \
+ W[i] + k)
+
+/*
+ * SHA256 block compression function. The 256-bit state is transformed via
+ * the 512-bit input block to produce a new state.
+ */
+static void
+SHA256_Transform(uint32_t * state, const unsigned char block[64])
+{
+ uint32_t W[64];
+ uint32_t S[8];
+ uint32_t t0, t1;
+ int i;
+
+ /* 1. Prepare message schedule W. */
+ be32dec_vect(W, block, 64);
+ for (i = 16; i < 64; i++)
+ W[i] = s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16];
+
+ /* 2. Initialize working variables. */
+ memcpy(S, state, 32);
+
+ /* 3. Mix. */
+ RNDr(S, W, 0, 0x428a2f98);
+ RNDr(S, W, 1, 0x71374491);
+ RNDr(S, W, 2, 0xb5c0fbcf);
+ RNDr(S, W, 3, 0xe9b5dba5);
+ RNDr(S, W, 4, 0x3956c25b);
+ RNDr(S, W, 5, 0x59f111f1);
+ RNDr(S, W, 6, 0x923f82a4);
+ RNDr(S, W, 7, 0xab1c5ed5);
+ RNDr(S, W, 8, 0xd807aa98);
+ RNDr(S, W, 9, 0x12835b01);
+ RNDr(S, W, 10, 0x243185be);
+ RNDr(S, W, 11, 0x550c7dc3);
+ RNDr(S, W, 12, 0x72be5d74);
+ RNDr(S, W, 13, 0x80deb1fe);
+ RNDr(S, W, 14, 0x9bdc06a7);
+ RNDr(S, W, 15, 0xc19bf174);
+ RNDr(S, W, 16, 0xe49b69c1);
+ RNDr(S, W, 17, 0xefbe4786);
+ RNDr(S, W, 18, 0x0fc19dc6);
+ RNDr(S, W, 19, 0x240ca1cc);
+ RNDr(S, W, 20, 0x2de92c6f);
+ RNDr(S, W, 21, 0x4a7484aa);
+ RNDr(S, W, 22, 0x5cb0a9dc);
+ RNDr(S, W, 23, 0x76f988da);
+ RNDr(S, W, 24, 0x983e5152);
+ RNDr(S, W, 25, 0xa831c66d);
+ RNDr(S, W, 26, 0xb00327c8);
+ RNDr(S, W, 27, 0xbf597fc7);
+ RNDr(S, W, 28, 0xc6e00bf3);
+ RNDr(S, W, 29, 0xd5a79147);
+ RNDr(S, W, 30, 0x06ca6351);
+ RNDr(S, W, 31, 0x14292967);
+ RNDr(S, W, 32, 0x27b70a85);
+ RNDr(S, W, 33, 0x2e1b2138);
+ RNDr(S, W, 34, 0x4d2c6dfc);
+ RNDr(S, W, 35, 0x53380d13);
+ RNDr(S, W, 36, 0x650a7354);
+ RNDr(S, W, 37, 0x766a0abb);
+ RNDr(S, W, 38, 0x81c2c92e);
+ RNDr(S, W, 39, 0x92722c85);
+ RNDr(S, W, 40, 0xa2bfe8a1);
+ RNDr(S, W, 41, 0xa81a664b);
+ RNDr(S, W, 42, 0xc24b8b70);
+ RNDr(S, W, 43, 0xc76c51a3);
+ RNDr(S, W, 44, 0xd192e819);
+ RNDr(S, W, 45, 0xd6990624);
+ RNDr(S, W, 46, 0xf40e3585);
+ RNDr(S, W, 47, 0x106aa070);
+ RNDr(S, W, 48, 0x19a4c116);
+ RNDr(S, W, 49, 0x1e376c08);
+ RNDr(S, W, 50, 0x2748774c);
+ RNDr(S, W, 51, 0x34b0bcb5);
+ RNDr(S, W, 52, 0x391c0cb3);
+ RNDr(S, W, 53, 0x4ed8aa4a);
+ RNDr(S, W, 54, 0x5b9cca4f);
+ RNDr(S, W, 55, 0x682e6ff3);
+ RNDr(S, W, 56, 0x748f82ee);
+ RNDr(S, W, 57, 0x78a5636f);
+ RNDr(S, W, 58, 0x84c87814);
+ RNDr(S, W, 59, 0x8cc70208);
+ RNDr(S, W, 60, 0x90befffa);
+ RNDr(S, W, 61, 0xa4506ceb);
+ RNDr(S, W, 62, 0xbef9a3f7);
+ RNDr(S, W, 63, 0xc67178f2);
+
+ /* 4. Mix local working variables into global state */
+ for (i = 0; i < 8; i++)
+ state[i] += S[i];
+}
+
+static unsigned char PAD[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* Add padding and terminating bit-count. */
+static void
+SHA256_Pad(SHA256_CTX * ctx)
+{
+ unsigned char len[8];
+ uint32_t r, plen;
+
+ /*
+ * Convert length to a vector of bytes -- we do this now rather
+ * than later because the length will change after we pad.
+ */
+ be32enc_vect(len, ctx->count, 8);
+
+ /* Add 1--64 bytes so that the resulting length is 56 mod 64 */
+ r = (ctx->count[1] >> 3) & 0x3f;
+ plen = (r < 56) ? (56 - r) : (120 - r);
+ SHA256_Update(ctx, PAD, (size_t)plen);
+
+ /* Add the terminating bit-count */
+ SHA256_Update(ctx, len, 8);
+}
+
+/* SHA-256 initialization. Begins a SHA-256 operation. */
+void
+SHA256_Init(SHA256_CTX * ctx)
+{
+
+ /* Zero bits processed so far */
+ ctx->count[0] = ctx->count[1] = 0;
+
+ /* Magic initialization constants */
+ ctx->state[0] = 0x6A09E667;
+ ctx->state[1] = 0xBB67AE85;
+ ctx->state[2] = 0x3C6EF372;
+ ctx->state[3] = 0xA54FF53A;
+ ctx->state[4] = 0x510E527F;
+ ctx->state[5] = 0x9B05688C;
+ ctx->state[6] = 0x1F83D9AB;
+ ctx->state[7] = 0x5BE0CD19;
+}
+
+/* Add bytes into the hash */
+void
+SHA256_Update(SHA256_CTX * ctx, const void *in, size_t len)
+{
+ uint32_t bitlen[2];
+ uint32_t r;
+ const unsigned char *src = in;
+
+ /* Number of bytes left in the buffer from previous updates */
+ r = (ctx->count[1] >> 3) & 0x3f;
+
+ /* Convert the length into a number of bits */
+ bitlen[1] = ((uint32_t)len) << 3;
+ bitlen[0] = (uint32_t)(len >> 29);
+
+ /* Update number of bits */
+ if ((ctx->count[1] += bitlen[1]) < bitlen[1])
+ ctx->count[0]++;
+ ctx->count[0] += bitlen[0];
+
+ /* Handle the case where we don't need to perform any transforms */
+ if (len < 64 - r) {
+ memcpy(&ctx->buf[r], src, len);
+ return;
+ }
+
+ /* Finish the current block */
+ memcpy(&ctx->buf[r], src, 64 - r);
+ SHA256_Transform(ctx->state, ctx->buf);
+ src += 64 - r;
+ len -= 64 - r;
+
+ /* Perform complete blocks */
+ while (len >= 64) {
+ SHA256_Transform(ctx->state, src);
+ src += 64;
+ len -= 64;
+ }
+
+ /* Copy left over data into buffer */
+ memcpy(ctx->buf, src, len);
+}
+
+/*
+ * SHA-256 finalization. Pads the input data, exports the hash value,
+ * and clears the context state.
+ */
+void
+SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx)
+{
+
+ /* Add padding */
+ SHA256_Pad(ctx);
+
+ /* Write the hash */
+ be32enc_vect(digest, ctx->state, 32);
+
+ /* Clear the context state */
+ memset((void *)ctx, 0, sizeof(*ctx));
+}
diff --git a/Radio/HW/BladeRF/common/src/str_queue.c b/Radio/HW/BladeRF/common/src/str_queue.c
new file mode 100644
index 0000000..d5b14cd
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/str_queue.c
@@ -0,0 +1,193 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2014 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <string.h>
+#include <stdlib.h>
+#include "str_queue.h"
+#include "host_config.h"
+
+struct str_queue_entry {
+ char *str;
+ struct str_queue_entry *next;
+};
+
+void str_queue_init(struct str_queue *q)
+{
+ memset(q, 0, sizeof(q[0]));
+}
+
+void str_queue_deinit(struct str_queue *q)
+{
+ char *str;
+
+ do {
+ str = str_queue_deq(q);
+ free(str);
+ } while (str != NULL);
+
+ q->head = NULL;
+ q->tail = NULL;
+}
+
+int str_queue_enq(struct str_queue *q, const char *str)
+{
+ struct str_queue_entry *entry = malloc(sizeof(entry[0]));
+ if (entry == NULL) {
+ return -1;
+ }
+
+ entry->str = strdup(str);
+ if (entry->str == NULL) {
+ free(entry);
+ return -1;
+ }
+ entry->next = NULL;
+
+ if (q->head == NULL) {
+ q->head = entry;
+ }
+
+ if (q->tail != NULL) {
+ q->tail->next = entry;
+ }
+
+ q->tail = entry;
+ return 0;
+}
+
+char * str_queue_deq(struct str_queue *q)
+{
+ char *ret;
+ struct str_queue_entry *entry;
+
+ entry = q->head;
+ if (entry == NULL) {
+ ret = NULL;
+ } else {
+ q->head = entry->next;
+ if (q->head == NULL) {
+ q->tail = NULL;
+ }
+
+ ret = entry->str;
+ free(entry);
+ }
+
+ return ret;
+}
+
+bool str_queue_empty(struct str_queue *q)
+{
+ return q->head == NULL || q->tail == NULL;
+}
+
+#if TEST_STR_QUEUE
+#include <stdio.h>
+
+#define STR_NULL "(null)"
+#define STR_1 "This is test string one."
+#define STR_2 "A second string queue test string"
+#define STR_3 "String thr33, this be"
+
+#define CHECK_EMPTY(q) do { \
+ if (str_queue_empty(q) != true) { \
+ fprintf(stderr, "Queue not empty!\n"); \
+ return EXIT_FAILURE; \
+ } \
+ } while (0)
+
+#define CHECK_NONEMPTY(q) do { \
+ if (str_queue_empty(q) != false) { \
+ fprintf(stderr, "Queue unexpectedly empty!\n"); \
+ return EXIT_FAILURE; \
+ } \
+ } while (0)
+
+#define ENQ(q, str) do { \
+ int status_ = str_queue_enq(q, str); \
+ if (status_ != 0) { \
+ fprintf(stderr, "Failed to enqueue %s\n", str); \
+ return EXIT_FAILURE; \
+ } else { \
+ printf("Enqueued: %s\n", str); \
+ } \
+ } while (0)
+
+#define DEQ(q, exp) do { \
+ char * str_; \
+ bool failed_; \
+ if (!strcmp(exp, STR_NULL)) { \
+ CHECK_EMPTY(q); \
+ } else { \
+ CHECK_NONEMPTY(q); \
+ } \
+ str_ = str_queue_deq(q); \
+ printf("Dequeued: %s\n", str_); \
+ if (str_ == NULL) { \
+ failed_ = strcmp(exp, STR_NULL) != 0; \
+ } else { \
+ failed_ = strcmp(str_, exp) != 0; \
+ } \
+ if (failed_) { \
+ fprintf(stderr, "Dequeue failed.\n"); \
+ return EXIT_FAILURE; \
+ } \
+ } while (0)
+
+int main(void)
+{
+ char *str;
+ struct str_queue q;
+
+ str_queue_init(&q);
+ str = str_queue_deq(&q);
+
+
+ DEQ(&q, STR_NULL);
+ ENQ(&q, STR_1);
+ DEQ(&q, STR_1);
+ DEQ(&q, STR_NULL);
+ DEQ(&q, STR_NULL);
+
+ ENQ(&q, STR_1);
+ ENQ(&q, STR_2);
+ DEQ(&q, STR_1);
+ DEQ(&q, STR_2);
+ DEQ(&q, STR_NULL);
+
+ ENQ(&q, STR_1);
+ ENQ(&q, STR_2);
+ DEQ(&q, STR_1);
+ ENQ(&q, STR_3);
+ DEQ(&q, STR_2);
+ ENQ(&q, STR_1);
+ DEQ(&q, STR_3);
+ DEQ(&q, STR_1);
+ DEQ(&q, STR_NULL);
+ DEQ(&q, STR_NULL);
+
+ str_queue_deinit(&q);
+ return 0;
+}
+#endif
diff --git a/Radio/HW/BladeRF/common/src/windows/clock_gettime.c b/Radio/HW/BladeRF/common/src/windows/clock_gettime.c
new file mode 100644
index 0000000..803799e
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/windows/clock_gettime.c
@@ -0,0 +1,58 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2013 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef WIN32
+# error "This file is intended for use with WIN32 systems only."
+#endif
+
+#include <Windows.h>
+#include <errno.h>
+#include "clock_gettime.h"
+#include "ptw32_timespec.h"
+
+int clock_gettime(clockid_t clk_id, struct timespec *tp)
+{
+ BOOL success;
+ FILETIME file_time;
+ SYSTEMTIME system_time;
+
+ if (clk_id != CLOCK_REALTIME) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ GetSystemTime(&system_time);
+ success = SystemTimeToFileTime(&system_time, &file_time);
+
+ if (!success) {
+ /* For lack of a better or more compliant return value... */
+ errno = EINVAL;
+ return -1;
+ }
+
+ ptw32_filetime_to_timespec(&file_time, tp);
+
+ return 0;
+}
+
diff --git a/Radio/HW/BladeRF/common/src/windows/getopt_long.c b/Radio/HW/BladeRF/common/src/windows/getopt_long.c
new file mode 100644
index 0000000..7a683fb
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/windows/getopt_long.c
@@ -0,0 +1,326 @@
+/** @file getopt_long.c
+ ** @brief getopt_long - Definition
+ ** @author Andrea Vedaldi
+ **/
+
+/*
+Copyright (C) 2007-12 Andrea Vedaldi and Brian Fulkerson.
+All rights reserved.
+
+This file is part of the VLFeat library and is made available under
+the terms of the BSD license (see legal/licenses/LICENSE.BSD.vlfeat).
+
+*/
+
+/**
+@file getopt_long.h
+@brief getopt_long
+@author Andrea Vedaldi
+
+This is a drop-in replacament of GNU getopt_long meant to be used
+on platforms that do not support such functionality.
+**/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "getopt.h"
+
+int opterr = 1 ;
+int optind = 1 ;
+int optopt ;
+char * optarg ;
+int optreset ;
+
+#define BADCH '?'
+#define BADARG ':'
+#define EEND -1
+#define EMSG ""
+
+/** @brief Parse long options (BSD style)
+ ** @param argc number of arguments.
+ ** @param argv pointer to the vector of arguments.
+ ** @param optstring list of abbreviated options
+ ** @param longopts list of long options.
+ ** @param longindex index of current option in @a longopts.
+ ** @return the code of the next option.
+ **
+ ** This function extract long and short options from the argument
+ ** list @a argv of @a argc entries.
+ **
+ ** A short options sequence is introduced by a single dash character
+ ** @c -. Each short option is described by a single character in the
+ ** string @a optstring, possibly followed by a @c : character to
+ ** denote a (mandatory) argument of the short option. A short option
+ ** with an argument cannot appear in the middle of a short option
+ ** sequence, but only at the end.
+ **
+ ** A long option is introduced by a double dash @c --. Each long
+ ** option is described by an instance of the ::option structure in
+ ** the @a longopts table (the last entry must be filled with zeroes
+ ** to denote the end).
+ **
+ ** Illegal options and missing arguments cause the function to skip
+ ** the option and return '?'. If ::opterr is @c true (default), the
+ ** function prints an error message to @a stderr. Finally, if @a
+ ** optstring has a leading @c :, then error messages are suppressed
+ ** and a missing argument causes @a : to be returned.
+ **
+ ** @remark The function is currently <em>not</em> thread safe.
+ **/
+
+VL_EXPORT int
+getopt_long(int argc, char *const argv[],
+ const char *optstring,
+ const struct option * longopts,
+ int *longindex)
+{
+ static char *place = EMSG; /* option letter processing */
+ static int optbegin = 0 ;
+ static int optend = 0 ;
+ const char *oli; /* option letter list index */
+ int has_colon = 0 ;
+ int ret_val = 0 ;
+
+ /*
+ A semicolon at the beginning of optstring has a special meaning.
+ If we find one, we annote and remove it.
+ */
+ has_colon = optstring && optstring[0] == ':' ;
+ if (has_colon) ++ optstring ;
+
+ /*
+ Here we are either processing a short option sequence or
+ we start processing a new option. This is indicated by optreset.
+ */
+
+ if (optreset || *place == '\0') {
+
+ /* ---------------------------------------------------------------
+ * Look for next short/long option
+ * ------------------------------------------------------------ */
+ optreset = 0 ;
+
+ /* no more arguments ? */
+ if (optind >= argc) {
+ place = EMSG ;
+ return -1 ;
+ }
+
+ /* next argument that may hold an option */
+ optbegin = optind ;
+
+ /* ---------------------------------------------------------------
+ * Look for an option to parse
+ * ------------------------------------------------------------ */
+
+ parse_option_at_optbegin :
+
+ /* place points to the candidate option */
+ place = argv [optbegin] ;
+
+ /* an option is introduced by '-' */
+ if (place [0] != '-') {
+ /* this argument is not an option: try next argument */
+ ++ optbegin ;
+ if (optbegin >= argc) {
+ /* no more arguments to look for options */
+ place = EMSG ;
+ return -1 ;
+ }
+ goto parse_option_at_optbegin ;
+ }
+
+ /* consume leading `-' */
+ ++ place ;
+
+ /* assume the option is composed of one argument only */
+ optend = optbegin + 1 ;
+
+ /* assume no argument */
+ optarg = 0 ;
+
+ /* ---------------------------------------------------------------
+ * option `--'
+ * ------------------------------------------------------------ */
+
+ /* this special option (void long option) ends the option processing */
+ if (place[0] &&
+ place[0] == '-' &&
+ place[1] == '\0') {
+
+ optind = optend ;
+ place = EMSG ;
+ ret_val = -1 ;
+ goto done_option ;
+ }
+
+ /* ---------------------------------------------------------------
+ * long option
+ * ------------------------------------------------------------ */
+
+ if (place[0] &&
+ place[0] == '-' &&
+ place[1] ) {
+
+ size_t namelen ;
+ int i ;
+
+ /* consume second `-' */
+ ++ place ;
+
+ /* count characters before `=' */
+ namelen = strcspn(place, "=") ;
+
+ /* scan longopts for this option */
+ for (i = 0 ; longopts[i].name != NULL ; ++ i) {
+
+ if (strlen ( longopts[i].name) == namelen &&
+ strncmp (place, longopts[i].name, namelen) == 0 ) {
+
+ /* save back long option index */
+ if (longindex) *longindex = i ;
+
+ /* process long option argument */
+ if (longopts[i].has_arg == required_argument ||
+ longopts[i].has_arg == optional_argument) {
+
+ /* --option=value style */
+ if (place[namelen] == '=') {
+ optarg = place + namelen + 1 ;
+ }
+
+ /* --option value style (only required_argument) */
+ else if (longopts[i].has_arg == required_argument) {
+ /* missing argument ? */
+ if (optbegin >= argc - 1) {
+ if (! has_colon && opterr)
+ fprintf(stderr,
+ "%s: option requires an argument -- %s\n",
+ argv[0], place);
+ place = EMSG ;
+ ret_val = has_colon ? BADARG : BADCH ;
+ goto done_option ;
+ }
+ optarg = argv [optend] ;
+ ++ optend ;
+ }
+ }
+
+ /* determine return value */
+ if (longopts[i].flag == NULL) {
+ ret_val = longopts[i].val ;
+ }
+ else {
+ *longopts[i].flag = longopts[i].val;
+ ret_val = 0 ;
+ }
+
+ /* mark sequence closed */
+ place = EMSG ;
+ goto done_option ;
+ } /* if match */
+
+ } /* scan longoptions */
+
+ /* no matching option found */
+ if (! has_colon && opterr)
+ fprintf(stderr,
+ "%s: illegal option -- %s\n", argv[0], place) ;
+ place = EMSG ;
+ ret_val = BADCH ;
+ goto done_option ;
+ }
+ } /* end new option */
+
+ /* -----------------------------------------------------------------
+ * Finish short option sequence
+ * -------------------------------------------------------------- */
+ optopt = (int) *place++ ;
+
+ /* search charcater in option list */
+ oli = strchr(optstring, optopt);
+
+ /* short option not found */
+ if (!oli) {
+
+ if (! has_colon && opterr)
+ fprintf(stderr,
+ "%s: illegal option -- %c\n",
+ argv[0], optopt);
+
+ if (*place) {
+ /* more short options in the list */
+ return BADCH ;
+ }
+
+ else {
+ /* error occured as last option in the list */
+ place = EMSG ;
+ ret_val = BADCH ;
+ goto done_option ;
+ }
+ } /* end short option not found */
+
+ if (oli[1] != ':') {
+ /* short option with no argument */
+
+ if (*place) {
+ /* more short options in the list */
+ return optopt ;
+ }
+ else {
+ /* last option in the list */
+ place = EMSG ;
+ ret_val = optopt ;
+ goto done_option ;
+ }
+
+ } else {
+ /* short option with argument */
+
+ /* -ovalue style */
+ if (*place) {
+ optarg = place ;
+ place = EMSG ;
+ ret_val = optopt ;
+ goto done_option ;
+ }
+ /* -o value style: missing argument */
+ else if (optbegin >= argc - 1) {
+ if (! has_colon && opterr)
+ fprintf(stderr,
+ "%s: option requires an argument -- %c\n",
+ argv[0], optopt);
+ place = EMSG ;
+ ret_val = has_colon ? BADARG : BADCH ;
+ goto done_option ;
+ }
+
+ /* -o value style: process argument */
+ optarg = argv [optend] ;
+ ++ optend ;
+ place = EMSG ;
+ ret_val = optopt ;
+ goto done_option ;
+ } /* short with argument */
+
+ done_option :
+ {
+ int pos = optend - optbegin ; /* n of circular shifts */
+ int c = pos ;
+
+ while (c --) {
+ int i ;
+ char *tmp = argv [optend - 1] ;
+ for (i = optend - 1 ; i > optind ; -- i) {
+ ((char**)argv) [i] = argv [i-1] ;
+ }
+ ((char**)argv) [optind] = tmp ;
+ }
+ optind += pos ;
+ }
+
+ return ret_val ;
+}
diff --git a/Radio/HW/BladeRF/common/src/windows/gettimeofday.c b/Radio/HW/BladeRF/common/src/windows/gettimeofday.c
new file mode 100644
index 0000000..144ee2f
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/windows/gettimeofday.c
@@ -0,0 +1,49 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2023 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef WIN32
+#error "This file is intended for use with WIN32 systems only."
+#endif
+
+#include <windows.h>
+#include <stdint.h>
+int gettimeofday(struct timeval *tp, struct timezone *tzp)
+{
+ // Note: some broken versions only have 8 trailing zero's, the correct epoch
+ // has 9 trailing zero's
+ static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL);
+
+ SYSTEMTIME system_time;
+ FILETIME file_time;
+ uint64_t time;
+
+ GetSystemTime(&system_time);
+ SystemTimeToFileTime(&system_time, &file_time);
+ time = ((uint64_t)file_time.dwLowDateTime);
+ time += ((uint64_t)file_time.dwHighDateTime) << 32;
+
+ tp->tv_sec = (long)((time - EPOCH) / 10000000L);
+ tp->tv_usec = (long)(system_time.wMilliseconds * 1000);
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/common/src/windows/mkdtemp.c b/Radio/HW/BladeRF/common/src/windows/mkdtemp.c
new file mode 100644
index 0000000..0e2f804
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/windows/mkdtemp.c
@@ -0,0 +1,250 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/Nuand/bladeRF
+ *
+ * Copyright (c) 2018 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// _CRT_RAND_S must be defined before including stdlib.h, to enable rand_s
+#define _CRT_RAND_S
+#include <stdlib.h>
+
+#ifdef WINDOWS_MKDTEMP_TEST_SUITE
+// Running in standalone test mode
+
+#include <stdbool.h>
+#include <stdio.h>
+#define __debug(...) fprintf(stderr, __VA_ARGS__)
+
+#ifndef WIN32
+// Running standalone test on non-Windows OS
+#include <time.h>
+#include <unistd.h>
+#define errno_t int
+errno_t rand_s(unsigned int *randomValue)
+{
+ *randomValue = rand();
+ return 0;
+}
+#endif // WIN32
+
+#else
+// Building as part of a library
+
+#ifndef WIN32
+#error "This file is intended for use with WIN32 systems only."
+#endif // WIN32
+
+#define __debug(...)
+
+#endif // WINDOWS_MKDTEMP_TEST_SUITE
+
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include "host_config.h"
+
+// The concepts of F_OK and S_IRWXU do not exist on Win32
+#ifndef F_OK
+#define F_OK 0
+#endif
+
+#ifndef S_IRWXU
+#define S_IRWXU 00700
+#endif
+
+/* h/t https://stackoverflow.com/a/14598879 */
+static int random_number(int min_num, int max_num)
+{
+ int result = 0, low_num = 0, hi_num = 0;
+ unsigned int randomValue;
+
+ if (min_num < max_num) {
+ low_num = min_num;
+ hi_num = max_num + 1; // include max_num in output
+ } else {
+ low_num = max_num + 1; // include max_num in output
+ hi_num = min_num;
+ }
+
+ // Use rand_s() on Windows, so that we don't have to deal with srand()
+ errno_t err = rand_s(&randomValue);
+ if (0 != err) {
+ return -1;
+ }
+
+ result = (randomValue % (hi_num - low_num)) + low_num;
+ return result;
+}
+
+char *mkdtemp(char *template)
+{
+ size_t const TEMPL_LEN = 6;
+ char const TEMPL_CHAR = 'X';
+
+ if (strlen(template) <= TEMPL_LEN) {
+ // template is too short
+ errno = EINVAL;
+ goto error;
+ }
+
+ // Loop through the end of the template, replacing 'X' with random char
+ for (size_t i = strlen(template) - TEMPL_LEN; i < strlen(template); ++i) {
+ // The last TEMPL_LEN characters MUST be 'X'
+ if (template[i] != TEMPL_CHAR) {
+ errno = EINVAL;
+ goto error;
+ }
+
+ // Pick a random letter
+ if (random_number(0, 1)) {
+ template[i] = (char)random_number('A', 'Z');
+ } else {
+ template[i] = (char)random_number('a', 'z');
+ }
+ }
+
+ // Error out if the file already exists
+ if (access(template, F_OK) != -1) {
+ __debug("%s: failed: %s exists\n", __FUNCTION__, template);
+ errno = EEXIST;
+ goto error;
+ }
+
+ // Try to create the directory...
+ if (0 != mkdir(template, S_IRWXU)) {
+ int errsv = errno;
+ __debug("%s: mkdir() failed: %s\n", __FUNCTION__, strerror(errsv));
+ goto error;
+ }
+
+ // Success!
+ errno = 0;
+ return template;
+
+error:
+ return NULL;
+}
+
+#ifdef WINDOWS_MKDTEMP_TEST_SUITE
+/**
+ * These functions are intended to verify proper operation of the test suite.
+ */
+static bool test(char *template, bool expect_success)
+{
+ char *rv = mkdtemp(template);
+
+ if (NULL == rv) {
+ int errsv = errno;
+ printf("%s: mkdtemp failed: %s\n", __FUNCTION__, strerror(errsv));
+ return (false == expect_success);
+ } else {
+ printf("%s: mkdtemp created: %s\n", __FUNCTION__, rv);
+
+ if (0 != rmdir(rv)) {
+ int errsv = errno;
+ printf("%s: rmdir failed: %s\n", __FUNCTION__, strerror(errsv));
+ }
+
+ return (true == expect_success);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+#ifndef WIN32
+ srand(time(NULL));
+#endif // WIN32
+
+ int success = 0, failure = 0;
+
+ // Normal: should pass
+ char template1[] = "/tmp/asdf.XXXXXX";
+ if (test(template1, true)) {
+ printf("*** Test case 1: PASS\n");
+ ++success;
+ } else {
+ printf("*** Test case 1: FAIL\n");
+ ++failure;
+ }
+
+ // Too short: should fail
+ char template2[] = "XXXXXX";
+ if (test(template2, false)) {
+ printf("*** Test case 2: PASS\n");
+ ++success;
+ } else {
+ printf("*** Test case 2: FAIL\n");
+ ++failure;
+ }
+
+ // Not enough replacement Xs: should fail
+ char template3[] = "/tmp/asdf.XXXXX";
+ if (test(template3, false)) {
+ printf("*** Test case 3: PASS\n");
+ ++success;
+ } else {
+ printf("*** Test case 3: FAIL\n");
+ ++failure;
+ }
+
+ // Make sure it only replaces the end: should pass
+ char template4[] = "/tmp/asdfXXXXXX.XXXXXX";
+ if (test(template4, true)) {
+ printf("*** Test case 4: PASS\n");
+ ++success;
+ } else {
+ printf("*** Test case 4: FAIL\n");
+ ++failure;
+ }
+
+ // Really long: should fail
+ char template5[] = "/tmp/asdfaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaXXXXXX";
+ if (test(template5, false)) {
+ printf("*** Test case 5: PASS\n");
+ ++success;
+ } else {
+ printf("*** Test case 5: FAIL\n");
+ ++failure;
+ }
+
+ // Unwriteable path: should fail
+ char template6[] = "/asdfkjavblkjadv/asdf.XXXX";
+ if (test(template6, false)) {
+ printf("*** Test case 6: PASS\n");
+ ++success;
+ } else {
+ printf("*** Test case 6: FAIL\n");
+ ++failure;
+ }
+
+ printf("TEST SUMMARY: Success=%d, Failure=%d\n", success, failure);
+
+ return failure;
+}
+#endif // WINDOWS_MKDTEMP_TEST_SUITE
diff --git a/Radio/HW/BladeRF/common/src/windows/nanosleep.c b/Radio/HW/BladeRF/common/src/windows/nanosleep.c
new file mode 100644
index 0000000..12d470a
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/windows/nanosleep.c
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2018 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef WIN32
+#error "This file is intended for use with WIN32 systems only."
+#endif
+
+#include "nanosleep.h"
+#include <windows.h>
+
+int nanosleep(const struct timespec *req, struct timespec *rem)
+{
+ DWORD sleep_ms;
+
+ sleep_ms = ((DWORD)req->tv_sec * 1000) + ((DWORD)req->tv_nsec / 1000000);
+
+ Sleep(sleep_ms);
+
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/common/src/windows/setenv.c b/Radio/HW/BladeRF/common/src/windows/setenv.c
new file mode 100644
index 0000000..1f7eb8c
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/windows/setenv.c
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/Nuand/bladeRF
+ *
+ * Copyright (c) 2018 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef WIN32
+#error "This file is intended for use with WIN32 systems only."
+#endif // WIN32
+
+#include <stdlib.h>
+
+int setenv(const char *name, const char *value, int overwrite)
+{
+ errno_t rv = 0;
+ size_t envsize = 0;
+
+ if (!overwrite) {
+ // Test for existence
+ rv = getenv_s(&envsize, NULL, 0, name);
+ if (rv != 0 || envsize != 0) {
+ return rv;
+ }
+ }
+ return _putenv_s(name, value);
+}
+
+int unsetenv(const char *name)
+{
+ return _putenv_s(name, "");
+}
diff --git a/Radio/HW/BladeRF/common/thirdparty/ad936x/ad9361.c b/Radio/HW/BladeRF/common/thirdparty/ad936x/ad9361.c
new file mode 100644
index 0000000..f63ef84
--- /dev/null
+++ b/Radio/HW/BladeRF/common/thirdparty/ad936x/ad9361.c
@@ -0,0 +1,7218 @@
+/***************************************************************************//**
+ * @file ad9361.c
+ * @brief Implementation of AD9361 Driver.
+********************************************************************************
+ * Copyright 2014-2015(c) Analog Devices, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * - Neither the name of Analog Devices, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * - The use of this software may or may not infringe the patent rights
+ * of one or more patent holders. This license does not release you
+ * from the requirement that you obtain separate licenses from these
+ * patent holders to use this software.
+ * - Use of the software either in source or binary form, must be run
+ * on or directly connected to an Analog Devices Inc. component.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*******************************************************************************/
+
+/******************************************************************************/
+/***************************** Include Files **********************************/
+/******************************************************************************/
+#include "host_config.h"
+#if !defined(BLADERF_OS_FREEBSD) && !defined(BLADERF_OS_OSX)
+#include <malloc.h>
+#endif // BLADERF_OS_FREEBSD + BLADERF_OS_OSX
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include "ad9361.h"
+#include "platform.h"
+#include "util.h"
+#include "config.h"
+
+/* Used for static code size optimization: please see config.h */
+const bool has_split_gt = HAVE_SPLIT_GAIN_TABLE;
+const bool have_tdd_tables = HAVE_TDD_SYNTH_TABLE;
+
+#define SYNTH_LUT_SIZE 53
+
+static const struct SynthLUT SynthLUT_FDD[LUT_FTDD_ENT][SYNTH_LUT_SIZE] = {
+ {
+ {12605, 13, 1, 4, 2, 15, 12, 7, 14, 6, 14, 5, 15}, /* 40 MHz */
+ {12245, 13, 1, 4, 2, 15, 12, 7, 14, 6, 14, 5, 15},
+ {11906, 13, 1, 4, 2, 15, 12, 7, 15, 6, 14, 5, 15},
+ {11588, 13, 1, 4, 2, 15, 12, 8, 15, 6, 14, 5, 15},
+ {11288, 13, 1, 4, 2, 15, 12, 8, 15, 6, 14, 5, 15},
+ {11007, 13, 1, 4, 2, 15, 12, 9, 15, 6, 14, 5, 15},
+ {10742, 13, 1, 4, 2, 15, 12, 9, 15, 6, 14, 5, 15},
+ {10492, 13, 1, 6, 2, 15, 12, 10, 15, 6, 14, 5, 15},
+ {10258, 13, 1, 6, 2, 15, 12, 10, 15, 6, 14, 5, 15},
+ {10036, 13, 1, 6, 2, 15, 12, 11, 15, 6, 14, 5, 15},
+ {9827, 13, 1, 6, 2, 14, 12, 11, 15, 6, 14, 5, 15},
+ {9631, 13, 1, 6, 2, 13, 12, 12, 15, 6, 14, 5, 15},
+ {9445, 13, 1, 6, 2, 12, 12, 12, 15, 6, 14, 5, 15},
+ {9269, 13, 1, 6, 2, 12, 12, 13, 15, 6, 14, 5, 15},
+ {9103, 13, 1, 6, 2, 12, 12, 13, 15, 6, 14, 5, 15},
+ {8946, 13, 1, 6, 2, 12, 12, 14, 15, 6, 14, 5, 15},
+ {8797, 12, 1, 7, 2, 12, 12, 13, 15, 6, 14, 5, 15},
+ {8655, 12, 1, 7, 2, 12, 12, 14, 15, 6, 14, 5, 15},
+ {8520, 12, 1, 7, 2, 12, 12, 14, 15, 6, 14, 5, 15},
+ {8392, 12, 1, 7, 2, 12, 12, 15, 15, 6, 14, 5, 15},
+ {8269, 12, 1, 7, 2, 12, 12, 15, 15, 6, 14, 5, 15},
+ {8153, 12, 1, 7, 2, 12, 12, 16, 15, 6, 14, 5, 15},
+ {8041, 12, 1, 7, 2, 13, 12, 16, 15, 6, 14, 5, 15},
+ {7934, 11, 1, 7, 2, 12, 12, 16, 15, 6, 14, 5, 15},
+ {7831, 11, 1, 7, 2, 12, 12, 16, 15, 6, 14, 5, 15},
+ {7733, 10, 1, 7, 3, 13, 12, 16, 15, 6, 14, 5, 15},
+ {7638, 10, 1, 7, 2, 12, 12, 16, 15, 6, 14, 5, 15},
+ {7547, 10, 1, 7, 2, 12, 12, 17, 15, 6, 14, 5, 15},
+ {7459, 10, 1, 7, 2, 12, 12, 17, 15, 6, 14, 5, 15},
+ {7374, 10, 2, 7, 3, 14, 13, 14, 15, 6, 14, 5, 15},
+ {7291, 10, 2, 7, 3, 14, 13, 14, 15, 6, 14, 5, 15},
+ {7212, 10, 2, 7, 3, 14, 13, 14, 15, 6, 14, 5, 15},
+ {7135, 10, 2, 7, 3, 14, 13, 15, 15, 7, 14, 5, 15},
+ {7061, 10, 2, 7, 3, 14, 13, 15, 15, 6, 14, 5, 15},
+ {6988, 10, 1, 7, 3, 12, 14, 20, 15, 6, 14, 5, 15},
+ {6918, 9, 2, 7, 3, 14, 13, 15, 15, 6, 14, 5, 15},
+ {6850, 9, 2, 7, 3, 14, 13, 15, 15, 6, 14, 5, 15},
+ {6784, 9, 2, 7, 2, 13, 13, 15, 15, 6, 14, 5, 15},
+ {6720, 9, 2, 7, 2, 13, 13, 16, 15, 6, 14, 5, 15},
+ {6658, 8, 2, 7, 3, 14, 13, 15, 15, 6, 14, 5, 15},
+ {6597, 8, 2, 7, 2, 13, 13, 15, 15, 6, 14, 5, 15},
+ {6539, 8, 2, 7, 2, 13, 13, 15, 15, 6, 14, 5, 15},
+ {6482, 8, 2, 7, 2, 13, 13, 16, 15, 6, 14, 5, 15},
+ {6427, 7, 2, 7, 3, 14, 13, 15, 15, 6, 14, 5, 15},
+ {6373, 7, 2, 7, 3, 15, 13, 15, 15, 6, 14, 5, 15},
+ {6321, 7, 2, 7, 3, 15, 13, 15, 15, 6, 14, 5, 15},
+ {6270, 7, 2, 7, 3, 15, 13, 16, 15, 6, 14, 5, 15},
+ {6222, 7, 2, 7, 3, 15, 13, 16, 15, 6, 14, 5, 15},
+ {6174, 6, 2, 7, 3, 15, 13, 15, 15, 6, 14, 5, 15},
+ {6128, 6, 2, 7, 3, 15, 13, 15, 15, 6, 14, 5, 15},
+ {6083, 6, 2, 7, 3, 15, 13, 16, 15, 6, 14, 5, 15},
+ {6040, 6, 2, 7, 3, 15, 13, 16, 15, 6, 14, 5, 15},
+ {5997, 6, 2, 7, 3, 15, 13, 16, 15, 6, 14, 5, 15},
+ }, {
+ {12605, 13, 1, 4, 2, 15, 12, 13, 15, 12, 12, 5, 14}, /* 60 MHz */
+ {12245, 13, 1, 4, 2, 15, 12, 13, 15, 12, 12, 5, 14},
+ {11906, 13, 1, 4, 2, 15, 12, 13, 15, 13, 12, 5, 13},
+ {11588, 13, 1, 4, 2, 15, 12, 14, 15, 13, 12, 5, 13},
+ {11288, 13, 1, 5, 2, 15, 12, 15, 15, 13, 12, 5, 13},
+ {11007, 13, 1, 5, 2, 15, 12, 16, 15, 13, 12, 5, 13},
+ {10742, 13, 1, 5, 2, 15, 12, 16, 15, 12, 12, 5, 14},
+ {10492, 13, 1, 6, 2, 15, 12, 17, 15, 12, 12, 5, 14},
+ {10258, 13, 1, 6, 2, 15, 12, 18, 15, 13, 12, 5, 13},
+ {10036, 13, 1, 6, 2, 15, 12, 19, 15, 13, 12, 5, 13},
+ {9827, 13, 1, 6, 2, 14, 12, 20, 15, 13, 12, 5, 13},
+ {9631, 13, 1, 6, 2, 13, 12, 21, 15, 13, 12, 5, 13},
+ {9445, 13, 1, 6, 2, 12, 12, 22, 15, 13, 12, 5, 13},
+ {9269, 13, 1, 6, 2, 12, 12, 22, 15, 12, 12, 5, 14},
+ {9103, 13, 1, 6, 2, 12, 12, 23, 15, 13, 12, 5, 13},
+ {8946, 13, 1, 6, 2, 12, 12, 24, 15, 13, 12, 5, 13},
+ {8797, 12, 1, 7, 2, 12, 12, 24, 15, 13, 12, 5, 13},
+ {8655, 12, 1, 7, 2, 12, 12, 25, 15, 13, 12, 5, 13},
+ {8520, 12, 1, 7, 2, 12, 12, 25, 15, 13, 12, 5, 13},
+ {8392, 12, 1, 7, 2, 12, 12, 26, 15, 13, 12, 5, 13},
+ {8269, 12, 1, 7, 2, 12, 12, 27, 15, 13, 12, 5, 13},
+ {8153, 12, 1, 7, 2, 12, 12, 28, 15, 13, 12, 5, 13},
+ {8041, 12, 1, 7, 2, 13, 12, 29, 15, 13, 12, 5, 13},
+ {7934, 11, 1, 7, 2, 12, 12, 28, 15, 13, 12, 5, 13},
+ {7831, 11, 1, 7, 2, 12, 12, 29, 15, 13, 12, 5, 13},
+ {7733, 10, 1, 7, 3, 13, 12, 28, 15, 13, 12, 5, 13},
+ {7638, 10, 1, 7, 2, 12, 12, 29, 15, 13, 12, 5, 13},
+ {7547, 10, 1, 7, 2, 12, 12, 29, 15, 13, 12, 5, 13},
+ {7459, 10, 1, 7, 2, 12, 12, 30, 15, 13, 12, 5, 13},
+ {7374, 10, 2, 7, 3, 14, 13, 24, 15, 13, 12, 5, 13},
+ {7291, 10, 2, 7, 3, 14, 13, 25, 15, 13, 12, 5, 13},
+ {7212, 10, 2, 7, 3, 14, 13, 25, 15, 13, 12, 5, 13},
+ {7135, 10, 2, 7, 3, 14, 13, 26, 15, 13, 12, 5, 13},
+ {7061, 10, 2, 7, 3, 14, 13, 26, 15, 13, 12, 5, 13},
+ {6988, 10, 1, 7, 3, 12, 14, 35, 15, 13, 12, 5, 13},
+ {6918, 9, 1, 7, 3, 12, 14, 33, 15, 13, 12, 5, 13},
+ {6850, 9, 1, 7, 3, 12, 14, 34, 15, 13, 12, 5, 13},
+ {6784, 9, 1, 7, 2, 11, 14, 35, 15, 13, 12, 5, 13},
+ {6720, 9, 1, 7, 2, 11, 14, 35, 15, 13, 12, 5, 13},
+ {6658, 8, 2, 7, 3, 15, 13, 26, 15, 13, 12, 5, 13},
+ {6597, 8, 2, 7, 2, 15, 13, 27, 15, 13, 12, 5, 13},
+ {6539, 8, 2, 7, 2, 15, 13, 27, 15, 13, 12, 5, 13},
+ {6482, 8, 2, 7, 2, 15, 13, 28, 15, 13, 12, 5, 13},
+ {6427, 7, 2, 7, 3, 14, 13, 27, 15, 13, 12, 5, 13},
+ {6373, 7, 2, 7, 3, 15, 13, 27, 15, 13, 12, 5, 13},
+ {6321, 7, 2, 7, 3, 15, 13, 27, 15, 13, 12, 5, 13},
+ {6270, 7, 2, 7, 3, 15, 13, 28, 15, 13, 12, 5, 13},
+ {6222, 7, 2, 7, 3, 15, 13, 28, 15, 13, 12, 5, 13},
+ {6174, 6, 2, 7, 3, 15, 13, 27, 15, 13, 12, 5, 13},
+ {6128, 6, 2, 7, 3, 15, 13, 27, 15, 13, 12, 5, 13},
+ {6083, 6, 2, 7, 3, 15, 13, 28, 15, 13, 12, 5, 13},
+ {6040, 6, 2, 7, 3, 15, 13, 28, 15, 13, 12, 5, 13},
+ {5997, 6, 2, 7, 3, 15, 13, 29, 15, 13, 12, 5, 13},
+ }, {
+ {12605, 13, 1, 4, 2, 15, 12, 7, 15, 6, 13, 5, 14}, /* 80 MHz */
+ {12245, 13, 1, 4, 2, 15, 12, 7, 15, 6, 13, 5, 14},
+ {11906, 13, 1, 4, 2, 15, 12, 7, 15, 6, 13, 5, 14},
+ {11588, 13, 1, 4, 2, 15, 12, 7, 14, 6, 14, 4, 14},
+ {11288, 13, 1, 4, 2, 15, 12, 8, 15, 6, 13, 5, 14},
+ {11007, 13, 1, 4, 2, 15, 12, 8, 14, 6, 13, 5, 14},
+ {10742, 13, 1, 4, 2, 15, 12, 9, 15, 6, 13, 5, 14},
+ {10492, 13, 1, 6, 2, 15, 12, 9, 14, 6, 13, 5, 14},
+ {10258, 13, 1, 6, 2, 15, 12, 10, 15, 6, 13, 5, 14},
+ {10036, 13, 1, 6, 2, 15, 12, 10, 15, 6, 13, 5, 14},
+ {9827, 13, 1, 6, 2, 14, 12, 11, 15, 6, 13, 5, 14},
+ {9631, 13, 1, 6, 2, 13, 12, 11, 15, 6, 13, 5, 14},
+ {9445, 13, 1, 6, 2, 12, 12, 12, 15, 6, 13, 5, 14},
+ {9269, 13, 1, 6, 2, 12, 12, 12, 15, 6, 13, 5, 14},
+ {9103, 13, 1, 6, 2, 12, 12, 13, 15, 6, 13, 5, 14},
+ {8946, 13, 1, 6, 2, 12, 12, 13, 15, 6, 13, 5, 14},
+ {8797, 12, 1, 7, 2, 12, 12, 13, 15, 6, 13, 5, 14},
+ {8655, 12, 1, 7, 2, 12, 12, 14, 15, 6, 13, 5, 14},
+ {8520, 12, 1, 7, 2, 12, 12, 14, 15, 6, 13, 5, 14},
+ {8392, 12, 1, 7, 2, 12, 12, 15, 15, 7, 13, 5, 14},
+ {8269, 12, 1, 7, 2, 12, 12, 15, 15, 6, 13, 5, 14},
+ {8153, 12, 1, 7, 2, 12, 12, 15, 15, 6, 13, 5, 14},
+ {8041, 12, 1, 7, 2, 13, 12, 16, 15, 6, 13, 5, 14},
+ {7934, 11, 1, 7, 2, 12, 12, 15, 15, 6, 13, 5, 14},
+ {7831, 11, 1, 7, 2, 12, 12, 16, 15, 6, 13, 5, 14},
+ {7733, 10, 1, 7, 3, 13, 12, 15, 15, 6, 13, 5, 14},
+ {7638, 10, 1, 7, 2, 12, 12, 16, 15, 6, 13, 5, 14},
+ {7547, 10, 1, 7, 2, 12, 12, 16, 15, 6, 13, 5, 14},
+ {7459, 10, 1, 7, 2, 12, 12, 17, 15, 6, 13, 5, 14},
+ {7374, 10, 2, 7, 3, 14, 13, 13, 15, 6, 13, 5, 14},
+ {7291, 10, 2, 7, 3, 14, 13, 14, 15, 6, 13, 5, 14},
+ {7212, 10, 2, 7, 3, 14, 13, 14, 15, 6, 13, 5, 14},
+ {7135, 10, 2, 7, 3, 14, 13, 14, 15, 6, 13, 5, 14},
+ {7061, 10, 2, 7, 3, 14, 13, 15, 15, 6, 13, 5, 14},
+ {6988, 10, 1, 7, 3, 12, 14, 19, 15, 6, 13, 5, 14},
+ {6918, 9, 2, 7, 3, 14, 13, 14, 15, 6, 13, 5, 14},
+ {6850, 9, 2, 7, 3, 14, 13, 15, 15, 6, 13, 5, 14},
+ {6784, 9, 2, 7, 2, 13, 13, 15, 15, 6, 13, 5, 14},
+ {6720, 9, 2, 7, 2, 13, 13, 15, 15, 6, 13, 5, 14},
+ {6658, 8, 2, 7, 3, 14, 13, 15, 15, 6, 13, 5, 14},
+ {6597, 8, 2, 7, 2, 13, 13, 15, 15, 6, 13, 5, 14},
+ {6539, 8, 2, 7, 2, 13, 13, 15, 15, 6, 13, 5, 14},
+ {6482, 8, 2, 7, 2, 13, 13, 15, 15, 6, 13, 5, 14},
+ {6427, 7, 2, 7, 3, 14, 13, 15, 15, 6, 13, 5, 14},
+ {6373, 7, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14},
+ {6321, 7, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14},
+ {6270, 7, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14},
+ {6222, 7, 2, 7, 3, 15, 13, 16, 15, 6, 13, 5, 14},
+ {6174, 6, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14},
+ {6128, 6, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14},
+ {6083, 6, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14},
+ {6040, 6, 2, 7, 3, 15, 13, 16, 15, 6, 13, 5, 14},
+ {5997, 6, 2, 7, 3, 15, 13, 16, 15, 6, 13, 5, 14},
+ } };
+
+static const struct SynthLUT SynthLUT_TDD[LUT_FTDD_ENT][SYNTH_LUT_SIZE] = {
+ {
+ {12605, 13, 1, 4, 2, 15, 12, 27, 12, 15, 12, 4, 13}, /* 40 MHz */
+ {12245, 13, 1, 4, 2, 15, 12, 27, 12, 15, 12, 4, 13},
+ {11906, 13, 1, 4, 2, 15, 12, 26, 11, 15, 12, 4, 13},
+ {11588, 13, 1, 4, 2, 15, 12, 28, 12, 15, 12, 4, 13},
+ {11288, 13, 1, 4, 2, 15, 12, 30, 12, 15, 12, 4, 13},
+ {11007, 13, 1, 4, 2, 15, 12, 32, 12, 15, 12, 4, 13},
+ {10742, 13, 1, 4, 2, 15, 12, 33, 12, 15, 12, 4, 13},
+ {10492, 13, 1, 6, 2, 15, 12, 35, 12, 15, 12, 4, 13},
+ {10258, 13, 1, 6, 2, 15, 12, 37, 12, 15, 12, 4, 13},
+ {10036, 13, 1, 6, 2, 15, 12, 38, 12, 15, 12, 4, 13},
+ {9827, 13, 1, 6, 2, 14, 12, 40, 12, 15, 12, 4, 13},
+ {9631, 13, 1, 6, 2, 13, 12, 42, 12, 15, 12, 4, 13},
+ {9445, 13, 1, 6, 2, 12, 12, 44, 12, 15, 12, 4, 13},
+ {9269, 13, 1, 6, 2, 12, 12, 45, 12, 15, 12, 4, 13},
+ {9103, 13, 1, 6, 2, 12, 12, 47, 12, 15, 12, 4, 13},
+ {8946, 13, 1, 6, 2, 12, 12, 49, 12, 15, 12, 4, 13},
+ {8797, 12, 1, 7, 2, 12, 12, 48, 12, 15, 12, 4, 13},
+ {8655, 12, 1, 7, 2, 12, 12, 50, 12, 15, 12, 4, 13},
+ {8520, 12, 1, 7, 2, 12, 12, 51, 12, 15, 12, 4, 13},
+ {8392, 12, 1, 7, 2, 12, 12, 53, 12, 15, 12, 4, 13},
+ {8269, 12, 1, 7, 2, 12, 12, 55, 12, 15, 12, 4, 13},
+ {8153, 12, 1, 7, 2, 12, 12, 56, 12, 15, 12, 4, 13},
+ {8041, 12, 1, 7, 2, 13, 12, 58, 12, 15, 12, 4, 13},
+ {7934, 11, 1, 7, 2, 12, 12, 57, 12, 15, 12, 4, 13},
+ {7831, 11, 1, 7, 2, 12, 12, 58, 12, 15, 12, 4, 13},
+ {7733, 10, 1, 7, 3, 13, 12, 56, 12, 15, 12, 4, 13},
+ {7638, 10, 1, 7, 2, 12, 12, 58, 12, 15, 12, 4, 13},
+ {7547, 10, 1, 7, 2, 12, 12, 59, 12, 15, 12, 4, 13},
+ {7459, 10, 1, 7, 2, 12, 12, 61, 12, 15, 12, 4, 13},
+ {7374, 10, 2, 7, 3, 14, 13, 49, 12, 15, 12, 4, 13},
+ {7291, 10, 2, 7, 3, 14, 13, 50, 12, 15, 12, 4, 13},
+ {7212, 10, 2, 7, 3, 14, 13, 51, 12, 15, 12, 4, 13},
+ {7135, 10, 2, 7, 3, 14, 13, 52, 12, 15, 12, 4, 13},
+ {7061, 10, 2, 7, 3, 14, 13, 53, 12, 15, 12, 4, 13},
+ {6988, 10, 1, 7, 3, 12, 14, 63, 11, 14, 12, 3, 13},
+ {6918, 9, 2, 7, 3, 14, 13, 52, 12, 15, 12, 4, 13},
+ {6850, 9, 2, 7, 3, 14, 13, 53, 12, 15, 12, 4, 13},
+ {6784, 9, 2, 7, 2, 13, 13, 54, 12, 15, 12, 4, 13},
+ {6720, 9, 2, 7, 2, 13, 13, 56, 12, 15, 12, 4, 13},
+ {6658, 8, 2, 7, 3, 14, 13, 53, 12, 15, 12, 4, 13},
+ {6597, 8, 2, 7, 2, 13, 13, 54, 12, 15, 12, 4, 13},
+ {6539, 8, 2, 7, 2, 13, 13, 55, 12, 15, 12, 4, 13},
+ {6482, 8, 2, 7, 2, 13, 13, 56, 12, 15, 12, 4, 13},
+ {6427, 7, 2, 7, 3, 14, 13, 54, 12, 15, 12, 4, 13},
+ {6373, 7, 2, 7, 3, 15, 13, 54, 12, 15, 12, 4, 13},
+ {6321, 7, 2, 7, 3, 15, 13, 55, 12, 15, 12, 4, 13},
+ {6270, 7, 2, 7, 3, 15, 13, 56, 12, 15, 12, 4, 13},
+ {6222, 7, 2, 7, 3, 15, 13, 57, 12, 15, 12, 4, 13},
+ {6174, 6, 2, 7, 3, 15, 13, 54, 12, 15, 12, 4, 13},
+ {6128, 6, 2, 7, 3, 15, 13, 55, 12, 15, 12, 4, 13},
+ {6083, 6, 2, 7, 3, 15, 13, 56, 12, 15, 12, 4, 13},
+ {6040, 6, 2, 7, 3, 15, 13, 57, 12, 15, 12, 4, 13},
+ {5997, 6, 2, 7, 3, 15, 13, 58, 12, 15, 12, 4, 13},
+ }, {
+ {12605, 13, 1, 4, 2, 15, 12, 26, 11, 15, 11, 4, 13}, /* 60 MHz */
+ {12245, 13, 1, 4, 2, 15, 12, 26, 11, 15, 11, 4, 13},
+ {11906, 13, 1, 4, 2, 15, 12, 26, 12, 15, 11, 4, 12},
+ {11588, 13, 1, 4, 2, 15, 12, 30, 12, 15, 11, 4, 12},
+ {11288, 13, 1, 4, 2, 15, 12, 32, 12, 15, 10, 4, 12},
+ {11007, 13, 1, 4, 2, 15, 12, 31, 12, 15, 11, 4, 12},
+ {10742, 13, 1, 4, 2, 15, 12, 33, 12, 15, 10, 4, 12},
+ {10492, 13, 1, 6, 2, 15, 12, 37, 12, 15, 10, 4, 12},
+ {10258, 13, 1, 6, 2, 15, 12, 38, 12, 15, 11, 4, 13},
+ {10036, 13, 1, 6, 2, 15, 12, 38, 12, 15, 10, 4, 12},
+ {9827, 13, 1, 6, 2, 14, 12, 42, 12, 15, 11, 4, 12},
+ {9631, 13, 1, 6, 2, 13, 12, 41, 12, 15, 11, 4, 12},
+ {9445, 13, 1, 6, 2, 12, 12, 45, 12, 15, 11, 4, 12},
+ {9269, 13, 1, 6, 2, 12, 12, 47, 12, 15, 11, 4, 12},
+ {9103, 13, 1, 6, 2, 12, 12, 46, 12, 15, 11, 4, 12},
+ {8946, 13, 1, 6, 2, 12, 12, 48, 12, 15, 10, 4, 12},
+ {8797, 12, 1, 7, 2, 12, 12, 49, 12, 15, 11, 4, 13},
+ {8655, 12, 1, 7, 2, 12, 12, 51, 12, 15, 11, 4, 12},
+ {8520, 12, 1, 7, 2, 12, 12, 50, 12, 15, 11, 4, 12},
+ {8392, 12, 1, 7, 2, 12, 12, 52, 12, 15, 10, 4, 12},
+ {8269, 12, 1, 7, 2, 12, 12, 56, 12, 15, 10, 4, 12},
+ {8153, 12, 1, 7, 2, 12, 12, 55, 12, 15, 11, 4, 12},
+ {8041, 12, 1, 7, 2, 13, 12, 57, 12, 15, 10, 4, 12},
+ {7934, 11, 1, 7, 2, 12, 12, 55, 12, 15, 11, 4, 12},
+ {7831, 11, 1, 7, 2, 12, 12, 57, 12, 15, 10, 4, 12},
+ {7733, 10, 1, 7, 3, 13, 12, 55, 12, 15, 11, 4, 12},
+ {7638, 10, 1, 7, 2, 12, 12, 59, 12, 15, 10, 4, 12},
+ {7547, 10, 1, 7, 2, 12, 12, 60, 12, 15, 11, 4, 12},
+ {7459, 10, 1, 7, 2, 12, 12, 48, 12, 15, 11, 4, 12},
+ {7374, 10, 2, 7, 3, 14, 13, 47, 12, 15, 11, 4, 13},
+ {7291, 10, 2, 7, 3, 14, 13, 49, 12, 15, 10, 4, 12},
+ {7212, 10, 2, 7, 3, 14, 13, 50, 12, 15, 10, 4, 12},
+ {7135, 10, 2, 7, 3, 14, 13, 52, 12, 15, 11, 4, 13},
+ {7061, 10, 2, 7, 3, 14, 13, 52, 12, 15, 11, 4, 12},
+ {6988, 10, 1, 7, 3, 12, 14, 63, 11, 15, 11, 4, 13},
+ {6918, 9, 1, 7, 3, 12, 14, 63, 11, 15, 11, 4, 13},
+ {6850, 9, 1, 7, 3, 12, 14, 63, 11, 15, 11, 4, 13},
+ {6784, 9, 1, 7, 2, 11, 14, 63, 11, 15, 11, 4, 13},
+ {6720, 9, 1, 7, 2, 11, 14, 63, 11, 14, 11, 3, 13},
+ {6658, 8, 1, 7, 3, 12, 14, 63, 11, 15, 11, 4, 13},
+ {6597, 8, 1, 7, 2, 11, 14, 63, 11, 14, 11, 3, 13},
+ {6539, 8, 1, 7, 2, 11, 14, 63, 10, 14, 11, 3, 13},
+ {6482, 8, 1, 7, 2, 11, 14, 63, 10, 14, 11, 3, 13},
+ {6427, 7, 2, 7, 3, 14, 13, 54, 12, 15, 10, 4, 12},
+ {6373, 7, 2, 7, 3, 15, 13, 53, 12, 15, 11, 4, 12},
+ {6321, 7, 2, 7, 3, 15, 13, 54, 12, 15, 11, 4, 12},
+ {6270, 7, 2, 7, 3, 15, 13, 55, 12, 15, 11, 4, 12},
+ {6222, 7, 2, 7, 3, 15, 13, 56, 12, 15, 11, 4, 12},
+ {6174, 6, 2, 7, 3, 15, 13, 53, 12, 15, 11, 4, 12},
+ {6128, 6, 2, 7, 3, 15, 13, 55, 12, 15, 11, 4, 12},
+ {6083, 6, 2, 7, 3, 15, 13, 55, 12, 15, 10, 4, 12},
+ {6040, 6, 2, 7, 3, 15, 13, 56, 12, 15, 10, 4, 12},
+ {5997, 6, 2, 7, 3, 15, 13, 57, 12, 15, 10, 4, 12},
+ }, {
+ {12605, 13, 1, 4, 2, 15, 12, 21, 12, 15, 11, 4, 13}, /* 80 MHz */
+ {12245, 13, 1, 4, 2, 15, 12, 21, 12, 15, 11, 4, 13},
+ {11906, 13, 1, 4, 2, 15, 12, 20, 11, 15, 11, 4, 13},
+ {11588, 13, 1, 4, 2, 15, 12, 22, 12, 15, 11, 4, 12},
+ {11288, 13, 1, 5, 2, 15, 12, 23, 12, 15, 11, 4, 13},
+ {11007, 13, 1, 5, 2, 15, 12, 25, 12, 15, 10, 4, 12},
+ {10742, 13, 1, 5, 2, 15, 12, 26, 12, 15, 11, 4, 13},
+ {10492, 13, 1, 6, 2, 15, 12, 27, 11, 15, 11, 4, 13},
+ {10258, 13, 1, 6, 2, 15, 12, 29, 12, 15, 10, 4, 12},
+ {10036, 13, 1, 6, 2, 15, 12, 30, 12, 15, 11, 4, 12},
+ {9827, 13, 1, 6, 2, 14, 12, 31, 12, 15, 11, 4, 13},
+ {9631, 13, 1, 6, 2, 13, 12, 33, 12, 15, 10, 4, 12},
+ {9445, 13, 1, 6, 2, 12, 12, 34, 12, 15, 11, 4, 12},
+ {9269, 13, 1, 6, 2, 12, 12, 35, 12, 15, 11, 4, 13},
+ {9103, 13, 1, 6, 2, 12, 12, 37, 12, 15, 10, 4, 12},
+ {8946, 13, 1, 6, 2, 12, 12, 38, 12, 15, 11, 4, 12},
+ {8797, 12, 1, 7, 2, 12, 12, 37, 12, 15, 11, 4, 13},
+ {8655, 12, 1, 7, 2, 12, 12, 39, 12, 15, 11, 4, 12},
+ {8520, 12, 1, 7, 2, 12, 12, 40, 12, 15, 11, 4, 12},
+ {8392, 12, 1, 7, 2, 12, 12, 41, 12, 15, 11, 4, 13},
+ {8269, 12, 1, 7, 2, 12, 12, 43, 12, 15, 10, 4, 12},
+ {8153, 12, 1, 7, 2, 12, 12, 44, 12, 15, 11, 4, 12},
+ {8041, 12, 1, 7, 2, 13, 12, 45, 12, 15, 11, 4, 12},
+ {7934, 11, 1, 7, 2, 12, 12, 44, 12, 15, 11, 4, 12},
+ {7831, 11, 1, 7, 2, 12, 12, 45, 12, 15, 11, 4, 13},
+ {7733, 10, 1, 7, 3, 13, 12, 44, 12, 15, 11, 4, 12},
+ {7638, 10, 1, 7, 2, 12, 12, 45, 12, 15, 11, 4, 12},
+ {7547, 10, 1, 7, 2, 12, 12, 46, 12, 15, 11, 4, 12},
+ {7459, 10, 1, 7, 2, 12, 12, 47, 12, 15, 11, 4, 13},
+ {7374, 10, 2, 7, 3, 14, 13, 38, 12, 15, 11, 4, 12},
+ {7291, 10, 2, 7, 3, 14, 13, 39, 12, 15, 10, 4, 12},
+ {7212, 10, 2, 7, 3, 14, 13, 40, 12, 15, 10, 4, 12},
+ {7135, 10, 2, 7, 3, 14, 13, 41, 12, 15, 10, 4, 12},
+ {7061, 10, 2, 7, 3, 14, 13, 41, 12, 15, 11, 4, 13},
+ {6988, 10, 1, 7, 3, 12, 14, 54, 12, 15, 11, 4, 12},
+ {6918, 9, 2, 7, 3, 14, 13, 41, 12, 15, 10, 4, 12},
+ {6850, 9, 2, 7, 3, 14, 13, 42, 12, 15, 10, 4, 12},
+ {6784, 9, 2, 7, 2, 13, 13, 42, 12, 15, 11, 4, 13},
+ {6720, 9, 2, 7, 2, 13, 13, 43, 12, 15, 11, 4, 13},
+ {6658, 8, 2, 7, 3, 14, 13, 41, 12, 15, 11, 4, 13},
+ {6597, 8, 2, 7, 2, 13, 13, 42, 12, 15, 11, 4, 12},
+ {6539, 8, 2, 7, 2, 13, 13, 43, 12, 15, 11, 4, 12},
+ {6482, 8, 2, 7, 2, 13, 13, 44, 12, 15, 11, 4, 12},
+ {6427, 7, 2, 7, 3, 14, 13, 42, 12, 15, 10, 4, 12},
+ {6373, 7, 2, 7, 3, 15, 13, 42, 12, 15, 11, 4, 13},
+ {6321, 7, 2, 7, 3, 15, 13, 43, 12, 15, 11, 4, 12},
+ {6270, 7, 2, 7, 3, 15, 13, 44, 12, 15, 11, 4, 12},
+ {6222, 7, 2, 7, 3, 15, 13, 45, 12, 15, 10, 4, 12},
+ {6174, 6, 2, 7, 3, 15, 13, 42, 12, 15, 11, 4, 13},
+ {6128, 6, 2, 7, 3, 15, 13, 43, 12, 15, 11, 4, 12},
+ {6083, 6, 2, 7, 3, 15, 13, 44, 12, 15, 10, 4, 12},
+ {6040, 6, 2, 7, 3, 15, 13, 44, 12, 15, 11, 4, 13},
+ {5997, 6, 2, 7, 3, 15, 13, 45, 12, 15, 11, 4, 12},
+ } };
+
+/* Rx Gain Tables */
+
+#define SIZE_FULL_TABLE 77
+
+static const uint8_t full_gain_table[RXGAIN_TBLS_END][SIZE_FULL_TABLE][3] =
+{ { /* 800 MHz */
+ { 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 },
+ { 0x00, 0x01, 0x00 }, { 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 },
+ { 0x00, 0x04, 0x00 }, { 0x00, 0x05, 0x00 }, { 0x01, 0x03, 0x20 },
+ { 0x01, 0x04, 0x00 }, { 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 },
+ { 0x01, 0x07, 0x00 }, { 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 },
+ { 0x01, 0x0A, 0x00 }, { 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 },
+ { 0x01, 0x0D, 0x00 }, { 0x01, 0x0E, 0x00 }, { 0x02, 0x09, 0x20 },
+ { 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x00 }, { 0x02, 0x0C, 0x00 },
+ { 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, { 0x02, 0x0F, 0x00 },
+ { 0x02, 0x10, 0x00 }, { 0x02, 0x2B, 0x20 }, { 0x02, 0x2C, 0x00 },
+ { 0x04, 0x28, 0x20 }, { 0x04, 0x29, 0x00 }, { 0x04, 0x2A, 0x00 },
+ { 0x04, 0x2B, 0x00 }, { 0x24, 0x20, 0x20 }, { 0x24, 0x21, 0x00 },
+ { 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 }, { 0x44, 0x22, 0x00 },
+ { 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 }, { 0x44, 0x25, 0x00 },
+ { 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 }, { 0x44, 0x28, 0x00 },
+ { 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 }, { 0x44, 0x2B, 0x00 },
+ { 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 }, { 0x44, 0x2E, 0x00 },
+ { 0x44, 0x2F, 0x00 }, { 0x44, 0x30, 0x00 }, { 0x44, 0x31, 0x00 },
+ { 0x44, 0x32, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 },
+ { 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 },
+ { 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 },
+ { 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 },
+ { 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 },
+ { 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 },
+ { 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 },
+ { 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 }
+}, { /* 2300 MHz */
+ { 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 },
+ { 0x00, 0x01, 0x00 }, { 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 },
+ { 0x00, 0x04, 0x00 }, { 0x00, 0x05, 0x00 }, { 0x01, 0x03, 0x20 },
+ { 0x01, 0x04, 0x00 }, { 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 },
+ { 0x01, 0x07, 0x00 }, { 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 },
+ { 0x01, 0x0A, 0x00 }, { 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 },
+ { 0x01, 0x0D, 0x00 }, { 0x01, 0x0E, 0x00 }, { 0x02, 0x09, 0x20 },
+ { 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x00 }, { 0x02, 0x0C, 0x00 },
+ { 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, { 0x02, 0x0F, 0x00 },
+ { 0x02, 0x10, 0x00 }, { 0x02, 0x2B, 0x20 }, { 0x02, 0x2C, 0x00 },
+ { 0x04, 0x27, 0x20 }, { 0x04, 0x28, 0x00 }, { 0x04, 0x29, 0x00 },
+ { 0x04, 0x2A, 0x00 }, { 0x04, 0x2B, 0x00 }, { 0x24, 0x21, 0x20 },
+ { 0x24, 0x22, 0x00 }, { 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 },
+ { 0x44, 0x22, 0x00 }, { 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 },
+ { 0x44, 0x25, 0x00 }, { 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 },
+ { 0x44, 0x28, 0x00 }, { 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 },
+ { 0x44, 0x2B, 0x00 }, { 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 },
+ { 0x44, 0x2E, 0x00 }, { 0x44, 0x2F, 0x00 }, { 0x44, 0x30, 0x00 },
+ { 0x44, 0x31, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 },
+ { 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 },
+ { 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 },
+ { 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 },
+ { 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 },
+ { 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 },
+ { 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 },
+ { 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 },
+}, { /* 5500 MHz */
+ { 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 },
+ { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x01, 0x00 },
+ { 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 }, { 0x01, 0x01, 0x20 },
+ { 0x01, 0x02, 0x00 }, { 0x01, 0x03, 0x00 }, { 0x01, 0x04, 0x20 },
+ { 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 }, { 0x01, 0x07, 0x00 },
+ { 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 }, { 0x01, 0x0A, 0x00 },
+ { 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 }, { 0x02, 0x08, 0x20 },
+ { 0x02, 0x09, 0x00 }, { 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x20 },
+ { 0x02, 0x0C, 0x00 }, { 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 },
+ { 0x02, 0x0F, 0x00 }, { 0x02, 0x2A, 0x20 }, { 0x02, 0x2B, 0x00 },
+ { 0x04, 0x27, 0x20 }, { 0x04, 0x28, 0x00 }, { 0x04, 0x29, 0x00 },
+ { 0x04, 0x2A, 0x00 }, { 0x04, 0x2B, 0x00 }, { 0x04, 0x2C, 0x00 },
+ { 0x04, 0x2D, 0x00 }, { 0x24, 0x20, 0x20 }, { 0x24, 0x21, 0x00 },
+ { 0x24, 0x22, 0x00 }, { 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 },
+ { 0x44, 0x22, 0x00 }, { 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 },
+ { 0x44, 0x25, 0x00 }, { 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 },
+ { 0x44, 0x28, 0x00 }, { 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 },
+ { 0x44, 0x2B, 0x00 }, { 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 },
+ { 0x44, 0x2E, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 },
+ { 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 },
+ { 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 },
+ { 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 },
+ { 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 },
+ { 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 },
+ { 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 },
+ { 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 }
+} };
+
+#define SIZE_SPLIT_TABLE 41
+
+static const uint8_t split_gain_table[RXGAIN_TBLS_END][SIZE_SPLIT_TABLE][3] =
+{ { /* 800 MHz */
+ { 0x00, 0x18, 0x20 }, { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 },
+ { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 },
+ { 0x00, 0x18, 0x20 }, { 0x01, 0x18, 0x20 }, { 0x02, 0x18, 0x20 },
+ { 0x04, 0x18, 0x20 }, { 0x04, 0x38, 0x20 }, { 0x05, 0x38, 0x20 },
+ { 0x06, 0x38, 0x20 }, { 0x07, 0x38, 0x20 }, { 0x08, 0x38, 0x20 },
+ { 0x09, 0x38, 0x20 }, { 0x0A, 0x38, 0x20 }, { 0x0B, 0x38, 0x20 },
+ { 0x0C, 0x38, 0x20 }, { 0x0D, 0x38, 0x20 }, { 0x0E, 0x38, 0x20 },
+ { 0x0F, 0x38, 0x20 }, { 0x24, 0x38, 0x20 }, { 0x25, 0x38, 0x20 },
+ { 0x44, 0x38, 0x20 }, { 0x45, 0x38, 0x20 }, { 0x46, 0x38, 0x20 },
+ { 0x47, 0x38, 0x20 }, { 0x48, 0x38, 0x20 }, { 0x64, 0x38, 0x20 },
+ { 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 },
+ { 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 },
+ { 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 },
+ { 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 },
+}, { /* 2300 MHz */
+ { 0x00, 0x18, 0x20 }, { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 },
+ { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 },
+ { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x20 }, { 0x01, 0x18, 0x20 },
+ { 0x02, 0x18, 0x20 }, { 0x04, 0x18, 0x20 }, { 0x04, 0x38, 0x20 },
+ { 0x05, 0x38, 0x20 }, { 0x06, 0x38, 0x20 }, { 0x07, 0x38, 0x20 },
+ { 0x08, 0x38, 0x20 }, { 0x09, 0x38, 0x20 }, { 0x0A, 0x38, 0x20 },
+ { 0x0B, 0x38, 0x20 }, { 0x0C, 0x38, 0x20 }, { 0x0D, 0x38, 0x20 },
+ { 0x0E, 0x38, 0x20 }, { 0x0F, 0x38, 0x20 }, { 0x25, 0x38, 0x20 },
+ { 0x26, 0x38, 0x20 }, { 0x44, 0x38, 0x20 }, { 0x45, 0x38, 0x20 },
+ { 0x46, 0x38, 0x20 }, { 0x47, 0x38, 0x20 }, { 0x64, 0x38, 0x20 },
+ { 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 },
+ { 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 },
+ { 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 },
+ { 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 },
+}, { /* 5500 MHz */
+ { 0x00, 0x18, 0x20 }, { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 },
+ { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 },
+ { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 },
+ { 0x00, 0x18, 0x00 }, { 0x01, 0x18, 0x20 }, { 0x02, 0x18, 0x20 },
+ { 0x04, 0x18, 0x20 }, { 0x04, 0x38, 0x20 }, { 0x05, 0x38, 0x20 },
+ { 0x06, 0x38, 0x20 }, { 0x07, 0x38, 0x20 }, { 0x08, 0x38, 0x20 },
+ { 0x09, 0x38, 0x20 }, { 0x0A, 0x38, 0x20 }, { 0x0B, 0x38, 0x20 },
+ { 0x0C, 0x38, 0x20 }, { 0x0D, 0x38, 0x20 }, { 0x0E, 0x38, 0x20 },
+ { 0x0F, 0x38, 0x20 }, { 0x62, 0x38, 0x20 }, { 0x25, 0x38, 0x20 },
+ { 0x26, 0x38, 0x20 }, { 0x44, 0x38, 0x20 }, { 0x64, 0x38, 0x20 },
+ { 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 },
+ { 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 },
+ { 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 },
+ { 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 },
+} };
+
+/* Mixer GM Sub-table */
+
+static const uint8_t gm_st_gain[16] = { 0x78, 0x74, 0x70, 0x6C, 0x68, 0x64, 0x60,
+0x5C, 0x58, 0x54, 0x50, 0x4C, 0x48, 0x30, 0x18, 0x0 };
+static const uint8_t gm_st_ctrl[16] = { 0x0, 0xD, 0x15, 0x1B, 0x21, 0x25, 0x29,
+0x2C, 0x2F, 0x31, 0x33, 0x34, 0x35, 0x3A, 0x3D, 0x3E };
+
+static const int8_t lna_table[RXGAIN_TBLS_END][4] = {
+ {5, 17, 19, 24}, {3, 14, 17, 21}, {-4, 10, 13, 14}};
+static const int8_t tia_table[] = { -6, 0 };
+static const int8_t mixer_table[RXGAIN_TBLS_END][16] = {
+ {0, 3, 9, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},
+ {0, 3, 9, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26},
+ {0, 3, 8, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}};
+
+static const uint32_t gain_step_calib_reg_val[4][5] = {
+ {0xC0, 0x2E, 0x10, 0x06, 0x00}, // LO Frequency Range: 600 to 1300 MHz
+ {0xC0, 0x2C, 0x10, 0x06, 0x00}, // LO Frequency Range: 1300 to 3300 MHz
+ {0xB8, 0x2C, 0x10, 0x06, 0x00}, // LO Frequency Range: 2700 to 4100 MHz
+ {0xA0, 0x24, 0x10, 0x06, 0x00}, // LO Frequency Range: 4000 to 6000 MHz
+};
+
+/******************************************************************************/
+/********************** Macros and Constants Definitions **********************/
+/******************************************************************************/
+const char *ad9361_ensm_states[] = {
+ "sleep", "", "", "", "", "alert", "tx", "tx flush",
+ "rx", "rx_flush", "fdd", "fdd_flush"
+};
+
+/**
+ * SPI multiple bytes register read.
+ * @param spi
+ * @param reg The register address.
+ * @param rbuf The data buffer.
+ * @param num The number of bytes to read.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_spi_readm(struct spi_device *spi, uint32_t reg,
+ uint8_t *rbuf, uint32_t num)
+{
+ uint8_t buf[2];
+ int32_t ret;
+ uint16_t cmd;
+
+ if (num > MAX_MBYTE_SPI)
+ return -EINVAL;
+
+ cmd = AD_READ | AD_CNT(num) | AD_ADDR(reg);
+ buf[0] = cmd >> 8;
+ buf[1] = cmd & 0xFF;
+
+#ifdef NUAND_MODIFICATIONS
+ // no-op to clear warning
+ buf[0] = buf[1];
+
+ // use alternate spi_read
+ ret = spi_read(spi, cmd, rbuf, num);
+#else
+ ret = spi_write_then_read(spi, &buf[0], 2, rbuf, num);
+#endif // NUAND_MODIFICATIONS
+ if (ret < 0) {
+ dev_err(&spi->dev, "Read Error %"PRId32, ret);
+ return ret;
+ }
+#ifdef _DEBUG
+ {
+ int32_t i;
+ for (i = 0; i < num; i++)
+ dev_dbg(&spi->dev, "%s: reg 0x%"PRIX32" val 0x%X",
+ __func__, reg--, rbuf[i]);
+ }
+#endif
+
+ return 0;
+}
+
+/**
+ * SPI register read.
+ * @param spi
+ * @param reg The register address.
+ * @return The register value or negative error code in case of failure.
+ */
+int32_t ad9361_spi_read(struct spi_device *spi, uint32_t reg)
+{
+ uint8_t buf;
+ int32_t ret;
+
+ ret = ad9361_spi_readm(spi, reg, &buf, 1);
+ if (ret < 0)
+ return ret;
+
+ return buf;
+}
+
+/**
+ * SPI register bits read.
+ * @param spi
+ * @param reg The register address.
+ * @param mask The bits mask.
+ * @param offset The mask offset.
+ * @return The bits value or negative error code in case of failure.
+ */
+static int32_t __ad9361_spi_readf(struct spi_device *spi, uint32_t reg,
+ uint32_t mask, uint32_t offset)
+{
+ uint8_t buf;
+ int32_t ret;
+
+ if (!mask)
+ return -EINVAL;
+
+ ret = ad9361_spi_readm(spi, reg, &buf, 1);
+ if (ret < 0)
+ return ret;
+
+ buf &= mask;
+ buf >>= offset;
+
+ return buf;
+}
+
+/**
+ * SPI register bits read.
+ * @param spi
+ * @param reg The register address.
+ * @param mask The bits mask.
+ * @return The bits value or negative error code in case of failure.
+ */
+#define ad9361_spi_readf(spi, reg, mask) \
+ __ad9361_spi_readf(spi, reg, mask, find_first_bit(mask))
+
+/**
+ * SPI register write.
+ * @param spi
+ * @param reg The register address.
+ * @param val The value of the register.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_spi_write(struct spi_device *spi,
+ uint32_t reg, uint32_t val)
+{
+ uint8_t buf[3];
+ int32_t ret;
+ uint16_t cmd;
+
+ cmd = AD_WRITE | AD_CNT(1) | AD_ADDR(reg);
+ buf[0] = cmd >> 8;
+ buf[1] = cmd & 0xFF;
+ buf[2] = val;
+
+#ifdef NUAND_MODIFICATIONS
+ // use alternate spi_write
+ ret = spi_write(spi, cmd, &(buf[2]), 1);
+#else
+ ret = spi_write_then_read(spi, buf, 3, NULL, 0);
+#endif // NUAND_MODIFICATIONS
+
+ if (ret < 0) {
+ dev_err(&spi->dev, "Write Error %"PRId32, ret);
+ return ret;
+ }
+
+#ifdef _DEBUG
+ dev_dbg(&spi->dev, "%s: reg 0x%"PRIX32" val 0x%X", __func__, reg, buf[2]);
+#endif
+
+ return 0;
+}
+
+/**
+ * SPI register bits write.
+ * @param spi
+ * @param reg The register address.
+ * @param mask The bits mask.
+ * @param offset The mask offset.
+ * @param val The bits value.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t __ad9361_spi_writef(struct spi_device *spi, uint32_t reg,
+ uint32_t mask, uint32_t offset, uint32_t val)
+{
+ uint8_t buf;
+ int32_t ret;
+
+ if (!mask)
+ return -EINVAL;
+
+ ret = ad9361_spi_readm(spi, reg, &buf, 1);
+ if (ret < 0)
+ return ret;
+
+ buf &= ~mask;
+ buf |= ((val << offset) & mask);
+
+ return ad9361_spi_write(spi, reg, buf);
+}
+
+/**
+ * SPI register bits write.
+ * @param spi
+ * @param reg The register address.
+ * @param mask The bits mask.
+ * @param val The bits value.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+#define ad9361_spi_writef(spi, reg, mask, val) \
+ __ad9361_spi_writef(spi, reg, mask, find_first_bit(mask), val)
+
+/**
+ * SPI multiple bytes register write.
+ * @param spi
+ * @param reg The register address.
+ * @param tbuf The data buffer.
+ * @param num The number of bytes to read.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_spi_writem(struct spi_device *spi,
+ uint32_t reg, uint8_t *tbuf, uint32_t num)
+{
+ uint8_t buf[10];
+ int32_t ret;
+ uint16_t cmd;
+
+ if (num > MAX_MBYTE_SPI)
+ return -EINVAL;
+
+ cmd = AD_WRITE | AD_CNT(num) | AD_ADDR(reg);
+ buf[0] = cmd >> 8;
+ buf[1] = cmd & 0xFF;
+
+#ifndef ALTERA_PLATFORM
+ memcpy(&buf[2], tbuf, num);
+#else
+ int32_t i;
+ for (i = 0; i < num; i++)
+ buf[2 + i] = tbuf[i];
+#endif
+
+#ifdef NUAND_MODIFICATIONS
+ // no-op to clear warning
+ buf[0] = buf[1];
+
+ // use alternate spi_write
+ ret = spi_write(spi, cmd, tbuf, num);
+#else
+ ret = spi_write_then_read(spi, buf, num + 2, NULL, 0);
+#endif // NUAND_MODIFICATIONS
+
+ if (ret < 0) {
+ dev_err(&spi->dev, "Write Error %"PRId32, ret);
+ return ret;
+ }
+
+#ifdef _DEBUG
+ {
+ int32_t i;
+ for (i = 0; i < num; i++)
+ dev_dbg(&spi->dev, "Reg 0x%"PRIX32" val 0x%X", reg--, tbuf[i]);
+ }
+#endif
+
+ return 0;
+}
+
+/**
+ * Validate RF BW frequency.
+ * @param phy The AD9361 state structure.
+ * @param bw The RF BW frequency.
+ * @return The validated RF BW frequency.
+ */
+uint32_t ad9361_validate_rf_bw(struct ad9361_rf_phy *phy, uint32_t bw)
+{
+ switch (phy->dev_sel) {
+ case ID_AD9363A:
+ return clamp_t(uint32_t, bw, 0, 20000000UL);
+ default:
+ return clamp_t(uint32_t, bw, 0, 56000000UL);
+ }
+}
+
+/**
+ * Validate RF PLL frequency.
+ * @param phy The AD9361 state structure.
+ * @param freq The RF PLL frequency.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_validate_rfpll(struct ad9361_rf_phy *phy, uint64_t freq)
+{
+ switch (phy->dev_sel) {
+ case ID_AD9363A:
+ if (freq > AD9363A_MAX_CARRIER_FREQ_HZ ||
+ freq < AD9363A_MIN_CARRIER_FREQ_HZ)
+ return -EINVAL;
+ break;
+ default:
+ if (freq > MAX_CARRIER_FREQ_HZ || freq < MIN_CARRIER_FREQ_HZ)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * Find optimal value.
+ * @param field
+ * @param ret_start
+ * @return The optimal delay in case of success, negative error code otherwise.
+ */
+int32_t ad9361_find_opt(uint8_t *field, uint32_t size, uint32_t *ret_start)
+{
+ int32_t i, cnt = 0, max_cnt = 0, start, max_start = 0;
+
+#ifdef NUAND_MODIFICATIONS
+ // add check for failed optimal value search
+ bool found_0 = false;
+ bool found_1 = false;
+#endif // NUAND_MODIFICATIONS
+
+ for(i = 0, start = -1; i < (int64_t)size; i++) {
+ if (field[i] == 0) {
+ if (start == -1)
+ start = i;
+ cnt++;
+
+#ifdef NUAND_MODIFICATIONS
+ found_0 = true;
+#endif // NUAND_MODIFICATIONS
+
+ } else {
+ if (cnt > max_cnt) {
+ max_cnt = cnt;
+ max_start = start;
+ }
+ start = -1;
+ cnt = 0;
+
+#ifdef NUAND_MODIFICATIONS
+ found_1 = true;
+#endif // NUAND_MODIFICATIONS
+
+ }
+ }
+
+ if (cnt > max_cnt) {
+ max_cnt = cnt;
+ max_start = start;
+ }
+
+ *ret_start = max_start;
+
+#ifdef NUAND_MODIFICATIONS
+ if (!found_0 || !found_1) {
+ // All of the values were the same. This is not a healthy result.
+ return -1;
+ }
+#endif // NUAND_MODIFICATIONS
+
+ return max_cnt;
+}
+
+/**
+ * Select the channel mapping in 1rx1tx mode.
+ * @param phy The AD9361 state structure.
+ * @param map Map
+ * @param channel Channel
+ * @return The channel number.
+ */
+int32_t ad9361_1rx1tx_channel_map(struct ad9361_rf_phy *phy, bool tx, int32_t channel)
+{
+ uint32_t map;
+
+ if (phy->pdata->rx2tx2)
+ return channel;
+
+ if (tx)
+ map = phy->pdata->rx1tx1_mode_use_tx_num;
+ else
+ map = phy->pdata->rx1tx1_mode_use_rx_num;
+
+ if (map == 2)
+ return channel + 1;
+
+ return channel;
+}
+
+/**
+ * AD9361 Device Reset
+ * @param phy The AD9361 state structure.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_reset(struct ad9361_rf_phy *phy)
+{
+#ifdef NUAND_MODIFICATIONS
+ // use alternate gpio accessors
+ if (gpio_is_valid(phy->gpio, phy->pdata->gpio_resetb)) {
+ gpio_set_value(phy->gpio, phy->pdata->gpio_resetb, 0);
+ mdelay(1);
+ gpio_set_value(phy->gpio, phy->pdata->gpio_resetb, 1);
+ mdelay(1);
+ dev_dbg(&phy->spi->dev, "%s: by GPIO", __func__);
+ return 0;
+ }
+#else
+ if (gpio_is_valid(phy->pdata->gpio_resetb)) {
+ gpio_set_value(phy->pdata->gpio_resetb, 0);
+ mdelay(1);
+ gpio_set_value(phy->pdata->gpio_resetb, 1);
+ mdelay(1);
+ dev_dbg(&phy->spi->dev, "%s: by GPIO", __func__);
+ return 0;
+ }
+#endif // NUAND_MODIFICATIONS
+
+ /* SPI Soft Reset was removed from the register map, since it doesn't
+ * work reliably. Without a prober HW reset randomness may happen.
+ * Please specify a RESET GPIO.
+ */
+
+ ad9361_spi_write(phy->spi, REG_SPI_CONF, SOFT_RESET | _SOFT_RESET);
+ ad9361_spi_write(phy->spi, REG_SPI_CONF, 0x0);
+ dev_err(&phy->spi->dev,
+ "%s: by SPI, this may cause unpredicted behavior!", __func__);
+
+ return -ENODEV;
+}
+
+/**
+ * Enable/disable the desired TX channel.
+ * @param phy The AD9361 state structure.
+ * @param tx_if The desired channel number [1, 2].
+ * @param enable Enable/disable option.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_en_dis_tx(struct ad9361_rf_phy *phy, uint32_t tx_if, uint32_t enable)
+{
+ if ((tx_if & enable) > 1 && AD9364_DEVICE && enable)
+ return -EINVAL;
+
+ return ad9361_spi_writef(phy->spi, REG_TX_ENABLE_FILTER_CTRL,
+ TX_CHANNEL_ENABLE(tx_if), enable);
+}
+
+/**
+ * Enable/disable the desired RX channel.
+ * @param phy The AD9361 state structure.
+ * @param tx_if The desired channel number [1, 2].
+ * @param enable Enable/disable option.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_en_dis_rx(struct ad9361_rf_phy *phy, uint32_t rx_if, uint32_t enable)
+{
+ if ((rx_if & enable) > 1 && AD9364_DEVICE && enable)
+ return -EINVAL;
+
+ return ad9361_spi_writef(phy->spi, REG_RX_ENABLE_FILTER_CTRL,
+ RX_CHANNEL_ENABLE(rx_if), enable);
+}
+
+/**
+ * Loopback works only TX1->RX1 or RX2->RX2.
+ * @param phy The AD9361 state structure.
+ * @param enable Enable.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_int_loopback_fix_ch_cross(struct ad9361_rf_phy *phy, bool enable)
+{
+ /* Loopback works only TX1->RX1 or RX2->RX2 */
+ if (!phy->pdata->rx2tx2 && phy->pdata->rx1tx1_mode_use_rx_num !=
+ phy->pdata->rx1tx1_mode_use_tx_num)
+ return ad9361_en_dis_tx(phy, TX_1 | TX_2,
+ enable ? phy->pdata->rx1tx1_mode_use_rx_num :
+ phy->pdata->rx1tx1_mode_use_tx_num);
+
+ return 0;
+}
+
+/**
+ * BIST loopback mode.
+ * @param phy The AD9361 state structure.
+ * @param mode BIST loopback mode.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_bist_loopback(struct ad9361_rf_phy *phy, int32_t mode)
+{
+ uint32_t sp_hd, reg;
+
+ dev_dbg(&phy->spi->dev, "%s: mode %"PRId32, __func__, mode);
+
+ reg = ad9361_spi_read(phy->spi, REG_OBSERVE_CONFIG);
+
+ phy->bist_loopback_mode = mode;
+
+ switch (mode) {
+ case 0:
+ ad9361_hdl_loopback(phy, false);
+ ad9361_int_loopback_fix_ch_cross(phy, false);
+ reg &= ~(DATA_PORT_SP_HD_LOOP_TEST_OE |
+ DATA_PORT_LOOP_TEST_ENABLE);
+ return ad9361_spi_write(phy->spi, REG_OBSERVE_CONFIG, reg);
+ case 1:
+ /* loopback (AD9361 internal) TX->RX */
+ ad9361_hdl_loopback(phy, false);
+ ad9361_int_loopback_fix_ch_cross(phy, true);
+ sp_hd = ad9361_spi_read(phy->spi, REG_PARALLEL_PORT_CONF_3);
+ if ((sp_hd & SINGLE_PORT_MODE) && (sp_hd & HALF_DUPLEX_MODE))
+ reg |= DATA_PORT_SP_HD_LOOP_TEST_OE;
+ else
+ reg &= ~DATA_PORT_SP_HD_LOOP_TEST_OE;
+
+ reg |= DATA_PORT_LOOP_TEST_ENABLE;
+
+ return ad9361_spi_write(phy->spi, REG_OBSERVE_CONFIG, reg);
+ case 2:
+ /* loopback (FPGA internal) RX->TX */
+ ad9361_hdl_loopback(phy, true);
+ ad9361_int_loopback_fix_ch_cross(phy, false);
+ reg &= ~(DATA_PORT_SP_HD_LOOP_TEST_OE |
+ DATA_PORT_LOOP_TEST_ENABLE);
+ return ad9361_spi_write(phy->spi, REG_OBSERVE_CONFIG, reg);
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * Get BIST loopback mode.
+ * @param phy The AD9361 state structure.
+ * @param mode BIST loopback mode.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+void ad9361_get_bist_loopback(struct ad9361_rf_phy *phy, int32_t *mode)
+{
+ *mode = phy->bist_loopback_mode;
+}
+
+/**
+ * BIST mode.
+ * @param phy The AD9361 state structure.
+ * @param mode Bist mode.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_bist_prbs(struct ad9361_rf_phy *phy, enum ad9361_bist_mode mode)
+{
+ uint32_t reg = 0;
+
+ dev_dbg(&phy->spi->dev, "%s: mode %d", __func__, mode);
+
+ phy->bist_prbs_mode = mode;
+
+ switch (mode) {
+ case BIST_DISABLE:
+ reg = 0;
+ break;
+ case BIST_INJ_TX:
+ reg = BIST_CTRL_POINT(0) | BIST_ENABLE;
+ break;
+ case BIST_INJ_RX:
+ reg = BIST_CTRL_POINT(2) | BIST_ENABLE;
+ break;
+ };
+
+ return ad9361_spi_write(phy->spi, REG_BIST_CONFIG, reg);
+}
+
+/**
+ * Get BIST mode settings.
+ * @param phy The AD9361 state structure.
+ * @param mode Bist mode.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+void ad9361_get_bist_prbs(struct ad9361_rf_phy *phy, enum ad9361_bist_mode *mode)
+{
+ *mode = phy->bist_prbs_mode;
+}
+
+/**
+ * BIST tone.
+ * @param phy The AD9361 state structure.
+ * @param mode Bist tone mode.
+ * @param freq_Hz Bist tone frequency.
+ * @param level_dB Bist tone level.
+ * @param mask Bist reg mask.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_bist_tone(struct ad9361_rf_phy *phy,
+ enum ad9361_bist_mode mode, uint32_t freq_Hz,
+ uint32_t level_dB, uint32_t mask)
+{
+ uint32_t clk = 0;
+ uint32_t reg = 0, reg1, reg_mask;
+
+ dev_dbg(&phy->spi->dev, "%s: mode %d", __func__, mode);
+
+ phy->bist_tone_mode = mode;
+ phy->bist_tone_freq_Hz = freq_Hz;
+ phy->bist_tone_level_dB = level_dB;
+ phy->bist_tone_mask = mask;
+
+ switch (mode) {
+ case BIST_DISABLE:
+ reg = 0;
+ break;
+ case BIST_INJ_TX:
+ clk = clk_get_rate(phy, phy->ref_clk_scale[TX_SAMPL_CLK]);
+ reg = BIST_CTRL_POINT(0) | BIST_ENABLE;
+ break;
+ case BIST_INJ_RX:
+ clk = clk_get_rate(phy, phy->ref_clk_scale[RX_SAMPL_CLK]);
+ reg = BIST_CTRL_POINT(2) | BIST_ENABLE;
+ break;
+ };
+
+ reg |= TONE_PRBS;
+ reg |= TONE_LEVEL(level_dB / 6);
+
+ if (freq_Hz < 4) {
+ reg |= TONE_FREQ(freq_Hz);
+ }
+ else {
+ if (clk)
+ reg |= TONE_FREQ(DIV_ROUND_CLOSEST(freq_Hz * 32, clk) - 1);
+ }
+
+ reg_mask = BIST_MASK_CHANNEL_1_I_DATA | BIST_MASK_CHANNEL_1_Q_DATA |
+ BIST_MASK_CHANNEL_2_I_DATA | BIST_MASK_CHANNEL_2_Q_DATA;
+
+ reg1 = ((mask << 2) & reg_mask);
+ ad9361_spi_write(phy->spi, REG_BIST_AND_DATA_PORT_TEST_CONFIG, reg1);
+
+ return ad9361_spi_write(phy->spi, REG_BIST_CONFIG, reg);
+}
+
+/**
+ * Get BIST tone settings.
+ * @param phy The AD9361 state structure.
+ * @param mode Bist tone mode.
+ * @param freq_Hz Bist tone frequency.
+ * @param level_dB Bist tone level.
+ * @param mask Bist reg mask.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+void ad9361_get_bist_tone(struct ad9361_rf_phy *phy,
+ enum ad9361_bist_mode *mode, uint32_t *freq_Hz,
+ uint32_t *level_dB, uint32_t *mask)
+{
+ *mode = phy->bist_tone_mode;
+ *freq_Hz = phy->bist_tone_freq_Hz;
+ *level_dB = phy->bist_tone_level_dB;
+ *mask = phy->bist_tone_mask;
+}
+
+/**
+ * Check the calibration done bit.
+ * @param phy The AD9361 state structure.
+ * @param reg The register address.
+ * @param mask The bit mask.
+ * @param done_state The done state [0,1].
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_check_cal_done(struct ad9361_rf_phy *phy, uint32_t reg,
+ uint32_t mask, uint32_t done_state)
+{
+ uint32_t timeout = 5000; /* RFDC_CAL can take long */
+ uint32_t state;
+
+ do {
+ state = ad9361_spi_readf(phy->spi, reg, mask);
+ if (state == done_state)
+ return 0;
+
+ if (reg == REG_CALIBRATION_CTRL)
+ udelay(1200);
+ else
+ udelay(120);
+ } while (timeout--);
+
+ dev_err(&phy->spi->dev, "Calibration TIMEOUT (0x%"PRIX32", 0x%"PRIX32")", reg, mask);
+
+ return -ETIMEDOUT;
+}
+
+/**
+ * Run an AD9361 calibration and check the calibration done bit.
+ * @param phy The AD9361 state structure.
+ * @param mask The calibration bit mask[RX_BB_TUNE_CAL, TX_BB_TUNE_CAL,
+ * RX_QUAD_CAL, TX_QUAD_CAL, RX_GAIN_STEP_CAL, TXMON_CAL,
+ * RFDC_CAL, BBDC_CAL].
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_run_calibration(struct ad9361_rf_phy *phy, uint32_t mask)
+{
+ int32_t ret = ad9361_spi_write(phy->spi, REG_CALIBRATION_CTRL, mask);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(&phy->spi->dev, "%s: CAL Mask 0x%"PRIx32, __func__, mask);
+
+ return ad9361_check_cal_done(phy, REG_CALIBRATION_CTRL, mask, 0);
+}
+
+/**
+ * Choose the right RX gain table index for the selected frequency.
+ * @param freq The frequency value [Hz].
+ * @return The index to the RX gain table.
+ */
+static enum rx_gain_table_name ad9361_gt_tableindex(uint64_t freq)
+{
+ if (freq <= 1300000000ULL)
+ return TBL_200_1300_MHZ;
+
+ if (freq <= 4000000000ULL)
+ return TBL_1300_4000_MHZ;
+
+ return TBL_4000_6000_MHZ;
+}
+
+/**
+ * Shift the real frequency value, so it fits type unsigned long
+ * Note: PLL operates between 47 .. 6000 MHz which is > 2^32.
+ * @param freq The frequency value [Hz].
+ * @return The shifted frequency value.
+ */
+uint32_t ad9361_to_clk(uint64_t freq)
+{
+ return (uint32_t)(freq >> 1);
+}
+
+/**
+ * Shift back the frequency value, so it reflects the real value.
+ * Note: PLL operates between 47 .. 6000 MHz which is > 2^32.
+ * @param freq The frequency value [Hz].
+ * @return The shifted frequency value.
+ */
+uint64_t ad9361_from_clk(uint32_t freq)
+{
+ return ((uint64_t)freq << 1);
+}
+
+/**
+ * Load the gain table for the selected frequency range and receiver.
+ * @param phy The AD9361 state structure.
+ * @param freq The frequency value [Hz].
+ * @param dest The destination [GT_RX1, GT_RX2].
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_load_gt(struct ad9361_rf_phy *phy, uint64_t freq, uint32_t dest)
+{
+ struct spi_device *spi = phy->spi;
+ const uint8_t(*tab)[3];
+ enum rx_gain_table_name band;
+ uint32_t index_max, i, lna;
+
+ dev_dbg(&phy->spi->dev, "%s: frequency %"PRIu64, __func__, freq);
+
+ band = ad9361_gt_tableindex(freq);
+
+ dev_dbg(&phy->spi->dev, "%s: frequency %"PRIu64" (band %d)",
+ __func__, freq, band);
+
+ /* check if table is present */
+ if (phy->current_table == band)
+ return 0;
+
+ ad9361_spi_writef(spi, REG_AGC_CONFIG_2,
+ AGC_USE_FULL_GAIN_TABLE, !phy->pdata->split_gt);
+
+ if (has_split_gt && phy->pdata->split_gt) {
+ tab = &split_gain_table[band][0];
+ index_max = SIZE_SPLIT_TABLE;
+ }
+ else {
+ tab = &full_gain_table[band][0];
+ index_max = SIZE_FULL_TABLE;
+ }
+
+ lna = phy->pdata->elna_ctrl.elna_in_gaintable_all_index_en ?
+ EXT_LNA_CTRL : 0;
+
+ ad9361_spi_write(spi, REG_GAIN_TABLE_CONFIG, START_GAIN_TABLE_CLOCK |
+ RECEIVER_SELECT(dest)); /* Start Gain Table Clock */
+
+ for (i = 0; i < index_max; i++) {
+ ad9361_spi_write(spi, REG_GAIN_TABLE_ADDRESS, i); /* Gain Table Index */
+ ad9361_spi_write(spi, REG_GAIN_TABLE_WRITE_DATA1, tab[i][0] | lna); /* Ext LNA, Int LNA, & Mixer Gain Word */
+ ad9361_spi_write(spi, REG_GAIN_TABLE_WRITE_DATA2, tab[i][1]); /* TIA & LPF Word */
+ ad9361_spi_write(spi, REG_GAIN_TABLE_WRITE_DATA3, tab[i][2]); /* DC Cal bit & Dig Gain Word */
+ ad9361_spi_write(spi, REG_GAIN_TABLE_CONFIG,
+ START_GAIN_TABLE_CLOCK |
+ WRITE_GAIN_TABLE |
+ RECEIVER_SELECT(dest)); /* Gain Table Index */
+ ad9361_spi_write(spi, REG_GAIN_TABLE_READ_DATA1, 0); /* Dummy Write to delay 3 ADCCLK/16 cycles */
+ ad9361_spi_write(spi, REG_GAIN_TABLE_READ_DATA1, 0); /* Dummy Write to delay ~1u */
+ }
+
+ ad9361_spi_write(spi, REG_GAIN_TABLE_CONFIG, START_GAIN_TABLE_CLOCK |
+ RECEIVER_SELECT(dest)); /* Clear Write Bit */
+ ad9361_spi_write(spi, REG_GAIN_TABLE_READ_DATA1, 0); /* Dummy Write to delay ~1u */
+ ad9361_spi_write(spi, REG_GAIN_TABLE_READ_DATA1, 0); /* Dummy Write to delay ~1u */
+ ad9361_spi_write(spi, REG_GAIN_TABLE_CONFIG, 0); /* Stop Gain Table Clock */
+
+ phy->current_table = band;
+
+ return 0;
+}
+
+/**
+ * Setup the external low-noise amplifier (LNA).
+ * @param phy The AD9361 state structure.
+ * @param ctrl Pointer to eLNA control structure.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_setup_ext_lna(struct ad9361_rf_phy *phy,
+struct elna_control *ctrl)
+{
+ ad9361_spi_writef(phy->spi, REG_EXTERNAL_LNA_CTRL, EXTERNAL_LNA1_CTRL,
+ ctrl->elna_1_control_en);
+
+ ad9361_spi_writef(phy->spi, REG_EXTERNAL_LNA_CTRL, EXTERNAL_LNA2_CTRL,
+ ctrl->elna_2_control_en);
+
+ ad9361_spi_write(phy->spi, REG_EXT_LNA_HIGH_GAIN,
+ EXT_LNA_HIGH_GAIN(ctrl->gain_mdB / 500));
+
+ return ad9361_spi_write(phy->spi, REG_EXT_LNA_LOW_GAIN,
+ EXT_LNA_LOW_GAIN(ctrl->bypass_loss_mdB / 500));
+}
+
+/**
+ * Set the clock output mode.
+ * @param phy The AD9361 state structure.
+ * @param mode The clock output mode [].
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_clkout_control(struct ad9361_rf_phy *phy,
+enum ad9361_clkout mode)
+{
+ if (mode == CLKOUT_DISABLE)
+ return ad9361_spi_writef(phy->spi, REG_BBPLL, CLKOUT_ENABLE, 0);
+
+ return ad9361_spi_writef(phy->spi, REG_BBPLL,
+ CLKOUT_ENABLE | CLKOUT_SELECT(~0),
+ ((mode - 1) << 1) | 0x1);
+}
+
+/**
+ * Load the Gm Sub Table.
+ * @param phy The AD9361 state structure.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_load_mixer_gm_subtable(struct ad9361_rf_phy *phy)
+{
+ int32_t i, addr;
+ dev_dbg(&phy->spi->dev, "%s", __func__);
+
+ ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_CONFIG,
+ START_GM_SUB_TABLE_CLOCK); /* Start Clock */
+
+ for (i = 0, addr = ARRAY_SIZE(gm_st_ctrl); i < (int64_t)ARRAY_SIZE(gm_st_ctrl); i++) {
+ ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_ADDRESS, --addr); /* Gain Table Index */
+ ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_BIAS_WRITE, 0); /* Bias */
+ ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_GAIN_WRITE, gm_st_gain[i]); /* Gain */
+ ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_CTRL_WRITE, gm_st_ctrl[i]); /* Control */
+ ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_CONFIG,
+ WRITE_GM_SUB_TABLE | START_GM_SUB_TABLE_CLOCK); /* Write Words */
+ ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_GAIN_READ, 0); /* Dummy Delay */
+ ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_GAIN_READ, 0); /* Dummy Delay */
+ }
+
+ ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_CONFIG, START_GM_SUB_TABLE_CLOCK); /* Clear Write */
+ ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_GAIN_READ, 0); /* Dummy Delay */
+ ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_GAIN_READ, 0); /* Dummy Delay */
+ ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_CONFIG, 0); /* Stop Clock */
+
+ return 0;
+}
+
+/**
+ * Set the attenuation for the selected TX channels.
+ * @param phy The AD9361 state structure.
+ * @param atten_mdb Attenuation value [mdB].
+ * @param tx1 Set true, the attenuation of the TX1 will be affected.
+ * @param tx2 Set true, the attenuation of the TX2 will be affected.
+ * @param immed Set true, an immediate update will take place.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_set_tx_atten(struct ad9361_rf_phy *phy, uint32_t atten_mdb,
+ bool tx1, bool tx2, bool immed)
+{
+ uint8_t buf[2];
+ int32_t ret = 0;
+
+ dev_dbg(&phy->spi->dev, "%s : attenuation %"PRIu32" mdB tx1=%d tx2=%d",
+ __func__, atten_mdb, tx1, tx2);
+
+ if (atten_mdb > 89750) /* 89.75 dB */
+ return -EINVAL;
+
+ atten_mdb /= 250; /* Scale to 0.25dB / LSB */
+
+ buf[0] = atten_mdb >> 8;
+ buf[1] = atten_mdb & 0xFF;
+
+ ad9361_spi_writef(phy->spi, REG_TX2_DIG_ATTEN,
+ IMMEDIATELY_UPDATE_TPC_ATTEN, 0);
+
+ if (tx1)
+ ret = ad9361_spi_writem(phy->spi, REG_TX1_ATTEN_1, buf, 2);
+
+ if (tx2)
+ ret = ad9361_spi_writem(phy->spi, REG_TX2_ATTEN_1, buf, 2);
+
+ if (immed)
+ ad9361_spi_writef(phy->spi, REG_TX2_DIG_ATTEN,
+ IMMEDIATELY_UPDATE_TPC_ATTEN, 1);
+
+ return ret;
+}
+
+/**
+ * Get the attenuation for the selected TX channel.
+ * @param phy The AD9361 state structure.
+ * @param tx_num The selected channel [1, 2].
+ * @return The attenuation value [mdB] or negative error code in case of failure.
+ */
+int32_t ad9361_get_tx_atten(struct ad9361_rf_phy *phy, uint32_t tx_num)
+{
+ uint8_t buf[2];
+ int32_t ret = 0;
+ uint32_t code;
+
+ ret = ad9361_spi_readm(phy->spi, (tx_num == 1) ?
+ REG_TX1_ATTEN_1 : REG_TX2_ATTEN_1, buf, 2);
+
+ if (ret < 0)
+ return ret;
+
+ code = (buf[0] << 8) | buf[1];
+
+ code *= 250;
+
+ return code;
+}
+
+/**
+ * Mute TX.
+ * @param phy The AD9361 state structure.
+ * @param state The state - 0 mute; 1 - unmute.
+ * @return 0 in case of success
+ */
+int32_t ad9361_tx_mute(struct ad9361_rf_phy *phy, uint32_t state)
+{
+ int32_t ret;
+
+ if (state) {
+ phy->tx1_atten_cached = ad9361_get_tx_atten(phy, 1);
+ phy->tx2_atten_cached = ad9361_get_tx_atten(phy, 2);
+
+ return ad9361_set_tx_atten(phy, 89750, true, true, true);
+ } else {
+ if (phy->tx1_atten_cached == phy->tx2_atten_cached)
+ return ad9361_set_tx_atten(phy, phy->tx1_atten_cached,
+ true, true, true);
+
+ ret = ad9361_set_tx_atten(phy, phy->tx1_atten_cached,
+ true, false, true);
+ ret |= ad9361_set_tx_atten(phy, phy->tx2_atten_cached,
+ false, true, true);
+
+ return ret;
+ }
+}
+
+/**
+ * Choose the right RF VCO table index for the selected frequency.
+ * @param freq The frequency value [Hz].
+ * @return The index from the RF VCO table.
+ */
+static uint32_t ad9361_rfvco_tableindex(uint32_t freq)
+{
+ if (freq < 50000000UL)
+ return LUT_FTDD_40;
+
+ if (freq <= 70000000UL)
+ return LUT_FTDD_60;
+
+ return LUT_FTDD_80;
+}
+
+/**
+ * Initialize the RFPLL VCO.
+ * @param phy The AD9361 state structure.
+ * @param tx Set true for TX_RFPLL.
+ * @param vco_freq The VCO frequency [Hz].
+ * @param ref_clk The reference clock frequency [Hz].
+ * @return 0 in case of success
+ */
+static int32_t ad9361_rfpll_vco_init(struct ad9361_rf_phy *phy,
+ bool tx, uint64_t vco_freq,
+ uint32_t ref_clk)
+{
+ struct spi_device *spi = phy->spi;
+ const struct SynthLUT(*tab);
+ int32_t i = 0;
+ uint32_t range, offs = 0;
+
+ range = ad9361_rfvco_tableindex(ref_clk);
+
+ dev_dbg(&phy->spi->dev, "%s : vco_freq %"PRIu64" : ref_clk %"PRIu32" : range %"PRIu32,
+ __func__, vco_freq, ref_clk, range);
+
+ do_div(&vco_freq, 1000000UL); /* vco_freq in MHz */
+
+ if ((phy->pdata->fdd && !phy->pdata->fdd_independent_mode)
+ && (phy->current_tx_lo_freq != phy->current_rx_lo_freq)) {
+ tab = &SynthLUT_FDD[range][0];
+ if (tx)
+ phy->current_tx_use_tdd_table = false;
+ else
+ phy->current_rx_use_tdd_table = false;
+ } else {
+ tab = &SynthLUT_TDD[range][0];
+ if (tx)
+ phy->current_tx_use_tdd_table = true;
+ else
+ phy->current_rx_use_tdd_table = true;
+ }
+
+ if (tx)
+ offs = REG_TX_VCO_OUTPUT - REG_RX_VCO_OUTPUT;
+
+ while (i < SYNTH_LUT_SIZE && tab[i].VCO_MHz > vco_freq)
+ i++;
+
+ dev_dbg(&phy->spi->dev, "%s : freq %d MHz : index %"PRId32,
+ __func__, tab[i].VCO_MHz, i);
+
+ ad9361_spi_write(spi, REG_RX_VCO_OUTPUT + offs,
+ VCO_OUTPUT_LEVEL(tab[i].VCO_Output_Level) |
+ PORB_VCO_LOGIC);
+ ad9361_spi_writef(spi, REG_RX_ALC_VARACTOR + offs,
+ VCO_VARACTOR(~0), tab[i].VCO_Varactor);
+ ad9361_spi_write(spi, REG_RX_VCO_BIAS_1 + offs,
+ VCO_BIAS_REF(tab[i].VCO_Bias_Ref) |
+ VCO_BIAS_TCF(tab[i].VCO_Bias_Tcf));
+
+ ad9361_spi_write(spi, REG_RX_FORCE_VCO_TUNE_1 + offs,
+ VCO_CAL_OFFSET(tab[i].VCO_Cal_Offset));
+ ad9361_spi_write(spi, REG_RX_VCO_VARACTOR_CTRL_1 + offs,
+ VCO_VARACTOR_REFERENCE(
+ tab[i].VCO_Varactor_Reference));
+
+ ad9361_spi_write(spi, REG_RX_VCO_CAL_REF + offs, VCO_CAL_REF_TCF(0));
+
+ ad9361_spi_write(spi, REG_RX_VCO_VARACTOR_CTRL_0 + offs,
+ VCO_VARACTOR_OFFSET(0) |
+ VCO_VARACTOR_REFERENCE_TCF(7));
+
+ ad9361_spi_writef(spi, REG_RX_CP_CURRENT + offs, CHARGE_PUMP_CURRENT(~0),
+ tab[i].Charge_Pump_Current);
+ ad9361_spi_write(spi, REG_RX_LOOP_FILTER_1 + offs,
+ LOOP_FILTER_C2(tab[i].LF_C2) |
+ LOOP_FILTER_C1(tab[i].LF_C1));
+ ad9361_spi_write(spi, REG_RX_LOOP_FILTER_2 + offs,
+ LOOP_FILTER_R1(tab[i].LF_R1) |
+ LOOP_FILTER_C3(tab[i].LF_C3));
+ ad9361_spi_write(spi, REG_RX_LOOP_FILTER_3 + offs,
+ LOOP_FILTER_R3(tab[i].LF_R3));
+
+ return 0;
+}
+
+/**
+ * Get the current gain in Split Gain Table Mode
+ * @param phy The AD9361 state structure.
+ * @param idx_reg Register base address for the selected receiver
+ * @param rx_gain A rf_rx_gain struct to store the RF gain.
+ * @return 0 in case of success,
+ */
+static int32_t ad9361_get_split_table_gain(struct ad9361_rf_phy *phy, uint32_t idx_reg,
+struct rf_rx_gain *rx_gain)
+{
+ struct spi_device *spi = phy->spi;
+ uint32_t val, tbl_addr;
+ int32_t rc = 0;
+
+ rx_gain->fgt_lmt_index = ad9361_spi_readf(spi, idx_reg,
+ FULL_TABLE_GAIN_INDEX(~0));
+ tbl_addr = ad9361_spi_read(spi, REG_GAIN_TABLE_ADDRESS);
+
+ ad9361_spi_write(spi, REG_GAIN_TABLE_ADDRESS, rx_gain->fgt_lmt_index);
+
+ val = ad9361_spi_read(spi, REG_GAIN_TABLE_READ_DATA1);
+ rx_gain->lna_index = TO_LNA_GAIN(val);
+ rx_gain->mixer_index = TO_MIXER_GM_GAIN(val);
+
+ rx_gain->tia_index = ad9361_spi_readf(spi, REG_GAIN_TABLE_READ_DATA2, TIA_GAIN);
+
+ rx_gain->lmt_gain = lna_table[phy->current_table][rx_gain->lna_index] +
+ mixer_table[phy->current_table][rx_gain->mixer_index] +
+ tia_table[rx_gain->tia_index];
+
+ ad9361_spi_write(spi, REG_GAIN_TABLE_ADDRESS, tbl_addr);
+
+ /* Read LPF Index */
+ rx_gain->lpf_gain = ad9361_spi_readf(spi, idx_reg + 1, LPF_GAIN_RX(~0));
+
+ /* Read Digital Gain */
+ rx_gain->digital_gain = ad9361_spi_readf(spi, idx_reg + 2,
+ DIGITAL_GAIN_RX(~0));
+
+ rx_gain->gain_db = rx_gain->lmt_gain + rx_gain->lpf_gain +
+ rx_gain->digital_gain;
+ return rc;
+}
+
+/**
+ * Get the current gain in Full Gain Table Mode
+ * @param phy The AD9361 state structure.
+ * @param idx_reg Register base address for the selected receiver
+ * @param rx_gain A rf_rx_gain struct to store the RF gain.
+ * @return 0 in case of success
+ */
+static int32_t ad9361_get_full_table_gain(struct ad9361_rf_phy *phy, uint32_t idx_reg,
+struct rf_rx_gain *rx_gain)
+{
+ struct spi_device *spi = phy->spi;
+ int32_t val;
+ enum rx_gain_table_name tbl;
+ struct rx_gain_info *gain_info;
+ int32_t rc = 0, rx_gain_db;
+
+ tbl = ad9361_gt_tableindex(
+ ad9361_from_clk(clk_get_rate(phy, phy->ref_clk_scale[RX_RFPLL])));
+
+ rx_gain->fgt_lmt_index = val = ad9361_spi_readf(spi, idx_reg,
+ FULL_TABLE_GAIN_INDEX(~0));
+ gain_info = &phy->rx_gain[tbl];
+ if (val > gain_info->idx_step_offset) {
+ val = val - gain_info->idx_step_offset;
+ rx_gain_db = gain_info->starting_gain_db +
+ ((val)* gain_info->gain_step_db);
+ }
+ else {
+ rx_gain_db = gain_info->starting_gain_db;
+ }
+
+ /* Read Digital Gain */
+ rx_gain->digital_gain = ad9361_spi_readf(spi, idx_reg + 2,
+ DIGITAL_GAIN_RX(~0));
+
+ rx_gain->gain_db = rx_gain_db;
+
+ return rc;
+}
+
+/**
+ * Get current RX gain for the selected channel.
+ * @param phy The AD9361 state structure.
+ * @param rx_id The desired channel number (0, 1).
+ * @param rx_gain A rf_rx_gain struct to store the RF gain.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_rx_gain(struct ad9361_rf_phy *phy,
+ uint32_t rx_id, struct rf_rx_gain *rx_gain)
+{
+ struct spi_device *spi = phy->spi;
+ uint32_t val, idx_reg;
+ uint8_t gain_ctl_shift, rx_enable_mask;
+ uint8_t fast_atk_shift;
+ int32_t rc = 0;
+
+ if (rx_id == 1) {
+ gain_ctl_shift = RX1_GAIN_CTRL_SHIFT;
+ idx_reg = REG_GAIN_RX1;
+ rx_enable_mask = RX_CHANNEL_ENABLE(RX_1);
+ fast_atk_shift = RX1_FAST_ATK_SHIFT;
+
+ }
+ else if (rx_id == 2) {
+ gain_ctl_shift = RX2_GAIN_CTRL_SHIFT;
+ idx_reg = REG_GAIN_RX2;
+ rx_enable_mask = RX_CHANNEL_ENABLE(RX_2);
+ fast_atk_shift = RX2_FAST_ATK_SHIFT;
+ }
+ else {
+ dev_err(dev, "Unknown Rx path %"PRIu32, rx_id);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ val = ad9361_spi_readf(spi, REG_RX_ENABLE_FILTER_CTRL, rx_enable_mask);
+
+ if (!val) {
+ dev_dbg(dev, "Rx%"PRIu32" is not enabled", rx_gain->ant);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ val = ad9361_spi_read(spi, REG_AGC_CONFIG_1);
+
+ val = (val >> gain_ctl_shift) & RX_GAIN_CTL_MASK;
+
+ if (val == RX_GAIN_CTL_AGC_FAST_ATK) {
+ /* In fast attack mode check whether Fast attack state machine
+ * has locked gain, if not then we can not read gain.
+ */
+ val = ad9361_spi_read(spi, REG_FAST_ATTACK_STATE);
+ val = (val >> fast_atk_shift) & FAST_ATK_MASK;
+ if (val != FAST_ATK_GAIN_LOCKED) {
+ dev_warn(dev, "Failed to read gain, state m/c at %"PRIx32,
+ val);
+ rc = -EAGAIN;
+ goto out;
+ }
+ }
+
+ if (has_split_gt && phy->pdata->split_gt)
+ rc = ad9361_get_split_table_gain(phy, idx_reg, rx_gain);
+ else
+ rc = ad9361_get_full_table_gain(phy, idx_reg, rx_gain);
+
+out:
+ return rc;
+}
+
+/**
+ * Force Enable State Machine (ENSM) to the desired state (internally used only).
+ * @param phy The AD9361 state structure.
+ * @param ensm_state The ENSM state [ENSM_STATE_SLEEP_WAIT, ENSM_STATE_ALERT,
+ * ENSM_STATE_TX, ENSM_STATE_TX_FLUSH, ENSM_STATE_RX,
+ * ENSM_STATE_RX_FLUSH, ENSM_STATE_FDD, ENSM_STATE_FDD_FLUSH].
+ * @return None.
+ */
+void ad9361_ensm_force_state(struct ad9361_rf_phy *phy, uint8_t ensm_state)
+{
+ struct spi_device *spi = phy->spi;
+ uint8_t dev_ensm_state;
+ int32_t rc;
+ uint32_t val;
+
+ dev_ensm_state = ad9361_spi_readf(spi, REG_STATE, ENSM_STATE(~0));
+
+ phy->prev_ensm_state = dev_ensm_state;
+
+ if (dev_ensm_state == ensm_state) {
+ dev_dbg(dev, "Nothing to do, device is already in %d state",
+ ensm_state);
+ goto out;
+ }
+
+ dev_dbg(dev, "Device is in %x state, forcing to %x", dev_ensm_state,
+ ensm_state);
+
+ val = ad9361_spi_read(spi, REG_ENSM_CONFIG_1);
+
+ /* Enable control through SPI writes, and take out from
+ * Alert
+ */
+ if (val & ENABLE_ENSM_PIN_CTRL) {
+ val &= ~ENABLE_ENSM_PIN_CTRL;
+ phy->ensm_pin_ctl_en = true;
+ }
+ else {
+ phy->ensm_pin_ctl_en = false;
+ }
+
+ if (dev_ensm_state)
+ val &= ~(TO_ALERT);
+
+ switch (ensm_state) {
+
+ case ENSM_STATE_TX:
+ case ENSM_STATE_FDD:
+ val |= FORCE_TX_ON;
+ break;
+ case ENSM_STATE_RX:
+ val |= FORCE_RX_ON;
+ break;
+ case ENSM_STATE_ALERT:
+ val &= ~(FORCE_TX_ON | FORCE_RX_ON);
+ val |= TO_ALERT | FORCE_ALERT_STATE;
+ break;
+ default:
+ dev_err(dev, "No handling for forcing %d ensm state",
+ ensm_state);
+ goto out;
+ }
+
+ ad9361_spi_write(spi, REG_ENSM_CONFIG_1, TO_ALERT | FORCE_ALERT_STATE);
+
+ rc = ad9361_spi_write(spi, REG_ENSM_CONFIG_1, val);
+ if (rc)
+ dev_err(dev, "Failed to restore state");
+
+out:
+ return;
+
+}
+
+/**
+ * Restore the previous Enable State Machine (ENSM) state.
+ * @param phy The AD9361 state structure.
+ * @return None.
+ */
+void ad9361_ensm_restore_prev_state(struct ad9361_rf_phy *phy)
+{
+ struct spi_device *spi = phy->spi;
+ int32_t rc;
+ uint32_t val;
+
+ val = ad9361_spi_read(spi, REG_ENSM_CONFIG_1);
+
+ /* We are restoring state only, so clear State bits first
+ * which might have set while forcing a particular state
+ */
+ val &= ~(FORCE_TX_ON | FORCE_RX_ON |
+ TO_ALERT | FORCE_ALERT_STATE);
+
+ switch (phy->prev_ensm_state) {
+
+ case ENSM_STATE_TX:
+ case ENSM_STATE_FDD:
+ val |= FORCE_TX_ON;
+ break;
+ case ENSM_STATE_RX:
+ val |= FORCE_RX_ON;
+ break;
+ case ENSM_STATE_ALERT:
+ val |= TO_ALERT;
+ break;
+ case ENSM_STATE_INVALID:
+ dev_dbg(dev, "No need to restore, ENSM state wasn't saved");
+ goto out;
+ default:
+ dev_dbg(dev, "Could not restore to %d ENSM state",
+ phy->prev_ensm_state);
+ goto out;
+ }
+
+ ad9361_spi_write(spi, REG_ENSM_CONFIG_1, TO_ALERT | FORCE_ALERT_STATE);
+
+ rc = ad9361_spi_write(spi, REG_ENSM_CONFIG_1, val);
+ if (rc) {
+ dev_err(dev, "Failed to write ENSM_CONFIG_1");
+ goto out;
+ }
+
+ if (phy->ensm_pin_ctl_en) {
+ val |= ENABLE_ENSM_PIN_CTRL;
+ rc = ad9361_spi_write(spi, REG_ENSM_CONFIG_1, val);
+ if (rc)
+ dev_err(dev, "Failed to write ENSM_CONFIG_1");
+ }
+
+out:
+ return;
+}
+
+/**
+ * Set gain in Split Gain Table Mode (used only in Manual Gain Control Mode).
+ * @param phy The AD9361 state structure.
+ * @param idx_reg Register base address for the selected receiver
+ * @param rx_gain The rf_rx_gain struct containing the RF gain.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t set_split_table_gain(struct ad9361_rf_phy *phy, uint32_t idx_reg,
+struct rf_rx_gain *rx_gain)
+{
+ struct spi_device *spi = phy->spi;
+ int32_t rc = 0;
+
+ if ((rx_gain->fgt_lmt_index > MAX_LMT_INDEX) ||
+ (rx_gain->lpf_gain > MAX_LPF_GAIN) ||
+ (rx_gain->digital_gain > MAX_DIG_GAIN)) {
+ dev_err(dev, "LMT_INDEX missing or greater than max value %d",
+ MAX_LMT_INDEX);
+ dev_err(dev, "LPF_GAIN missing or greater than max value %d",
+ MAX_LPF_GAIN);
+ dev_err(dev, "DIGITAL_GAIN cannot be more than %d",
+ MAX_DIG_GAIN);
+ rc = -EINVAL;
+ goto out;
+ }
+ if (rx_gain->gain_db > 0)
+ dev_dbg(dev, "Ignoring rx_gain value in split table mode.");
+ if (rx_gain->fgt_lmt_index == 0 && rx_gain->lpf_gain == 0 &&
+ rx_gain->digital_gain == 0) {
+ dev_err(dev,
+ "In split table mode, All LMT/LPF/digital gains cannot be 0");
+ rc = -EINVAL;
+ goto out;
+ }
+
+ ad9361_spi_writef(spi, idx_reg, RX_FULL_TBL_IDX_MASK, rx_gain->fgt_lmt_index);
+ ad9361_spi_writef(spi, idx_reg + 1, RX_LPF_IDX_MASK, rx_gain->lpf_gain);
+
+ if (phy->pdata->gain_ctrl.dig_gain_en) {
+ ad9361_spi_writef(spi, idx_reg + 2, RX_DIGITAL_IDX_MASK, rx_gain->digital_gain);
+
+ }
+ else if (rx_gain->digital_gain > 0) {
+ dev_err(dev, "Digital gain is disabled and cannot be set");
+ }
+out:
+ return rc;
+}
+
+/**
+ * Set gain in Full Gain Table Mode (used only in Manual Gain Control Mode).
+ * @param phy The AD9361 state structure.
+ * @param idx_reg Register base address for the selected receiver
+ * @param rx_gain The rf_rx_gain struct containing the RF gain.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t set_full_table_gain(struct ad9361_rf_phy *phy, uint32_t idx_reg,
+struct rf_rx_gain *rx_gain)
+{
+ struct spi_device *spi = phy->spi;
+ enum rx_gain_table_name tbl;
+ struct rx_gain_info *gain_info;
+ uint32_t val;
+ int32_t rc = 0;
+
+ if (rx_gain->fgt_lmt_index != (uint32_t)~0 || (int64_t)rx_gain->lpf_gain != (uint32_t)~0 ||
+ rx_gain->digital_gain > 0)
+ dev_dbg(dev,
+ "Ignoring lmt/lpf/digital gains in Single Table mode");
+
+ tbl = ad9361_gt_tableindex(
+ ad9361_from_clk(clk_get_rate(phy, phy->ref_clk_scale[RX_RFPLL])));
+
+ gain_info = &phy->rx_gain[tbl];
+ if ((rx_gain->gain_db < gain_info->starting_gain_db) ||
+ (rx_gain->gain_db > gain_info->max_gain_db)) {
+
+ dev_err(dev, "Invalid gain %"PRId32", supported range [%"PRId32" - %"PRId32"]",
+ rx_gain->gain_db, gain_info->starting_gain_db,
+ gain_info->max_gain_db);
+ rc = -EINVAL;
+ goto out;
+
+ }
+
+ val = ((rx_gain->gain_db - gain_info->starting_gain_db) /
+ gain_info->gain_step_db) + gain_info->idx_step_offset;
+ ad9361_spi_writef(spi, idx_reg, RX_FULL_TBL_IDX_MASK, val);
+
+out:
+ return rc;
+}
+
+/**
+ * Set the RX gain for the selected channel.
+ * @param phy The AD9361 state structure.
+ * @param rx_id The desired channel number (0, 1).
+ * @param rx_gain The rf_rx_gain struct containing the RF gain.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_set_rx_gain(struct ad9361_rf_phy *phy,
+ uint32_t rx_id, struct rf_rx_gain *rx_gain)
+{
+ struct spi_device *spi = phy->spi;
+ uint32_t val, idx_reg;
+ uint8_t gain_ctl_shift;
+ int32_t rc = 0;
+
+ if (rx_id == 1) {
+ gain_ctl_shift = RX1_GAIN_CTRL_SHIFT;
+ idx_reg = REG_RX1_MANUAL_LMT_FULL_GAIN;
+
+ }
+ else if (rx_id == 2) {
+ gain_ctl_shift = RX2_GAIN_CTRL_SHIFT;
+ idx_reg = REG_RX2_MANUAL_LMT_FULL_GAIN;
+ }
+ else {
+ dev_err(dev, "Unknown Rx path %"PRIu32, rx_id);
+ rc = -EINVAL;
+ goto out;
+
+ }
+
+ val = ad9361_spi_read(spi, REG_AGC_CONFIG_1);
+ val = (val >> gain_ctl_shift) & RX_GAIN_CTL_MASK;
+
+ if (val != RX_GAIN_CTL_MGC) {
+ dev_dbg(dev, "Rx gain can be set in MGC mode only");
+ goto out;
+ }
+
+ if (has_split_gt && phy->pdata->split_gt)
+ rc = set_split_table_gain(phy, idx_reg, rx_gain);
+ else
+ rc = set_full_table_gain(phy, idx_reg, rx_gain);
+
+ if (rc) {
+ dev_err(dev, "Unable to write gain tbl idx reg: %"PRIu32, idx_reg);
+ goto out;
+ }
+
+out:
+ return rc;
+
+}
+
+/**
+ * Initialize the rx_gain_info structure.
+ * @param rx_gain The rx_gain_info structure pointer.
+ * @param type Either Full or Split Table
+ * @param starting_gain The starting gain value.
+ * @param max_gain The maximum gain value.
+ * @param gain_step The gain step.
+ * @param max_idx The max table size.
+ * @param idx_offset Offset in the table where linear progression starts
+ * @return None
+ */
+static void ad9361_init_gain_info(struct rx_gain_info *rx_gain,
+enum rx_gain_table_type type, int32_t starting_gain,
+ int32_t max_gain, int32_t gain_step, int32_t max_idx, int32_t idx_offset)
+{
+ rx_gain->tbl_type = type;
+ rx_gain->starting_gain_db = starting_gain;
+ rx_gain->max_gain_db = max_gain;
+ rx_gain->gain_step_db = gain_step;
+ rx_gain->max_idx = max_idx;
+ rx_gain->idx_step_offset = idx_offset;
+}
+
+/**
+ * Initialize the gain table information.
+ * @param phy The AD9361 state structure.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_init_gain_tables(struct ad9361_rf_phy *phy)
+{
+ struct rx_gain_info *rx_gain;
+
+ /* Intialize Meta data according to default gain tables
+ * of AD9631. Changing/Writing of gain tables is not
+ * supported yet.
+ */
+ rx_gain = &phy->rx_gain[TBL_200_1300_MHZ];
+ ad9361_init_gain_info(rx_gain, RXGAIN_FULL_TBL, 1, 77, 1,
+ SIZE_FULL_TABLE, 0);
+
+ rx_gain = &phy->rx_gain[TBL_1300_4000_MHZ];
+ ad9361_init_gain_info(rx_gain, RXGAIN_FULL_TBL, -4, 71, 1,
+ SIZE_FULL_TABLE, 1);
+
+ rx_gain = &phy->rx_gain[TBL_4000_6000_MHZ];
+ ad9361_init_gain_info(rx_gain, RXGAIN_FULL_TBL, -10, 62, 1,
+ SIZE_FULL_TABLE, 4);
+
+ return 0;
+}
+
+/**
+ * Update the Gain Control.
+ * @param phy The AD9361 state structure.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_gc_update(struct ad9361_rf_phy *phy)
+{
+ struct spi_device *spi = phy->spi;
+ uint32_t clkrf;
+ uint32_t reg, delay_lna, settling_delay, dec_pow_meas_dur;
+ int32_t ret;
+ uint32_t fir_div;
+
+ clkrf = clk_get_rate(phy, phy->ref_clk_scale[CLKRF_CLK]);
+ delay_lna = phy->pdata->elna_ctrl.settling_delay_ns;
+
+ /*
+ * AGC Attack Delay (us)=ceiling((((0.2+Delay_LNA)*ClkRF+14))/(2*ClkRF))+1
+ * ClkRF in MHz, delay in us
+ */
+
+ reg = (200 * delay_lna) / 2 + (14000000UL / (clkrf / 500U));
+ reg = DIV_ROUND_UP(reg, 1000UL) +
+ phy->pdata->gain_ctrl.agc_attack_delay_extra_margin_us;
+ reg = clamp_t(uint8_t, reg, 0U, 31U);
+ ret = ad9361_spi_writef(spi, REG_AGC_ATTACK_DELAY,
+ AGC_ATTACK_DELAY(~0), reg);
+
+ /*
+ * Peak Overload Wait Time (ClkRF cycles)=ceiling((0.1+Delay_LNA) *clkRF+1)
+ */
+
+ reg = (delay_lna + 100UL) * (clkrf / 1000UL);
+ reg = DIV_ROUND_UP(reg, 1000000UL) + 1;
+ reg = clamp_t(uint8_t, reg, 0U, 31U);
+ ret |= ad9361_spi_writef(spi, REG_PEAK_WAIT_TIME,
+ PEAK_OVERLOAD_WAIT_TIME(~0), reg);
+
+ /*
+ * Settling Delay in 0x111. Applies to all gain control modes:
+ * 0x111[D4:D0]= ceiling(((0.2+Delay_LNA)*clkRF
+ dodebug = false;+14)/2)
+ */
+
+ reg = (delay_lna + 200UL) * (clkrf / 2000UL);
+ reg = DIV_ROUND_UP(reg, 1000000UL) + 7;
+ reg = settling_delay = clamp_t(uint8_t, reg, 0U, 31U);
+ ret |= ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY,
+ SETTLING_DELAY(~0), reg);
+
+ /*
+ * Gain Update Counter [15:0]= round((((time*ClkRF-0x111[D4:D0]*2)-2))/2)
+ */
+ reg = phy->pdata->gain_ctrl.gain_update_interval_us * (clkrf / 1000UL) -
+ settling_delay * 2000UL - 2000UL;
+
+ reg = DIV_ROUND_CLOSEST(reg, 2000UL);
+ reg = clamp_t(uint32_t, reg, 0U, 131071UL);
+
+ if (phy->agc_mode[0] == RF_GAIN_FASTATTACK_AGC ||
+ phy->agc_mode[1] == RF_GAIN_FASTATTACK_AGC) {
+ dec_pow_meas_dur =
+ phy->pdata->gain_ctrl.f_agc_dec_pow_measuremnt_duration;
+ } else {
+ fir_div = DIV_ROUND_CLOSEST(clkrf,
+ clk_get_rate(phy, phy->ref_clk_scale[RX_SAMPL_CLK]));
+ dec_pow_meas_dur = phy->pdata->gain_ctrl.dec_pow_measuremnt_duration;
+
+ if (((reg * 2 / fir_div) / dec_pow_meas_dur) < 2) {
+ dec_pow_meas_dur = reg / fir_div;
+ }
+ }
+
+ /* Power Measurement Duration */
+ ad9361_spi_writef(spi, REG_DEC_POWER_MEASURE_DURATION_0,
+ DEC_POWER_MEASUREMENT_DURATION(~0),
+ ilog2(dec_pow_meas_dur / 16));
+
+
+ ret |= ad9361_spi_writef(spi, REG_DIGITAL_SAT_COUNTER,
+ DOUBLE_GAIN_COUNTER, reg > 65535);
+
+ if (reg > 65535)
+ reg /= 2;
+
+ ret |= ad9361_spi_write(spi, REG_GAIN_UPDATE_COUNTER1, reg & 0xFF);
+ ret |= ad9361_spi_write(spi, REG_GAIN_UPDATE_COUNTER2, reg >> 8);
+
+ /*
+ * Fast AGC State Wait Time - Energy Detect Count
+ */
+
+ reg = DIV_ROUND_CLOSEST(phy->pdata->gain_ctrl.f_agc_state_wait_time_ns *
+ (clkrf / 1000UL), 1000000UL);
+ reg = clamp_t(uint32_t, reg, 0U, 31U);
+ ret |= ad9361_spi_writef(spi, REG_FAST_ENERGY_DETECT_COUNT,
+ ENERGY_DETECT_COUNT(~0), reg);
+
+ return ret;
+}
+
+/**
+ * Set the gain control mode.
+ * @param phy The AD9361 state structure.
+ * @param rf_gain_ctrl A rf_gain_ctrl struct that contains the the desired
+ * channel information and the gain control mode.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_set_gain_ctrl_mode(struct ad9361_rf_phy *phy,
+ struct rf_gain_ctrl *gain_ctrl)
+{
+ struct spi_device *spi = phy->spi;
+ int32_t rc = 0;
+ uint32_t gain_ctl_shift, mode;
+ uint8_t val;
+
+ rc = ad9361_spi_readm(spi, REG_AGC_CONFIG_1, &val, 1);
+ if (rc) {
+ dev_err(dev, "Unable to read AGC config1 register: %x",
+ REG_AGC_CONFIG_1);
+ goto out;
+ }
+
+ switch (gain_ctrl->mode) {
+ case RF_GAIN_MGC:
+ mode = RX_GAIN_CTL_MGC;
+ break;
+ case RF_GAIN_FASTATTACK_AGC:
+ mode = RX_GAIN_CTL_AGC_FAST_ATK;
+ break;
+ case RF_GAIN_SLOWATTACK_AGC:
+ mode = RX_GAIN_CTL_AGC_SLOW_ATK;
+ break;
+ case RF_GAIN_HYBRID_AGC:
+ mode = RX_GAIN_CTL_AGC_SLOW_ATK_HYBD;
+ break;
+ default:
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (gain_ctrl->ant == 1) {
+ gain_ctl_shift = RX1_GAIN_CTRL_SHIFT;
+ }
+ else if (gain_ctrl->ant == 2) {
+ gain_ctl_shift = RX2_GAIN_CTRL_SHIFT;
+ }
+ else {
+ dev_err(dev, "Unknown Rx path %"PRIu32, gain_ctrl->ant);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = ad9361_en_dis_rx(phy, gain_ctrl->ant, RX_DISABLE);
+ if (rc) {
+ dev_err(dev, "Unable to disable rx%"PRIu32, gain_ctrl->ant);
+ goto out;
+ }
+
+ val &= ~(RX_GAIN_CTL_MASK << gain_ctl_shift);
+ val |= mode << gain_ctl_shift;
+ if (mode == RX_GAIN_CTL_AGC_SLOW_ATK_HYBD)
+ val |= SLOW_ATTACK_HYBRID_MODE;
+ else
+ val &= ~SLOW_ATTACK_HYBRID_MODE;
+
+ rc = ad9361_spi_write(spi, REG_AGC_CONFIG_1, val);
+ if (rc) {
+ dev_err(dev, "Unable to write AGC config1 register: %x",
+ REG_AGC_CONFIG_1);
+ goto out;
+ }
+
+ ad9361_en_dis_rx(phy, gain_ctrl->ant, RX_ENABLE);
+ rc = ad9361_gc_update(phy);
+out:
+ return rc;
+}
+
+/**
+ * Get the RSSI.
+ * @param phy The AD9361 state structure.
+ * @param rssi A rf_rssi struct to store the RSSI.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_read_rssi(struct ad9361_rf_phy *phy, struct rf_rssi *rssi)
+{
+ struct spi_device *spi = phy->spi;
+ uint8_t reg_val_buf[6];
+ int32_t rc;
+
+ rc = ad9361_spi_readm(spi, REG_PREAMBLE_LSB,
+ reg_val_buf, ARRAY_SIZE(reg_val_buf));
+ if (rssi->ant == 1) {
+ rssi->symbol = RSSI_RESOLUTION *
+ ((reg_val_buf[5] << RSSI_LSB_SHIFT) +
+ (reg_val_buf[1] & RSSI_LSB_MASK1));
+ rssi->preamble = RSSI_RESOLUTION *
+ ((reg_val_buf[4] << RSSI_LSB_SHIFT) +
+ (reg_val_buf[0] & RSSI_LSB_MASK1));
+ }
+ else if (rssi->ant == 2) {
+ rssi->symbol = RSSI_RESOLUTION *
+ ((reg_val_buf[3] << RSSI_LSB_SHIFT) +
+ ((reg_val_buf[1] & RSSI_LSB_MASK2) >> 1));
+ rssi->preamble = RSSI_RESOLUTION *
+ ((reg_val_buf[2] << RSSI_LSB_SHIFT) +
+ ((reg_val_buf[0] & RSSI_LSB_MASK2) >> 1));
+ }
+ else
+ rc = -EFAULT;
+
+ rssi->multiplier = RSSI_MULTIPLIER;
+
+ return rc;
+}
+
+/**
+ * Setup the RX ADC.
+ * @param phy The AD9361 state structure.
+ * @param bbpll_freq The BBPLL frequency [Hz].
+ * @param adc_sampl_freq_Hz The ADC sampling frequency [Hz].
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_rx_adc_setup(struct ad9361_rf_phy *phy, uint32_t bbpll_freq,
+ uint32_t adc_sampl_freq_Hz)
+{
+
+ uint32_t scale_snr_1e3, maxsnr, sqrt_inv_rc_tconst_1e3, tmp_1e3,
+ scaled_adc_clk_1e6, inv_scaled_adc_clk_1e3, sqrt_term_1e3,
+ min_sqrt_term_1e3, bb_bw_Hz;
+ uint64_t tmp, invrc_tconst_1e6;
+ uint8_t data[40];
+ uint32_t i;
+ int32_t ret;
+
+ uint8_t c3_msb = ad9361_spi_read(phy->spi, REG_RX_BBF_C3_MSB);
+ uint8_t c3_lsb = ad9361_spi_read(phy->spi, REG_RX_BBF_C3_LSB);
+ uint8_t r2346 = ad9361_spi_read(phy->spi, REG_RX_BBF_R2346);
+
+ /*
+ * BBBW = (BBPLL / RxTuneDiv) * ln(2) / (1.4 * 2PI )
+ * We assume ad9361_rx_bb_analog_filter_calib() is always run prior
+ */
+
+ tmp = bbpll_freq * 10000ULL;
+ do_div(&tmp, 126906UL * phy->rxbbf_div);
+ bb_bw_Hz = tmp;
+
+ dev_dbg(&phy->spi->dev, "%s : BBBW %"PRIu32" : ADCfreq %"PRIu32,
+ __func__, bb_bw_Hz, adc_sampl_freq_Hz);
+
+ dev_dbg(&phy->spi->dev, "c3_msb 0x%X : c3_lsb 0x%X : r2346 0x%X : ",
+ c3_msb, c3_lsb, r2346);
+
+ bb_bw_Hz = clamp(bb_bw_Hz, 200000UL, 28000000UL);
+
+ if (adc_sampl_freq_Hz < 80000000)
+ scale_snr_1e3 = 1000;
+ else
+ scale_snr_1e3 = 1585; /* pow(10, scale_snr_dB/10); */
+
+ if (bb_bw_Hz >= 18000000) {
+ invrc_tconst_1e6 = (160975ULL * r2346 *
+ (160 * c3_msb + 10 * c3_lsb + 140) *
+ (bb_bw_Hz)* (1000 + (10 * (bb_bw_Hz - 18000000) / 1000000)));
+
+ do_div(&invrc_tconst_1e6, 1000UL);
+
+ }
+ else {
+ invrc_tconst_1e6 = (160975ULL * r2346 *
+ (160 * c3_msb + 10 * c3_lsb + 140) *
+ (bb_bw_Hz));
+ }
+
+ do_div(&invrc_tconst_1e6, 1000000000UL);
+
+ if (invrc_tconst_1e6 > ULONG_MAX)
+ dev_err(&phy->spi->dev, "invrc_tconst_1e6 > ULONG_MAX");
+
+ sqrt_inv_rc_tconst_1e3 = int_sqrt((uint32_t)invrc_tconst_1e6);
+ maxsnr = 640 / 160;
+ scaled_adc_clk_1e6 = DIV_ROUND_CLOSEST(adc_sampl_freq_Hz, 640);
+ inv_scaled_adc_clk_1e3 = DIV_ROUND_CLOSEST(640000000,
+ DIV_ROUND_CLOSEST(adc_sampl_freq_Hz, 1000));
+ tmp_1e3 = DIV_ROUND_CLOSEST(980000 + 20 * max_t(uint32_t, 1000U,
+ DIV_ROUND_CLOSEST(inv_scaled_adc_clk_1e3, maxsnr)), 1000);
+ sqrt_term_1e3 = int_sqrt(scaled_adc_clk_1e6);
+ min_sqrt_term_1e3 = min_t(uint32_t, 1000U,
+ int_sqrt(maxsnr * scaled_adc_clk_1e6));
+
+ dev_dbg(&phy->spi->dev, "invrc_tconst_1e6 %"PRIu64", sqrt_inv_rc_tconst_1e3 %"PRIu32,
+ invrc_tconst_1e6, sqrt_inv_rc_tconst_1e3);
+ dev_dbg(&phy->spi->dev, "scaled_adc_clk_1e6 %"PRIu32", inv_scaled_adc_clk_1e3 %"PRIu32,
+ scaled_adc_clk_1e6, inv_scaled_adc_clk_1e3);
+ dev_dbg(&phy->spi->dev, "tmp_1e3 %"PRIu32", sqrt_term_1e3 %"PRIu32", min_sqrt_term_1e3 %"PRIu32,
+ tmp_1e3, sqrt_term_1e3, min_sqrt_term_1e3);
+
+ data[0] = 0;
+ data[1] = 0;
+ data[2] = 0;
+ data[3] = 0x24;
+ data[4] = 0x24;
+ data[5] = 0;
+ data[6] = 0;
+
+ tmp = -50000000 + 8ULL * scale_snr_1e3 * sqrt_inv_rc_tconst_1e3 *
+ min_sqrt_term_1e3;
+ do_div(&tmp, 100000000UL);
+ data[7] = min_t(uint64_t, 124U, tmp);
+
+ tmp = (invrc_tconst_1e6 >> 1) + 20 * inv_scaled_adc_clk_1e3 *
+ data[7] / 80 * 1000ULL;
+ do_div(&tmp, invrc_tconst_1e6);
+ data[8] = min_t(uint64_t, 255U, tmp);
+
+ tmp = (-500000 + 77ULL * sqrt_inv_rc_tconst_1e3 * min_sqrt_term_1e3);
+ do_div(&tmp, 1000000UL);
+ data[10] = min_t(uint64_t, 127U, tmp);
+
+ data[9] = min_t(uint32_t, 127U, ((800 * data[10]) / 1000));
+ tmp = ((invrc_tconst_1e6 >> 1) + (20 * inv_scaled_adc_clk_1e3 *
+ data[10] * 1000ULL));
+ do_div(&tmp, invrc_tconst_1e6 * 77);
+ data[11] = min_t(uint64_t, 255U, tmp);
+ data[12] = min_t(uint32_t, 127U, (-500000 + 80 * sqrt_inv_rc_tconst_1e3 *
+ min_sqrt_term_1e3) / 1000000UL);
+
+ tmp = -3 * (long)(invrc_tconst_1e6 >> 1) + inv_scaled_adc_clk_1e3 *
+ data[12] * (1000ULL * 20 / 80);
+ do_div(&tmp, invrc_tconst_1e6);
+ data[13] = min_t(uint64_t, 255, tmp);
+
+ data[14] = 21 * (inv_scaled_adc_clk_1e3 / 10000);
+ data[15] = min_t(uint32_t, 127U, (500 + 1025 * data[7]) / 1000);
+ data[16] = min_t(uint32_t, 127U, (data[15] * tmp_1e3) / 1000);
+ data[17] = data[15];
+ data[18] = min_t(uint32_t, 127U, (500 + 975 * data[10]) / 1000);
+ data[19] = min_t(uint32_t, 127U, (data[18] * tmp_1e3) / 1000);
+ data[20] = data[18];
+ data[21] = min_t(uint32_t, 127U, (500 + 975 * data[12]) / 1000);
+ data[22] = min_t(uint32_t, 127, (data[21] * tmp_1e3) / 1000);
+ data[23] = data[21];
+ data[24] = 0x2E;
+ data[25] = (128 + min_t(uint32_t, 63000U, DIV_ROUND_CLOSEST(63 *
+ scaled_adc_clk_1e6, 1000)) / 1000);
+ data[26] = min_t(uint32_t, 63U, 63 * scaled_adc_clk_1e6 / 1000000 *
+ (920 + 80 * inv_scaled_adc_clk_1e3 / 1000) / 1000);
+ data[27] = min_t(uint32_t, 63, (32 * sqrt_term_1e3) / 1000);
+ data[28] = data[25];
+ data[29] = data[26];
+ data[30] = data[27];
+ data[31] = data[25];
+ data[32] = data[26];
+ data[33] = min_t(uint32_t, 63U, 63 * sqrt_term_1e3 / 1000);
+ data[34] = min_t(uint32_t, 127U, 64 * sqrt_term_1e3 / 1000);
+ data[35] = 0x40;
+ data[36] = 0x40;
+ data[37] = 0x2C;
+ data[38] = 0x00;
+ data[39] = 0x00;
+
+ for (i = 0; i < 40; i++) {
+ ret = ad9361_spi_write(phy->spi, 0x200 + i, data[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * Perform a RX TIA calibration.
+ * @param phy The AD9361 state structure.
+ * @param bb_bw_Hz The baseband bandwidth [Hz].
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_rx_tia_calib(struct ad9361_rf_phy *phy, uint32_t bb_bw_Hz)
+{
+ uint32_t Cbbf, R2346;
+ uint64_t CTIA_fF;
+
+ uint8_t reg1EB = ad9361_spi_read(phy->spi, REG_RX_BBF_C3_MSB);
+ uint8_t reg1EC = ad9361_spi_read(phy->spi, REG_RX_BBF_C3_LSB);
+ uint8_t reg1E6 = ad9361_spi_read(phy->spi, REG_RX_BBF_R2346);
+ uint8_t reg1DB, reg1DF, reg1DD, reg1DC, reg1DE, temp;
+
+ dev_dbg(&phy->spi->dev, "%s : bb_bw_Hz %"PRIu32,
+ __func__, bb_bw_Hz);
+
+ bb_bw_Hz = clamp(bb_bw_Hz, 200000UL, 20000000UL);
+
+ Cbbf = (reg1EB * 160) + (reg1EC * 10) + 140; /* fF */
+ R2346 = 18300 * RX_BBF_R2346(reg1E6);
+
+ CTIA_fF = Cbbf * R2346 * 560ULL;
+ do_div(&CTIA_fF, 3500000UL);
+
+ if (bb_bw_Hz <= 3000000UL)
+ reg1DB = 0xE0;
+ else if (bb_bw_Hz <= 10000000UL)
+ reg1DB = 0x60;
+ else
+ reg1DB = 0x20;
+
+ if (CTIA_fF > 2920ULL) {
+ reg1DC = 0x40;
+ reg1DE = 0x40;
+ temp = min(127U, DIV_ROUND_CLOSEST((uint32_t)CTIA_fF - 400, 320U));
+ reg1DD = temp;
+ reg1DF = temp;
+ }
+ else {
+ temp = DIV_ROUND_CLOSEST((uint32_t)CTIA_fF - 400, 40U) + 0x40;
+ reg1DC = temp;
+ reg1DE = temp;
+ reg1DD = 0;
+ reg1DF = 0;
+ }
+
+ ad9361_spi_write(phy->spi, REG_RX_TIA_CONFIG, reg1DB);
+ ad9361_spi_write(phy->spi, REG_TIA1_C_LSB, reg1DC);
+ ad9361_spi_write(phy->spi, REG_TIA1_C_MSB, reg1DD);
+ ad9361_spi_write(phy->spi, REG_TIA2_C_LSB, reg1DE);
+ ad9361_spi_write(phy->spi, REG_TIA2_C_MSB, reg1DF);
+
+ return 0;
+}
+
+/**
+ * Perform a baseband RX analog filter calibration.
+ * @param phy The AD9361 state structure.
+ * @param rx_bb_bw The baseband bandwidth [Hz].
+ * @param bbpll_freq The BBPLL frequency [Hz].
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_rx_bb_analog_filter_calib(struct ad9361_rf_phy *phy,
+ uint32_t rx_bb_bw,
+ uint32_t bbpll_freq)
+{
+ uint32_t target;
+ uint8_t tmp;
+ int32_t ret;
+
+ dev_dbg(&phy->spi->dev, "%s : rx_bb_bw %"PRIu32" bbpll_freq %"PRIu32,
+ __func__, rx_bb_bw, bbpll_freq);
+
+ rx_bb_bw = clamp(rx_bb_bw, 200000UL, 28000000UL);
+
+ /* 1.4 * BBBW * 2PI / ln(2) */
+ target = 126906UL * (rx_bb_bw / 10000UL);
+ phy->rxbbf_div = min_t(uint32_t, 511UL, DIV_ROUND_UP(bbpll_freq, target));
+
+ /* Set RX baseband filter divide value */
+ ad9361_spi_write(phy->spi, REG_RX_BBF_TUNE_DIVIDE, phy->rxbbf_div);
+ ad9361_spi_writef(phy->spi, REG_RX_BBF_TUNE_CONFIG, BIT(0), phy->rxbbf_div >> 8);
+
+ /* Write the BBBW into registers 0x1FB and 0x1FC */
+ ad9361_spi_write(phy->spi, REG_RX_BBBW_MHZ, rx_bb_bw / 1000000UL);
+
+ tmp = DIV_ROUND_CLOSEST((rx_bb_bw % 1000000UL) * 128, 1000000UL);
+ ad9361_spi_write(phy->spi, REG_RX_BBBW_KHZ, min_t(uint8_t, 127, tmp));
+
+ ad9361_spi_write(phy->spi, REG_RX_MIX_LO_CM, RX_MIX_LO_CM(0x3F)); /* Set Rx Mix LO CM */
+ ad9361_spi_write(phy->spi, REG_RX_MIX_GM_CONFIG, RX_MIX_GM_PLOAD(3)); /* Set GM common mode */
+
+ /* Enable the RX BBF tune circuit by writing 0x1E2=0x02 and 0x1E3=0x02 */
+ ad9361_spi_write(phy->spi, REG_RX1_TUNE_CTRL, RX1_TUNE_RESAMPLE);
+ ad9361_spi_write(phy->spi, REG_RX2_TUNE_CTRL, RX2_TUNE_RESAMPLE);
+
+ /* Start the RX Baseband Filter calibration in register 0x016[7] */
+ /* Calibration is complete when register 0x016[7] self clears */
+ ret = ad9361_run_calibration(phy, RX_BB_TUNE_CAL);
+
+ /* Disable the RX baseband filter tune circuit, write 0x1E2=3, 0x1E3=3 */
+ ad9361_spi_write(phy->spi, REG_RX1_TUNE_CTRL,
+ RX1_TUNE_RESAMPLE | RX1_PD_TUNE);
+ ad9361_spi_write(phy->spi, REG_RX2_TUNE_CTRL,
+ RX2_TUNE_RESAMPLE | RX2_PD_TUNE);
+
+ return ret;
+}
+
+/**
+ * Perform a baseband TX analog filter calibration.
+ * @param phy The AD9361 state structure.
+ * @param tx_bb_bw The baseband bandwidth [Hz].
+ * @param bbpll_freq The BBPLL frequency [Hz].
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_tx_bb_analog_filter_calib(struct ad9361_rf_phy *phy,
+ uint32_t tx_bb_bw,
+ uint32_t bbpll_freq)
+{
+ uint32_t target, txbbf_div;
+ int32_t ret;
+
+ dev_dbg(&phy->spi->dev, "%s : tx_bb_bw %"PRIu32" bbpll_freq %"PRIu32,
+ __func__, tx_bb_bw, bbpll_freq);
+
+ tx_bb_bw = clamp(tx_bb_bw, 625000UL, 20000000UL);
+
+ /* 1.6 * BBBW * 2PI / ln(2) */
+ target = 145036 * (tx_bb_bw / 10000UL);
+ txbbf_div = min_t(uint32_t, 511UL, DIV_ROUND_UP(bbpll_freq, target));
+
+ /* Set TX baseband filter divide value */
+ ad9361_spi_write(phy->spi, REG_TX_BBF_TUNE_DIVIDER, txbbf_div);
+ ad9361_spi_writef(phy->spi, REG_TX_BBF_TUNE_MODE,
+ TX_BBF_TUNE_DIVIDER, txbbf_div >> 8);
+
+ /* Enable the TX baseband filter tune circuit by setting 0x0CA=0x22. */
+ ad9361_spi_write(phy->spi, REG_TX_TUNE_CTRL, TUNER_RESAMPLE | TUNE_CTRL(1));
+
+ /* Start the TX Baseband Filter calibration in register 0x016[6] */
+ /* Calibration is complete when register 0x016[] self clears */
+ ret = ad9361_run_calibration(phy, TX_BB_TUNE_CAL);
+
+ /* Disable the TX baseband filter tune circuit by writing 0x0CA=0x26. */
+ ad9361_spi_write(phy->spi, REG_TX_TUNE_CTRL,
+ TUNER_RESAMPLE | TUNE_CTRL(1) | PD_TUNE);
+
+ return ret;
+}
+
+/**
+ * Perform a baseband TX secondary filter calibration.
+ * @param phy The AD9361 state structure.
+ * @param tx_rf_bw The RF bandwidth [Hz].
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_tx_bb_second_filter_calib(struct ad9361_rf_phy *phy,
+ uint32_t tx_bb_bw)
+{
+ uint64_t cap;
+ uint32_t corner, res, div;
+ uint32_t reg_conf, reg_res;
+ int32_t ret, i;
+
+ dev_dbg(&phy->spi->dev, "%s : tx_bb_bw %"PRIu32,
+ __func__, tx_bb_bw);
+
+ tx_bb_bw = clamp(tx_bb_bw, 530000UL, 20000000UL);
+
+ /* BBBW * 5PI */
+ corner = 15708 * (tx_bb_bw / 10000UL);
+
+ for (i = 0, res = 1; i < 4; i++) {
+ div = corner * res;
+ cap = (500000000ULL) + (div >> 1);
+ do_div(&cap, div);
+ cap -= 12ULL;
+ if (cap < 64ULL)
+ break;
+
+ res <<= 1;
+ }
+
+ if (cap > 63ULL)
+ cap = 63ULL;
+
+ if (tx_bb_bw <= 4500000UL)
+ reg_conf = 0x59;
+ else if (tx_bb_bw <= 12000000UL)
+ reg_conf = 0x56;
+ else
+ reg_conf = 0x57;
+
+ switch (res) {
+ case 1:
+ reg_res = 0x0C;
+ break;
+ case 2:
+ reg_res = 0x04;
+ break;
+ case 4:
+ reg_res = 0x03;
+ break;
+ case 8:
+ reg_res = 0x01;
+ break;
+ default:
+ reg_res = 0x01;
+ }
+
+ ret = ad9361_spi_write(phy->spi, REG_CONFIG0, reg_conf);
+ ret |= ad9361_spi_write(phy->spi, REG_RESISTOR, reg_res);
+ ret |= ad9361_spi_write(phy->spi, REG_CAPACITOR, (uint8_t)cap);
+
+ return ret;
+}
+
+/**
+ * Perform a RF synthesizer charge pump calibration.
+ * @param phy The AD9361 state structure.
+ * @param ref_clk_hz The reference clock rate [Hz].
+ * @param tx The Synthesizer TX = 1, RX = 0.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_txrx_synth_cp_calib(struct ad9361_rf_phy *phy,
+ uint32_t ref_clk_hz, bool tx)
+{
+ uint32_t offs = tx ? 0x40 : 0;
+ uint32_t vco_cal_cnt;
+ dev_dbg(&phy->spi->dev, "%s : ref_clk_hz %"PRIu32" : is_tx %d",
+ __func__, ref_clk_hz, tx);
+
+ /* REVIST: */
+ ad9361_spi_write(phy->spi, REG_RX_CP_LEVEL_DETECT + offs, 0x17);
+
+ ad9361_spi_write(phy->spi, REG_RX_DSM_SETUP_1 + offs, 0x0);
+
+ ad9361_spi_write(phy->spi, REG_RX_LO_GEN_POWER_MODE + offs, 0x00);
+ ad9361_spi_write(phy->spi, REG_RX_VCO_LDO + offs, 0x0B);
+ ad9361_spi_write(phy->spi, REG_RX_VCO_PD_OVERRIDES + offs, 0x02);
+ ad9361_spi_write(phy->spi, REG_RX_CP_CURRENT + offs, 0x80);
+ ad9361_spi_write(phy->spi, REG_RX_CP_CONFIG + offs, 0x00);
+
+ /* see Table 70 Example Calibration Times for RF VCO Cal */
+ if (phy->pdata->fdd) {
+ vco_cal_cnt = VCO_CAL_EN | VCO_CAL_COUNT(3) | FB_CLOCK_ADV(2);
+ }
+ else {
+ if (ref_clk_hz > 40000000UL)
+ vco_cal_cnt = VCO_CAL_EN | VCO_CAL_COUNT(1) |
+ FB_CLOCK_ADV(2);
+ else
+ vco_cal_cnt = VCO_CAL_EN | VCO_CAL_COUNT(0) |
+ FB_CLOCK_ADV(2);
+ }
+
+ ad9361_spi_write(phy->spi, REG_RX_VCO_CAL + offs, vco_cal_cnt);
+
+ /* Enable FDD mode during calibrations */
+
+ if (!phy->pdata->fdd) {
+ ad9361_spi_writef(phy->spi, REG_PARALLEL_PORT_CONF_3,
+ HALF_DUPLEX_MODE, 0);
+ }
+
+ ad9361_spi_write(phy->spi, REG_ENSM_CONFIG_2, DUAL_SYNTH_MODE);
+ ad9361_spi_write(phy->spi, REG_ENSM_CONFIG_1,
+ FORCE_ALERT_STATE |
+ TO_ALERT);
+ ad9361_spi_write(phy->spi, REG_ENSM_MODE, FDD_MODE);
+
+ ad9361_spi_write(phy->spi, REG_RX_CP_CONFIG + offs, CP_CAL_ENABLE);
+
+ return ad9361_check_cal_done(phy, REG_RX_CAL_STATUS + offs,
+ CP_CAL_VALID, 1);
+}
+
+/**
+ * Perform a baseband DC offset calibration.
+ * @param phy The AD9361 state structure.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_bb_dc_offset_calib(struct ad9361_rf_phy *phy)
+{
+ dev_dbg(&phy->spi->dev, "%s", __func__);
+
+ ad9361_spi_write(phy->spi, REG_BB_DC_OFFSET_COUNT, 0x3F);
+ ad9361_spi_write(phy->spi, REG_BB_DC_OFFSET_SHIFT, BB_DC_M_SHIFT(0xF));
+ ad9361_spi_write(phy->spi, REG_BB_DC_OFFSET_ATTEN, BB_DC_OFFSET_ATTEN(1));
+
+ return ad9361_run_calibration(phy, BBDC_CAL);
+}
+
+/**
+ * Perform a RF DC offset calibration.
+ * @param phy The AD9361 state structure.
+ * @param ref_clk_hz The RX LO frequency [Hz].
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_rf_dc_offset_calib(struct ad9361_rf_phy *phy,
+ uint64_t rx_freq)
+{
+ struct spi_device *spi = phy->spi;
+
+ dev_dbg(&phy->spi->dev, "%s : rx_freq %"PRIu64,
+ __func__, rx_freq);
+
+ ad9361_spi_write(spi, REG_WAIT_COUNT, 0x20);
+
+ if (rx_freq <= 4000000000ULL) {
+ ad9361_spi_write(spi, REG_RF_DC_OFFSET_COUNT,
+ phy->pdata->rf_dc_offset_count_low);
+ ad9361_spi_write(spi, REG_RF_DC_OFFSET_CONFIG_1,
+ RF_DC_CALIBRATION_COUNT(4) | DAC_FS(2));
+ ad9361_spi_write(spi, REG_RF_DC_OFFSET_ATTEN,
+ RF_DC_OFFSET_ATTEN(
+ phy->pdata->dc_offset_attenuation_low));
+ }
+ else {
+ ad9361_spi_write(spi, REG_RF_DC_OFFSET_COUNT,
+ phy->pdata->rf_dc_offset_count_high);
+ ad9361_spi_write(spi, REG_RF_DC_OFFSET_CONFIG_1,
+ RF_DC_CALIBRATION_COUNT(4) | DAC_FS(3));
+ ad9361_spi_write(spi, REG_RF_DC_OFFSET_ATTEN,
+ RF_DC_OFFSET_ATTEN(
+ phy->pdata->dc_offset_attenuation_high));
+ }
+
+ ad9361_spi_write(spi, REG_DC_OFFSET_CONFIG2,
+ USE_WAIT_COUNTER_FOR_RF_DC_INIT_CAL |
+ DC_OFFSET_UPDATE(3));
+
+ if (phy->pdata->rx1rx2_phase_inversion_en ||
+ (phy->pdata->port_ctrl.pp_conf[1] & INVERT_RX2)) {
+ ad9361_spi_write(spi, REG_INVERT_BITS,
+ INVERT_RX1_RF_DC_CGOUT_WORD);
+ } else {
+ ad9361_spi_write(spi, REG_INVERT_BITS,
+ INVERT_RX1_RF_DC_CGOUT_WORD |
+ INVERT_RX2_RF_DC_CGOUT_WORD);
+ }
+
+ return ad9361_run_calibration(phy, RFDC_CAL);
+}
+
+/**
+ * Update RF bandwidth.
+ * @param phy The AD9361 state structure.
+ * @param rf_rx_bw RF RX bandwidth [Hz].
+ * @param rf_tx_bw RF TX bandwidth [Hz].
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t __ad9361_update_rf_bandwidth(struct ad9361_rf_phy *phy,
+ uint32_t rf_rx_bw, uint32_t rf_tx_bw)
+{
+ uint32_t real_rx_bandwidth = rf_rx_bw / 2;
+ uint32_t real_tx_bandwidth = rf_tx_bw / 2;
+ uint32_t bbpll_freq;
+ int32_t ret;
+
+ dev_dbg(&phy->spi->dev, "%s: %"PRIu32" %"PRIu32,
+ __func__, rf_rx_bw, rf_tx_bw);
+
+ bbpll_freq = clk_get_rate(phy, phy->ref_clk_scale[BBPLL_CLK]);
+
+ ret = ad9361_rx_bb_analog_filter_calib(phy,
+ real_rx_bandwidth,
+ bbpll_freq);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_tx_bb_analog_filter_calib(phy,
+ real_tx_bandwidth,
+ bbpll_freq);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_rx_tia_calib(phy, real_rx_bandwidth);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_tx_bb_second_filter_calib(phy, real_tx_bandwidth);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_rx_adc_setup(phy,
+ bbpll_freq,
+ clk_get_rate(phy, phy->ref_clk_scale[ADC_CLK]));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/**
+ * TX Quad Calib.
+ * @param phy The AD9361 state structure.
+ * @param phase phase
+ * @param rxnco_word Rx NCO word.
+ * @param decim decim
+ * @param res res
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t __ad9361_tx_quad_calib(struct ad9361_rf_phy *phy, uint32_t phase,
+ uint32_t rxnco_word, uint32_t decim, uint8_t *res)
+{
+ int32_t ret;
+
+ ad9361_spi_write(phy->spi, REG_QUAD_CAL_NCO_FREQ_PHASE_OFFSET,
+ RX_NCO_FREQ(rxnco_word) | RX_NCO_PHASE_OFFSET(phase));
+ ad9361_spi_write(phy->spi, REG_QUAD_CAL_CTRL,
+ SETTLE_MAIN_ENABLE | DC_OFFSET_ENABLE | QUAD_CAL_SOFT_RESET |
+ GAIN_ENABLE | PHASE_ENABLE | M_DECIM(decim));
+ ad9361_spi_write(phy->spi, REG_QUAD_CAL_CTRL,
+ SETTLE_MAIN_ENABLE | DC_OFFSET_ENABLE |
+ GAIN_ENABLE | PHASE_ENABLE | M_DECIM(decim));
+
+ ret = ad9361_run_calibration(phy, TX_QUAD_CAL);
+ if (ret < 0)
+ return ret;
+
+ if (res)
+ *res = ad9361_spi_read(phy->spi,
+ (phy->pdata->rx1tx1_mode_use_tx_num == 2) ?
+ REG_QUAD_CAL_STATUS_TX2 : REG_QUAD_CAL_STATUS_TX1) &
+ (TX1_LO_CONV | TX1_SSB_CONV);
+
+ return 0;
+}
+
+/**
+ * Loop through all possible phase offsets in case the QUAD CAL doesn't converge.
+ * @param phy The AD9361 state structure.
+ * @param rxnco_word Rx NCO word.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_tx_quad_phase_search(struct ad9361_rf_phy *phy, uint32_t rxnco_word, uint8_t decim)
+{
+ int32_t i, ret;
+ uint8_t field[64], val;
+ uint32_t start;
+
+ dev_dbg(&phy->spi->dev, "%s", __func__);
+
+ for (i = 0; i < (int64_t)(ARRAY_SIZE(field) / 2); i++) {
+ ret = __ad9361_tx_quad_calib(phy, i, rxnco_word, decim, &val);
+ if (ret < 0)
+ return ret;
+
+ /* Handle 360/0 wrap around */
+ field[i] = field[i + 32] = !((val & TX1_LO_CONV) && (val & TX1_SSB_CONV));
+ }
+
+ ret = ad9361_find_opt(field, ARRAY_SIZE(field), &start);
+
+ phy->last_tx_quad_cal_phase = (start + ret / 2) & 0x1F;
+
+#ifdef _DEBUG
+ for (i = 0; i < 64; i++) {
+ printk("%c", (field[i] ? '#' : 'o'));
+ }
+#ifdef WIN32
+ printk(" RX_NCO_PHASE_OFFSET(%d, 0x%X) \n", phy->last_tx_quad_cal_phase,
+ phy->last_tx_quad_cal_phase);
+#else
+ printk(" RX_NCO_PHASE_OFFSET(%"PRIu32", 0x%"PRIX32") \n", phy->last_tx_quad_cal_phase,
+ phy->last_tx_quad_cal_phase);
+#endif
+#endif
+
+ ret = __ad9361_tx_quad_calib(phy, phy->last_tx_quad_cal_phase, rxnco_word, decim, NULL);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/**
+ * Perform a TX quadrature calibration.
+ * @param phy The AD9361 state structure.
+ * @param bw The bandwidth [Hz].
+ * @param rx_phase The optional RX phase value overwrite (set to zero).
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_tx_quad_calib(struct ad9361_rf_phy *phy,
+ uint32_t bw_rx, uint32_t bw_tx,
+ int32_t rx_phase)
+{
+ struct spi_device *spi = phy->spi;
+ uint32_t clktf, clkrf;
+ int32_t txnco_word, rxnco_word, txnco_freq, ret;
+ uint8_t __rx_phase = 0, reg_inv_bits = 0, val, decim;
+ const uint8_t(*tab)[3];
+ uint32_t index_max, i, lpf_tia_mask;
+
+ /*
+ * Find NCO frequency that matches this equation:
+ * BW / 4 = Rx NCO freq = Tx NCO freq:
+ * Rx NCO = ClkRF * (rxNCO <1:0> + 1) / 32
+ * Tx NCO = ClkTF * (txNCO <1:0> + 1) / 32
+ */
+
+ clkrf = clk_get_rate(phy, phy->ref_clk_scale[CLKRF_CLK]);
+ clktf = clk_get_rate(phy, phy->ref_clk_scale[CLKTF_CLK]);
+
+ dev_dbg(&phy->spi->dev, "%s : bw_tx %"PRIu32" clkrf %"PRIu32" clktf %"PRIu32,
+ __func__, bw_tx, clkrf, clktf);
+
+ txnco_word = DIV_ROUND_CLOSEST(bw_tx * 8, clktf) - 1;
+ txnco_word = clamp_t(int, txnco_word, 0, 3);
+ rxnco_word = txnco_word;
+
+ dev_dbg(dev, "Tx NCO frequency: %"PRIu32" (BW/4: %"PRIu32") txnco_word %"PRId32,
+ clktf * (txnco_word + 1) / 32, bw_tx / 4, txnco_word);
+
+ if (clktf <= 4000000UL)
+ decim = 2;
+ else
+ decim = 3;
+
+ if (clkrf == (2 * clktf)) {
+ __rx_phase = 0x0E;
+ switch (txnco_word) {
+ case 0:
+ txnco_word++;
+ break;
+ case 1:
+ rxnco_word--;
+ break;
+ case 2:
+ rxnco_word -= 2;
+ txnco_word--;
+ break;
+ case 3:
+ rxnco_word -= 2; /* REVISIT */
+ __rx_phase = 0x08;
+ break;
+ }
+ }
+ else if (clkrf == clktf) {
+ switch (txnco_word) {
+ case 0:
+ case 3:
+ __rx_phase = 0x15;
+ break;
+ case 2:
+ __rx_phase = 0x1F;
+ break;
+ case 1:
+ if (ad9361_spi_readf(spi,
+ REG_TX_ENABLE_FILTER_CTRL, 0x3F) == 0x22)
+ __rx_phase = 0x15; /* REVISIT */
+ else
+ __rx_phase = 0x1A;
+ break;
+ }
+ }
+ else
+ dev_err(dev, "Unhandled case in %s line %d clkrf %"PRIu32" clktf %"PRIu32,
+ __func__, __LINE__, clkrf, clktf);
+
+
+ if (rx_phase >= 0)
+ __rx_phase = rx_phase;
+
+ txnco_freq = clktf * (txnco_word + 1) / 32;
+
+ if (txnco_freq > (int64_t)(bw_rx / 4) || txnco_freq > (int64_t)(bw_tx / 4)) {
+ /* Make sure the BW during calibration is wide enough */
+ ret = __ad9361_update_rf_bandwidth(phy, txnco_freq * 8, txnco_freq * 8);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (phy->pdata->rx1rx2_phase_inversion_en ||
+ (phy->pdata->port_ctrl.pp_conf[1] & INVERT_RX2)) {
+
+ ad9361_spi_writef(spi, REG_PARALLEL_PORT_CONF_2, INVERT_RX2, 0);
+
+ reg_inv_bits = ad9361_spi_read(spi, REG_INVERT_BITS);
+
+ ad9361_spi_write(spi, REG_INVERT_BITS,
+ INVERT_RX1_RF_DC_CGOUT_WORD |
+ INVERT_RX2_RF_DC_CGOUT_WORD);
+ }
+
+ ad9361_spi_writef(spi, REG_KEXP_2, TX_NCO_FREQ(~0), txnco_word);
+ ad9361_spi_write(spi, REG_QUAD_CAL_COUNT, 0xFF);
+ ad9361_spi_write(spi, REG_KEXP_1, KEXP_TX(1) | KEXP_TX_COMP(3) |
+ KEXP_DC_I(3) | KEXP_DC_Q(3));
+ ad9361_spi_write(spi, REG_MAG_FTEST_THRESH, 0x01);
+ ad9361_spi_write(spi, REG_MAG_FTEST_THRESH_2, 0x01);
+
+ if (has_split_gt && phy->pdata->split_gt) {
+ tab = &split_gain_table[phy->current_table][0];
+ index_max = SIZE_SPLIT_TABLE;
+ lpf_tia_mask = 0x20;
+ }
+ else {
+ tab = &full_gain_table[phy->current_table][0];
+ index_max = SIZE_FULL_TABLE;
+ lpf_tia_mask = 0x3F;
+ }
+
+ for (i = 0; i < index_max; i++)
+ if ((tab[i][1] & lpf_tia_mask) == 0x20) {
+ ad9361_spi_write(spi, REG_TX_QUAD_FULL_LMT_GAIN, i);
+ break;
+ }
+
+ if (i >= index_max)
+ dev_err(dev, "failed to find suitable LPF TIA value in gain table");
+
+ ad9361_spi_write(spi, REG_QUAD_SETTLE_COUNT, 0xF0);
+ ad9361_spi_write(spi, REG_TX_QUAD_LPF_GAIN, 0x00);
+
+ ret = __ad9361_tx_quad_calib(phy, __rx_phase, rxnco_word, decim, &val);
+
+ dev_dbg(dev, "LO leakage: %d Quadrature Calibration: %d : rx_phase %d",
+ !!(val & TX1_LO_CONV), !!(val & TX1_SSB_CONV), __rx_phase);
+
+ /* Calibration failed -> try last phase offset */
+ if (val != (TX1_LO_CONV | TX1_SSB_CONV)) {
+ if (phy->last_tx_quad_cal_phase < 31)
+ ret = __ad9361_tx_quad_calib(phy, phy->last_tx_quad_cal_phase,
+ rxnco_word, decim, &val);
+ } else {
+ phy->last_tx_quad_cal_phase = __rx_phase;
+ }
+
+ /* Calibration failed -> loop through all 32 phase offsets */
+ if (val != (TX1_LO_CONV | TX1_SSB_CONV))
+ ret = ad9361_tx_quad_phase_search(phy, rxnco_word, decim);
+
+ if (phy->pdata->rx1rx2_phase_inversion_en ||
+ (phy->pdata->port_ctrl.pp_conf[1] & INVERT_RX2)) {
+ ad9361_spi_writef(spi, REG_PARALLEL_PORT_CONF_2, INVERT_RX2, 1);
+ ad9361_spi_write(spi, REG_INVERT_BITS, reg_inv_bits);
+ }
+
+ if (phy->pdata->rx1rx2_phase_inversion_en ||
+ (phy->pdata->port_ctrl.pp_conf[1] & INVERT_RX2)) {
+ ad9361_spi_writef(spi, REG_PARALLEL_PORT_CONF_2, INVERT_RX2, 1);
+ ad9361_spi_write(spi, REG_INVERT_BITS, reg_inv_bits);
+ }
+
+ if (txnco_freq > (int64_t)(bw_rx / 4) || txnco_freq > (int64_t)(bw_tx / 4)) {
+ __ad9361_update_rf_bandwidth(phy,
+ phy->current_rx_bw_Hz,
+ phy->current_tx_bw_Hz);
+ }
+
+ return ret;
+}
+
+/**
+ * Setup RX tracking calibrations.
+ * @param phy The AD9361 state structure.
+ * @param bbdc_track Set true, will enable the BBDC tracking.
+ * @param rfdc_track Set true, will enable the RFDC tracking.
+ * @param rxquad_track Set true, will enable the RXQUAD tracking.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_tracking_control(struct ad9361_rf_phy *phy, bool bbdc_track,
+ bool rfdc_track, bool rxquad_track)
+{
+ struct spi_device *spi = phy->spi;
+ uint32_t qtrack = 0;
+
+ dev_dbg(&spi->dev, "%s : bbdc_track=%d, rfdc_track=%d, rxquad_track=%d",
+ __func__, bbdc_track, rfdc_track, rxquad_track);
+
+ ad9361_spi_write(spi, REG_CALIBRATION_CONFIG_2,
+ CALIBRATION_CONFIG2_DFLT | K_EXP_PHASE(0x15));
+ ad9361_spi_write(spi, REG_CALIBRATION_CONFIG_3,
+ PREVENT_POS_LOOP_GAIN | K_EXP_AMPLITUDE(0x15));
+
+ ad9361_spi_write(spi, REG_DC_OFFSET_CONFIG2,
+ USE_WAIT_COUNTER_FOR_RF_DC_INIT_CAL |
+ DC_OFFSET_UPDATE(phy->pdata->dc_offset_update_events) |
+ (bbdc_track ? ENABLE_BB_DC_OFFSET_TRACKING : 0) |
+ (rfdc_track ? ENABLE_RF_OFFSET_TRACKING : 0));
+
+ ad9361_spi_writef(spi, REG_RX_QUAD_GAIN2,
+ CORRECTION_WORD_DECIMATION_M(~0),
+ phy->pdata->qec_tracking_slow_mode_en ? 4 : 0);
+
+ if (rxquad_track) {
+ if (phy->pdata->rx2tx2)
+ qtrack = ENABLE_TRACKING_MODE_CH1 | ENABLE_TRACKING_MODE_CH2;
+ else
+ qtrack = (phy->pdata->rx1tx1_mode_use_rx_num == 1) ?
+ ENABLE_TRACKING_MODE_CH1 : ENABLE_TRACKING_MODE_CH2;
+ }
+
+ ad9361_spi_write(spi, REG_CALIBRATION_CONFIG_1,
+ ENABLE_PHASE_CORR | ENABLE_GAIN_CORR |
+ FREE_RUN_MODE | ENABLE_CORR_WORD_DECIMATION |
+ qtrack);
+
+ return 0;
+}
+
+/**
+ * Enable/disable the VCO cal.
+ * @param phy The AD9361 state structure.
+ * @param rx Set true for rx.
+ * @param enable Set true to enable.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_trx_vco_cal_control(struct ad9361_rf_phy *phy,
+ bool tx, bool enable)
+{
+ dev_dbg(&phy->spi->dev, "%s : state %d",
+ __func__, enable);
+
+ return ad9361_spi_writef(phy->spi,
+ tx ? REG_TX_PFD_CONFIG : REG_RX_PFD_CONFIG,
+ BYPASS_LD_SYNTH, !enable);
+}
+
+/**
+ * Enable/disable the ext. LO.
+ * @param phy The AD9361 state structure.
+ * @param rx Set true for rx.
+ * @param enable Set true to enable.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_trx_ext_lo_control(struct ad9361_rf_phy *phy,
+ bool tx, bool enable)
+{
+ int32_t val = enable ? ~0 : 0;
+ int32_t ret;
+
+ /* REVIST:
+ * POWER_DOWN_TRX_SYNTH and MCS_RF_ENABLE somehow conflict
+ */
+
+ bool mcs_rf_enable = ad9361_spi_readf(phy->spi,
+ REG_MULTICHIP_SYNC_AND_TX_MON_CTRL, MCS_RF_ENABLE);
+
+ dev_dbg(&phy->spi->dev, "%s : %s state %d",
+ __func__, tx ? "TX" : "RX", enable);
+
+ if (tx) {
+ ret = ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2,
+ POWER_DOWN_TX_SYNTH, mcs_rf_enable ? 0 : enable);
+
+ ret |= ad9361_spi_writef(phy->spi, REG_RFPLL_DIVIDERS,
+ TX_VCO_DIVIDER(~0), enable ? 7 :
+ phy->cached_tx_rfpll_div);
+
+ ret |= ad9361_spi_write(phy->spi, REG_TX_SYNTH_POWER_DOWN_OVERRIDE,
+ enable ? TX_SYNTH_VCO_ALC_POWER_DOWN |
+ TX_SYNTH_PTAT_POWER_DOWN |
+ TX_SYNTH_VCO_POWER_DOWN : 0);
+
+ ret |= ad9361_spi_writef(phy->spi, REG_ANALOG_POWER_DOWN_OVERRIDE,
+ TX_EXT_VCO_BUFFER_POWER_DOWN, !enable);
+
+ ret |= ad9361_spi_write(phy->spi, REG_TX_LO_GEN_POWER_MODE,
+ TX_LO_GEN_POWER_MODE(val));
+ }
+ else {
+ ret = ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2,
+ POWER_DOWN_RX_SYNTH, mcs_rf_enable ? 0 : enable);
+
+ ret |= ad9361_spi_writef(phy->spi, REG_RFPLL_DIVIDERS,
+ RX_VCO_DIVIDER(~0), enable ? 7 :
+ phy->cached_rx_rfpll_div);
+
+ ret |= ad9361_spi_write(phy->spi, REG_RX_SYNTH_POWER_DOWN_OVERRIDE,
+ enable ? RX_SYNTH_VCO_ALC_POWER_DOWN |
+ RX_SYNTH_PTAT_POWER_DOWN |
+ RX_SYNTH_VCO_POWER_DOWN : 0);
+
+ ret |= ad9361_spi_writef(phy->spi, REG_ANALOG_POWER_DOWN_OVERRIDE,
+ RX_EXT_VCO_BUFFER_POWER_DOWN, !enable);
+
+ ret |= ad9361_spi_write(phy->spi, REG_RX_LO_GEN_POWER_MODE,
+ RX_LO_GEN_POWER_MODE(val));
+ }
+
+ return ret;
+}
+
+/**
+ * Setup the reference clock delay unit counter register.
+ * @param phy The AD9361 state structure.
+ * @param ref_clk_hz The reference clock frequency [Hz].
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_set_ref_clk_cycles(struct ad9361_rf_phy *phy,
+ uint32_t ref_clk_hz)
+{
+ dev_dbg(&phy->spi->dev, "%s : ref_clk_hz %"PRIu32,
+ __func__, ref_clk_hz);
+
+ return ad9361_spi_write(phy->spi, REG_REFERENCE_CLOCK_CYCLES,
+ REFERENCE_CLOCK_CYCLES_PER_US((ref_clk_hz / 1000000UL) - 1));
+}
+
+/**
+ * Setup the DCXO tune.
+ * @param phy The AD9361 state structure.
+ * @param coarse The DCXO tune coarse.
+ * @param fine The DCXO tune fine.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_set_dcxo_tune(struct ad9361_rf_phy *phy,
+ uint32_t coarse, uint32_t fine)
+{
+ dev_dbg(&phy->spi->dev, "%s : coarse %"PRIu32" fine %"PRIu32,
+ __func__, coarse, fine);
+
+ ad9361_spi_write(phy->spi, REG_DCXO_COARSE_TUNE,
+ DCXO_TUNE_COARSE(coarse));
+ ad9361_spi_write(phy->spi, REG_DCXO_FINE_TUNE_LOW,
+ DCXO_TUNE_FINE_LOW(fine));
+ return ad9361_spi_write(phy->spi, REG_DCXO_FINE_TUNE_HIGH,
+ DCXO_TUNE_FINE_HIGH(fine));
+}
+
+/**
+ * Setup TXMON.
+ * @param phy The AD9361 state structure.
+ * @param ctrl The TXMON settings.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_txmon_setup(struct ad9361_rf_phy *phy,
+ struct tx_monitor_control *ctrl)
+{
+ struct spi_device *spi = phy->spi;
+
+ dev_dbg(&phy->spi->dev, "%s", __func__);
+
+ ad9361_spi_write(spi, REG_TPM_MODE_ENABLE,
+ (ctrl->one_shot_mode_en ? ONE_SHOT_MODE : 0) |
+ TX_MON_DURATION(ilog2(ctrl->tx_mon_duration / 16)));
+
+ ad9361_spi_write(spi, REG_TX_MON_DELAY, ctrl->tx_mon_delay);
+
+ ad9361_spi_write(spi, REG_TX_MON_1_CONFIG,
+ TX_MON_1_LO_CM(ctrl->tx1_mon_lo_cm) |
+ TX_MON_1_GAIN(ctrl->tx1_mon_front_end_gain));
+ ad9361_spi_write(spi, REG_TX_MON_2_CONFIG,
+ TX_MON_2_LO_CM(ctrl->tx2_mon_lo_cm) |
+ TX_MON_2_GAIN(ctrl->tx2_mon_front_end_gain));
+
+ ad9361_spi_write(spi, REG_TX_ATTEN_THRESH,
+ ctrl->low_high_gain_threshold_mdB / 250);
+
+ ad9361_spi_write(spi, REG_TX_MON_HIGH_GAIN,
+ TX_MON_HIGH_GAIN(ctrl->high_gain_dB));
+
+ ad9361_spi_write(spi, REG_TX_MON_LOW_GAIN,
+ (ctrl->tx_mon_track_en ? TX_MON_TRACK : 0) |
+ TX_MON_LOW_GAIN(ctrl->low_gain_dB));
+
+ return 0;
+}
+
+/**
+ * Enable TXMON.
+ * @param phy The AD9361 state structure.
+ * @param en_mask The enable mask.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_txmon_control(struct ad9361_rf_phy *phy,
+ int32_t en_mask)
+{
+ dev_dbg(&phy->spi->dev, "%s: mask 0x%"PRIx32, __func__, en_mask);
+
+#if 0
+ if (!phy->pdata->fdd && en_mask) {
+ ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_1,
+ ENABLE_RX_DATA_PORT_FOR_CAL, 1);
+ phy->txmon_tdd_en = true;
+ } else {
+ ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_1,
+ ENABLE_RX_DATA_PORT_FOR_CAL, 0);
+ phy->txmon_tdd_en = false;
+ }
+#endif
+
+ ad9361_spi_writef(phy->spi, REG_ANALOG_POWER_DOWN_OVERRIDE,
+ TX_MONITOR_POWER_DOWN(~0), ~en_mask);
+
+ ad9361_spi_writef(phy->spi, REG_TPM_MODE_ENABLE,
+ TX1_MON_ENABLE, !!(en_mask & TX_1));
+
+ return ad9361_spi_writef(phy->spi, REG_TPM_MODE_ENABLE,
+ TX2_MON_ENABLE, !!(en_mask & TX_2));
+}
+
+/**
+* Setup the RF port.
+* Note:
+* val
+* 0 (RX1A_N & RX1A_P) and (RX2A_N & RX2A_P) enabled; balanced
+* 1 (RX1B_N & RX1B_P) and (RX2B_N & RX2B_P) enabled; balanced
+* 2 (RX1C_N & RX1C_P) and (RX2C_N & RX2C_P) enabled; balanced
+*
+* 3 RX1A_N and RX2A_N enabled; unbalanced
+* 4 RX1A_P and RX2A_P enabled; unbalanced
+* 5 RX1B_N and RX2B_N enabled; unbalanced
+* 6 RX1B_P and RX2B_P enabled; unbalanced
+* 7 RX1C_N and RX2C_N enabled; unbalanced
+* 8 RX1C_P and RX2C_P enabled; unbalanced
+* 9 TX_MON1
+* 10 TX_MON2
+* 11 TX_MON1 & TX_MON2
+* @param phy The AD9361 state structure.
+* @param rx_inputs RX input option identifier
+* @param txb TX output option identifier
+* @return 0 in case of success, negative error code otherwise.
+*/
+int32_t ad9361_rf_port_setup(struct ad9361_rf_phy *phy, bool is_out,
+ uint32_t rx_inputs, uint32_t txb)
+{
+ uint32_t val;
+
+ if (rx_inputs > 11)
+ return -EINVAL;
+
+ if (!is_out) {
+ if (rx_inputs > 8)
+ return ad9361_txmon_control(phy, rx_inputs & (TX_1 | TX_2));
+ else
+ ad9361_txmon_control(phy, 0);
+ }
+
+ if (rx_inputs < 3)
+ val = 3 << (rx_inputs * 2);
+ else
+ val = 1 << (rx_inputs - 3);
+
+ if (txb)
+ val |= TX_OUTPUT; /* Select TX1B, TX2B */
+
+ dev_dbg(&phy->spi->dev, "%s : INPUT_SELECT 0x%"PRIx32,
+ __func__, val);
+
+ return ad9361_spi_write(phy->spi, REG_INPUT_SELECT, val);
+}
+
+/**
+ * Setup the Parallel Port (Digital Data Interface).
+ * @param phy The AD9361 state structure.
+ * @param restore_c3 Set true, will restore the Parallel Port Configuration 3
+ * register.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_pp_port_setup(struct ad9361_rf_phy *phy, bool restore_c3)
+{
+ struct spi_device *spi = phy->spi;
+ struct ad9361_phy_platform_data *pd = phy->pdata;
+
+ dev_dbg(&phy->spi->dev, "%s", __func__);
+
+ if (restore_c3) {
+ return ad9361_spi_write(spi, REG_PARALLEL_PORT_CONF_3,
+ pd->port_ctrl.pp_conf[2]);
+ }
+
+ /* Sanity check */
+ if (pd->port_ctrl.pp_conf[2] & LVDS_MODE)
+ pd->port_ctrl.pp_conf[2] &=
+ ~(HALF_DUPLEX_MODE | SINGLE_DATA_RATE | SINGLE_PORT_MODE);
+
+ if (pd->port_ctrl.pp_conf[2] & FULL_PORT)
+ pd->port_ctrl.pp_conf[2] &= ~(HALF_DUPLEX_MODE | SINGLE_PORT_MODE);
+
+ ad9361_spi_write(spi, REG_PARALLEL_PORT_CONF_1, pd->port_ctrl.pp_conf[0]);
+ ad9361_spi_write(spi, REG_PARALLEL_PORT_CONF_2, pd->port_ctrl.pp_conf[1]);
+ ad9361_spi_write(spi, REG_PARALLEL_PORT_CONF_3, pd->port_ctrl.pp_conf[2]);
+ ad9361_spi_write(spi, REG_RX_CLOCK_DATA_DELAY, pd->port_ctrl.rx_clk_data_delay);
+ ad9361_spi_write(spi, REG_TX_CLOCK_DATA_DELAY, pd->port_ctrl.tx_clk_data_delay);
+
+#ifdef NUAND_MODIFICATIONS
+ // set up digital interface slew and drive strength
+ ad9361_spi_write(spi, REG_LVDS_BIAS_CTRL, pd->port_ctrl.lvds_bias_ctrl | CLK_OUT_SLEW(pd->port_ctrl.clk_out_slew));
+ ad9361_spi_write(spi, REG_DIGITAL_IO_CTRL, DATACLK_SLEW(pd->port_ctrl.dataclk_slew) | DATA_PORT_SLEW(pd->port_ctrl.data_port_slew));
+ ad9361_spi_writef(spi, REG_DIGITAL_IO_CTRL, DATACLK_DRIVE, pd->port_ctrl.dataclk_drive);
+ ad9361_spi_writef(spi, REG_DIGITAL_IO_CTRL, DATA_PORT_DRIVE, pd->port_ctrl.data_port_drive);
+ ad9361_spi_writef(spi, REG_DIGITAL_IO_CTRL, CLK_OUT_DRIVE, pd->port_ctrl.clk_out_drive);
+#else
+ ad9361_spi_write(spi, REG_LVDS_BIAS_CTRL, pd->port_ctrl.lvds_bias_ctrl);
+#endif // NUAND_MODIFICATIONS
+
+ // ad9361_spi_write(spi, REG_DIGITAL_IO_CTRL, pd->port_ctrl.digital_io_ctrl);
+ ad9361_spi_write(spi, REG_LVDS_INVERT_CTRL1, pd->port_ctrl.lvds_invert[0]);
+ ad9361_spi_write(spi, REG_LVDS_INVERT_CTRL2, pd->port_ctrl.lvds_invert[1]);
+
+ if (pd->rx1rx2_phase_inversion_en ||
+ (pd->port_ctrl.pp_conf[1] & INVERT_RX2)) {
+
+ ad9361_spi_writef(spi, REG_PARALLEL_PORT_CONF_2, INVERT_RX2, 1);
+ ad9361_spi_writef(spi, REG_INVERT_BITS,
+ INVERT_RX2_RF_DC_CGOUT_WORD, 0);
+ }
+
+ return 0;
+}
+
+/**
+ * Setup the Gain Control Blocks (common function for MGC, AGC modes)
+ * @param phy The AD9361 state structure.
+ * @param ctrl The gain control settings.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_gc_setup(struct ad9361_rf_phy *phy, struct gain_control *ctrl)
+{
+ struct spi_device *spi = phy->spi;
+ uint32_t reg, tmp1, tmp2;
+
+ dev_dbg(&phy->spi->dev, "%s", __func__);
+
+ reg = DEC_PWR_FOR_GAIN_LOCK_EXIT | DEC_PWR_FOR_LOCK_LEVEL |
+ DEC_PWR_FOR_LOW_PWR;
+
+ if (ctrl->rx1_mode == RF_GAIN_HYBRID_AGC ||
+ ctrl->rx2_mode == RF_GAIN_HYBRID_AGC)
+ reg |= SLOW_ATTACK_HYBRID_MODE;
+
+ reg |= RX1_GAIN_CTRL_SETUP(ctrl->rx1_mode) |
+ RX2_GAIN_CTRL_SETUP(ctrl->rx2_mode);
+
+ phy->agc_mode[0] = ctrl->rx1_mode;
+ phy->agc_mode[1] = ctrl->rx2_mode;
+
+ ad9361_spi_write(spi, REG_AGC_CONFIG_1, reg); // Gain Control Mode Select
+
+ /* AGC_USE_FULL_GAIN_TABLE handled in ad9361_load_gt() */
+ ad9361_spi_writef(spi, REG_AGC_CONFIG_2, MAN_GAIN_CTRL_RX1,
+ ctrl->mgc_rx1_ctrl_inp_en);
+ ad9361_spi_writef(spi, REG_AGC_CONFIG_2, MAN_GAIN_CTRL_RX2,
+ ctrl->mgc_rx2_ctrl_inp_en);
+ ad9361_spi_writef(spi, REG_AGC_CONFIG_2, DIG_GAIN_EN,
+ ctrl->dig_gain_en);
+
+ ctrl->adc_ovr_sample_size = clamp_t(uint8_t, ctrl->adc_ovr_sample_size, 1U, 8U);
+ reg = ADC_OVERRANGE_SAMPLE_SIZE(ctrl->adc_ovr_sample_size - 1);
+
+ if (has_split_gt && phy->pdata->split_gt &&
+ (ctrl->mgc_rx1_ctrl_inp_en || ctrl->mgc_rx2_ctrl_inp_en)) {
+ switch (ctrl->mgc_split_table_ctrl_inp_gain_mode) {
+ case 1:
+ reg &= ~INCDEC_LMT_GAIN;
+ break;
+ case 2:
+ reg |= INCDEC_LMT_GAIN;
+ break;
+ default:
+ case 0:
+ reg |= USE_AGC_FOR_LMTLPF_GAIN;
+ break;
+ }
+ }
+
+ ctrl->mgc_inc_gain_step = clamp_t(uint8_t, ctrl->mgc_inc_gain_step, 1U, 8U);
+ reg |= MANUAL_INCR_STEP_SIZE(ctrl->mgc_inc_gain_step - 1);
+ ad9361_spi_write(spi, REG_AGC_CONFIG_3, reg); // Incr Step Size, ADC Overrange Size
+
+ if (has_split_gt && phy->pdata->split_gt) {
+ reg = SIZE_SPLIT_TABLE - 1;
+ }
+ else {
+ reg = SIZE_FULL_TABLE - 1;
+ }
+ ad9361_spi_write(spi, REG_MAX_LMT_FULL_GAIN, reg); // Max Full/LMT Gain Table Index
+ ad9361_spi_write(spi, REG_RX1_MANUAL_LMT_FULL_GAIN, reg); // Rx1 Full/LMT Gain Index
+ ad9361_spi_write(spi, REG_RX2_MANUAL_LMT_FULL_GAIN, reg); // Rx2 Full/LMT Gain Index
+
+ ctrl->mgc_dec_gain_step = clamp_t(uint8_t, ctrl->mgc_dec_gain_step, 1U, 8U);
+ reg = MANUAL_CTRL_IN_DECR_GAIN_STP_SIZE(ctrl->mgc_dec_gain_step);
+ ad9361_spi_write(spi, REG_PEAK_WAIT_TIME, reg); // Decr Step Size, Peak Overload Time
+
+ if (ctrl->dig_gain_en)
+ ad9361_spi_write(spi, REG_DIGITAL_GAIN,
+ MAXIMUM_DIGITAL_GAIN(ctrl->max_dig_gain) |
+ DIG_GAIN_STP_SIZE(ctrl->dig_gain_step_size));
+
+ if (ctrl->adc_large_overload_thresh >= ctrl->adc_small_overload_thresh) {
+ ad9361_spi_write(spi, REG_ADC_SMALL_OVERLOAD_THRESH,
+ ctrl->adc_small_overload_thresh); // ADC Small Overload Threshold
+ ad9361_spi_write(spi, REG_ADC_LARGE_OVERLOAD_THRESH,
+ ctrl->adc_large_overload_thresh); // ADC Large Overload Threshold
+ }
+ else {
+ ad9361_spi_write(spi, REG_ADC_SMALL_OVERLOAD_THRESH,
+ ctrl->adc_large_overload_thresh); // ADC Small Overload Threshold
+ ad9361_spi_write(spi, REG_ADC_LARGE_OVERLOAD_THRESH,
+ ctrl->adc_small_overload_thresh); // ADC Large Overload Threshold
+ }
+
+ reg = (ctrl->lmt_overload_high_thresh / 16) - 1;
+ reg = clamp(reg, 0U, 63U);
+ ad9361_spi_write(spi, REG_LARGE_LMT_OVERLOAD_THRESH, reg);
+ reg = (ctrl->lmt_overload_low_thresh / 16) - 1;
+ reg = clamp(reg, 0U, 63U);
+ ad9361_spi_writef(spi, REG_SMALL_LMT_OVERLOAD_THRESH,
+ SMALL_LMT_OVERLOAD_THRESH(~0), reg);
+
+ if (has_split_gt && phy->pdata->split_gt) {
+ /* REVIST */
+ ad9361_spi_write(spi, REG_RX1_MANUAL_LPF_GAIN, 0x58); // Rx1 LPF Gain Index
+ ad9361_spi_write(spi, REG_RX2_MANUAL_LPF_GAIN, 0x18); // Rx2 LPF Gain Index
+ ad9361_spi_write(spi, REG_FAST_INITIAL_LMT_GAIN_LIMIT, 0x27); // Initial LMT Gain Limit
+ }
+
+ ad9361_spi_write(spi, REG_RX1_MANUAL_DIGITALFORCED_GAIN, 0x00); // Rx1 Digital Gain Index
+ ad9361_spi_write(spi, REG_RX2_MANUAL_DIGITALFORCED_GAIN, 0x00); // Rx2 Digital Gain Index
+
+ reg = clamp_t(uint8_t, ctrl->low_power_thresh, 0U, 64U) * 2;
+ ad9361_spi_write(spi, REG_FAST_LOW_POWER_THRESH, reg); // Low Power Threshold
+ ad9361_spi_write(spi, REG_TX_SYMBOL_ATTEN_CONFIG, 0x00); // Tx Symbol Gain Control
+
+ ad9361_spi_writef(spi, REG_DEC_POWER_MEASURE_DURATION_0,
+ USE_HB1_OUT_FOR_DEC_PWR_MEAS, 1); // Power Measurement Duration
+
+ ad9361_spi_writef(spi, REG_DEC_POWER_MEASURE_DURATION_0,
+ ENABLE_DEC_PWR_MEAS, 1); // Power Measurement Duration
+
+ if (ctrl->rx1_mode == RF_GAIN_FASTATTACK_AGC ||
+ ctrl->rx2_mode == RF_GAIN_FASTATTACK_AGC)
+ reg = ilog2(ctrl->f_agc_dec_pow_measuremnt_duration / 16);
+ else
+ reg = ilog2(ctrl->dec_pow_measuremnt_duration / 16);
+
+ ad9361_spi_writef(spi, REG_DEC_POWER_MEASURE_DURATION_0,
+ DEC_POWER_MEASUREMENT_DURATION(~0), reg); // Power Measurement Duration
+
+ /* AGC */
+
+ tmp1 = reg = clamp_t(uint8_t, ctrl->agc_inner_thresh_high, 0U, 127U);
+ ad9361_spi_writef(spi, REG_AGC_LOCK_LEVEL,
+ AGC_LOCK_LEVEL_FAST_AGC_INNER_HIGH_THRESH_SLOW(~0),
+ reg);
+
+ tmp2 = reg = clamp_t(uint8_t, ctrl->agc_inner_thresh_low, 0U, 127U);
+ reg |= (ctrl->adc_lmt_small_overload_prevent_gain_inc ?
+ PREVENT_GAIN_INC : 0);
+ ad9361_spi_write(spi, REG_AGC_INNER_LOW_THRESH, reg);
+
+ reg = AGC_OUTER_HIGH_THRESH(tmp1 - ctrl->agc_outer_thresh_high) |
+ AGC_OUTER_LOW_THRESH(ctrl->agc_outer_thresh_low - tmp2);
+ ad9361_spi_write(spi, REG_OUTER_POWER_THRESHS, reg);
+
+ reg = AGC_OUTER_HIGH_THRESH_EXED_STP_SIZE(ctrl->agc_outer_thresh_high_dec_steps) |
+ AGC_OUTER_LOW_THRESH_EXED_STP_SIZE(ctrl->agc_outer_thresh_low_inc_steps);
+ ad9361_spi_write(spi, REG_GAIN_STP_2, reg);
+
+ reg = ((ctrl->immed_gain_change_if_large_adc_overload) ?
+ IMMED_GAIN_CHANGE_IF_LG_ADC_OVERLOAD : 0) |
+ ((ctrl->immed_gain_change_if_large_lmt_overload) ?
+ IMMED_GAIN_CHANGE_IF_LG_LMT_OVERLOAD : 0) |
+ AGC_INNER_HIGH_THRESH_EXED_STP_SIZE(ctrl->agc_inner_thresh_high_dec_steps) |
+ AGC_INNER_LOW_THRESH_EXED_STP_SIZE(ctrl->agc_inner_thresh_low_inc_steps);
+ ad9361_spi_write(spi, REG_GAIN_STP1, reg);
+
+ reg = LARGE_ADC_OVERLOAD_EXED_COUNTER(ctrl->adc_large_overload_exceed_counter) |
+ SMALL_ADC_OVERLOAD_EXED_COUNTER(ctrl->adc_small_overload_exceed_counter);
+ ad9361_spi_write(spi, REG_ADC_OVERLOAD_COUNTERS, reg);
+
+ ad9361_spi_writef(spi, REG_GAIN_STP_CONFIG_2, LARGE_LPF_GAIN_STEP(~0),
+ LARGE_LPF_GAIN_STEP(ctrl->adc_large_overload_inc_steps));
+
+ reg = LARGE_LMT_OVERLOAD_EXED_COUNTER(ctrl->lmt_overload_large_exceed_counter) |
+ SMALL_LMT_OVERLOAD_EXED_COUNTER(ctrl->lmt_overload_small_exceed_counter);
+ ad9361_spi_write(spi, REG_LMT_OVERLOAD_COUNTERS, reg);
+
+ ad9361_spi_writef(spi, REG_GAIN_STP_CONFIG1,
+ DEC_STP_SIZE_FOR_LARGE_LMT_OVERLOAD(~0),
+ ctrl->lmt_overload_large_inc_steps);
+
+ reg = DIG_SATURATION_EXED_COUNTER(ctrl->dig_saturation_exceed_counter) |
+ (ctrl->sync_for_gain_counter_en ?
+ ENABLE_SYNC_FOR_GAIN_COUNTER : 0);
+ ad9361_spi_write(spi, REG_DIGITAL_SAT_COUNTER, reg);
+
+ /*
+ * Fast AGC
+ */
+
+ /* Fast AGC - Low Power */
+ ad9361_spi_writef(spi, REG_FAST_CONFIG_1,
+ ENABLE_INCR_GAIN,
+ ctrl->f_agc_allow_agc_gain_increase);
+
+ ad9361_spi_write(spi, REG_FAST_INCREMENT_TIME,
+ ctrl->f_agc_lp_thresh_increment_time);
+
+ reg = ctrl->f_agc_lp_thresh_increment_steps - 1;
+ reg = clamp_t(uint32_t, reg, 0U, 7U);
+ ad9361_spi_writef(spi, REG_FAST_ENERGY_DETECT_COUNT,
+ INCREMENT_GAIN_STP_LPFLMT(~0), reg);
+
+ /* Fast AGC - Lock Level */
+ /* Dual use see also agc_inner_thresh_high */
+ ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY,
+ ENABLE_LMT_GAIN_INC_FOR_LOCK_LEVEL,
+ ctrl->f_agc_lock_level_lmt_gain_increase_en);
+
+ reg = ctrl->f_agc_lock_level_gain_increase_upper_limit;
+ reg = clamp_t(uint32_t, reg, 0U, 63U);
+ ad9361_spi_writef(spi, REG_FAST_AGCLL_UPPER_LIMIT,
+ AGCLL_MAX_INCREASE(~0), reg);
+
+ /* Fast AGC - Peak Detectors and Final Settling */
+ reg = ctrl->f_agc_lpf_final_settling_steps;
+ reg = clamp_t(uint32_t, reg, 0U, 3U);
+ ad9361_spi_writef(spi, REG_FAST_ENERGY_LOST_THRESH,
+ POST_LOCK_LEVEL_STP_SIZE_FOR_LPF_TABLE_FULL_TABLE(~0),
+ reg);
+
+ reg = ctrl->f_agc_lmt_final_settling_steps;
+ reg = clamp_t(uint32_t, reg, 0U, 3U);
+ ad9361_spi_writef(spi, REG_FAST_STRONGER_SIGNAL_THRESH,
+ POST_LOCK_LEVEL_STP_FOR_LMT_TABLE(~0), reg);
+
+ reg = ctrl->f_agc_final_overrange_count;
+ reg = clamp_t(uint32_t, reg, 0U, 7U);
+ ad9361_spi_writef(spi, REG_FAST_FINAL_OVER_RANGE_AND_OPT_GAIN,
+ FINAL_OVER_RANGE_COUNT(~0), reg);
+
+ /* Fast AGC - Final Power Test */
+ ad9361_spi_writef(spi, REG_FAST_CONFIG_1,
+ ENABLE_GAIN_INC_AFTER_GAIN_LOCK,
+ ctrl->f_agc_gain_increase_after_gain_lock_en);
+
+ /* Fast AGC - Unlocking the Gain */
+ /* 0 = MAX Gain, 1 = Optimized Gain, 2 = Set Gain */
+
+ reg = ctrl->f_agc_gain_index_type_after_exit_rx_mode;
+ ad9361_spi_writef(spi, REG_FAST_CONFIG_1,
+ GOTO_SET_GAIN_IF_EXIT_RX_STATE, reg == SET_GAIN);
+ ad9361_spi_writef(spi, REG_FAST_CONFIG_1,
+ GOTO_OPTIMIZED_GAIN_IF_EXIT_RX_STATE,
+ reg == OPTIMIZED_GAIN);
+
+ ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY,
+ USE_LAST_LOCK_LEVEL_FOR_SET_GAIN,
+ ctrl->f_agc_use_last_lock_level_for_set_gain_en);
+
+ reg = ctrl->f_agc_optimized_gain_offset;
+ reg = clamp_t(uint32_t, reg, 0U, 15U);
+ ad9361_spi_writef(spi, REG_FAST_FINAL_OVER_RANGE_AND_OPT_GAIN,
+ OPTIMIZE_GAIN_OFFSET(~0), reg);
+
+ tmp1 = !ctrl->f_agc_rst_gla_stronger_sig_thresh_exceeded_en ||
+ !ctrl->f_agc_rst_gla_engergy_lost_sig_thresh_exceeded_en ||
+ !ctrl->f_agc_rst_gla_large_adc_overload_en ||
+ !ctrl->f_agc_rst_gla_large_lmt_overload_en ||
+ ctrl->f_agc_rst_gla_en_agc_pulled_high_en;
+
+ ad9361_spi_writef(spi, REG_AGC_CONFIG_2,
+ AGC_GAIN_UNLOCK_CTRL, tmp1);
+
+ reg = !ctrl->f_agc_rst_gla_stronger_sig_thresh_exceeded_en;
+ ad9361_spi_writef(spi, REG_FAST_STRONG_SIGNAL_FREEZE,
+ DONT_UNLOCK_GAIN_IF_STRONGER_SIGNAL, reg);
+
+ reg = ctrl->f_agc_rst_gla_stronger_sig_thresh_above_ll;
+ reg = clamp_t(uint32_t, reg, 0U, 63U);
+ ad9361_spi_writef(spi, REG_FAST_STRONGER_SIGNAL_THRESH,
+ STRONGER_SIGNAL_THRESH(~0), reg);
+
+ reg = ctrl->f_agc_rst_gla_engergy_lost_sig_thresh_below_ll;
+ reg = clamp_t(uint32_t, reg, 0U, 63U);
+ ad9361_spi_writef(spi, REG_FAST_ENERGY_LOST_THRESH,
+ ENERGY_LOST_THRESH(~0), reg);
+
+ reg = ctrl->f_agc_rst_gla_engergy_lost_goto_optim_gain_en;
+ ad9361_spi_writef(spi, REG_FAST_CONFIG_1,
+ GOTO_OPT_GAIN_IF_ENERGY_LOST_OR_EN_AGC_HIGH, reg);
+
+ reg = !ctrl->f_agc_rst_gla_engergy_lost_sig_thresh_exceeded_en;
+ ad9361_spi_writef(spi, REG_FAST_CONFIG_1,
+ DONT_UNLOCK_GAIN_IF_ENERGY_LOST, reg);
+
+ reg = ctrl->f_agc_energy_lost_stronger_sig_gain_lock_exit_cnt;
+ reg = clamp_t(uint32_t, reg, 0U, 63U);
+ ad9361_spi_writef(spi, REG_FAST_GAIN_LOCK_EXIT_COUNT,
+ GAIN_LOCK_EXIT_COUNT(~0), reg);
+
+ reg = !ctrl->f_agc_rst_gla_large_adc_overload_en ||
+ !ctrl->f_agc_rst_gla_large_lmt_overload_en;
+ ad9361_spi_writef(spi, REG_FAST_CONFIG_1,
+ DONT_UNLOCK_GAIN_IF_LG_ADC_OR_LMT_OVRG, reg);
+
+ reg = !ctrl->f_agc_rst_gla_large_adc_overload_en;
+ ad9361_spi_writef(spi, REG_FAST_LOW_POWER_THRESH,
+ DONT_UNLOCK_GAIN_IF_ADC_OVRG, reg);
+
+ /* 0 = Max Gain, 1 = Set Gain, 2 = Optimized Gain, 3 = No Gain Change */
+
+ if (ctrl->f_agc_rst_gla_en_agc_pulled_high_en) {
+ switch (ctrl->f_agc_rst_gla_if_en_agc_pulled_high_mode) {
+ case MAX_GAIN:
+ ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY,
+ GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH, 1);
+
+ ad9361_spi_writef(spi, REG_FAST_CONFIG_1,
+ GOTO_SET_GAIN_IF_EN_AGC_HIGH, 0);
+
+ ad9361_spi_writef(spi, REG_FAST_CONFIG_1,
+ GOTO_OPT_GAIN_IF_ENERGY_LOST_OR_EN_AGC_HIGH, 0);
+ break;
+ case SET_GAIN:
+ ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY,
+ GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH, 0);
+
+ ad9361_spi_writef(spi, REG_FAST_CONFIG_1,
+ GOTO_SET_GAIN_IF_EN_AGC_HIGH, 1);
+ break;
+ case OPTIMIZED_GAIN:
+ ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY,
+ GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH, 1);
+
+ ad9361_spi_writef(spi, REG_FAST_CONFIG_1,
+ GOTO_SET_GAIN_IF_EN_AGC_HIGH, 0);
+
+ ad9361_spi_writef(spi, REG_FAST_CONFIG_1,
+ GOTO_OPT_GAIN_IF_ENERGY_LOST_OR_EN_AGC_HIGH, 1);
+ break;
+ case NO_GAIN_CHANGE:
+ ad9361_spi_writef(spi, REG_FAST_CONFIG_1,
+ GOTO_SET_GAIN_IF_EN_AGC_HIGH, 0);
+ ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY,
+ GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH, 0);
+ break;
+ }
+ }
+ else {
+ ad9361_spi_writef(spi, REG_FAST_CONFIG_1,
+ GOTO_SET_GAIN_IF_EN_AGC_HIGH, 0);
+ ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY,
+ GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH, 0);
+ }
+
+ reg = ilog2(ctrl->f_agc_power_measurement_duration_in_state5 / 16);
+ reg = clamp_t(uint32_t, reg, 0U, 15U);
+ ad9361_spi_writef(spi, REG_RX1_MANUAL_LPF_GAIN,
+ POWER_MEAS_IN_STATE_5(~0), reg);
+ ad9361_spi_writef(spi, REG_RX1_MANUAL_LMT_FULL_GAIN,
+ POWER_MEAS_IN_STATE_5_MSB, reg >> 3);
+
+ return ad9361_gc_update(phy);
+}
+
+/**
+ * Set the Aux DAC.
+ * @param phy The AD9361 state structure.
+ * @param dac The DAC.
+ * @param val_mV The value.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_auxdac_set(struct ad9361_rf_phy *phy, int32_t dac,
+ int32_t val_mV)
+{
+ struct spi_device *spi = phy->spi;
+ uint32_t val, tmp;
+
+ dev_dbg(&phy->spi->dev, "%s DAC%"PRId32" = %"PRId32" mV", __func__, dac, val_mV);
+
+ /* Disable DAC if val == 0, Ignored in ENSM Auto Mode */
+ ad9361_spi_writef(spi, REG_AUXDAC_ENABLE_CTRL,
+ AUXDAC_MANUAL_BAR(dac), val_mV ? 0 : 1);
+
+ if (val_mV < 306)
+ val_mV = 306;
+
+ if (val_mV < 1888) {
+ val = ((val_mV - 306) * 1000) / 1404; /* Vref = 1V, Step = 2 */
+ tmp = AUXDAC_1_VREF(0);
+ }
+ else {
+ val = ((val_mV - 1761) * 1000) / 1836; /* Vref = 2.5V, Step = 2 */
+ tmp = AUXDAC_1_VREF(3);
+ }
+
+ val = clamp_t(uint32_t, val, 0, 1023);
+
+ switch (dac) {
+ case 1:
+ ad9361_spi_write(spi, REG_AUXDAC_1_WORD, val >> 2);
+ ad9361_spi_write(spi, REG_AUXDAC_1_CONFIG, AUXDAC_1_WORD_LSB(val) | tmp);
+ phy->auxdac1_value = val_mV;
+ break;
+ case 2:
+ ad9361_spi_write(spi, REG_AUXDAC_2_WORD, val >> 2);
+ ad9361_spi_write(spi, REG_AUXDAC_2_CONFIG, AUXDAC_2_WORD_LSB(val) | tmp);
+ phy->auxdac2_value = val_mV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * Get the Aux DAC value.
+ * @param phy The AD9361 state structure.
+ * @param dac The DAC.
+ * @return The value in case of success, negative error code otherwise.
+ */
+int32_t ad9361_auxdac_get(struct ad9361_rf_phy *phy, int32_t dac)
+{
+
+ switch (dac) {
+ case 1:
+ return phy->auxdac1_value;
+ case 2:
+ return phy->auxdac2_value;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * Setup the AuxDAC.
+ * @param phy The AD9361 state structure.
+ * @param ctrl Pointer to auxdac_control structure.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_auxdac_setup(struct ad9361_rf_phy *phy,
+struct auxdac_control *ctrl)
+{
+ struct spi_device *spi = phy->spi;
+ uint8_t tmp;
+
+ dev_dbg(&phy->spi->dev, "%s", __func__);
+
+ ad9361_auxdac_set(phy, 1, ctrl->dac1_default_value);
+ ad9361_auxdac_set(phy, 2, ctrl->dac2_default_value);
+
+ tmp = ~(AUXDAC_AUTO_TX_BAR(ctrl->dac2_in_tx_en << 1 | ctrl->dac1_in_tx_en) |
+ AUXDAC_AUTO_RX_BAR(ctrl->dac2_in_rx_en << 1 | ctrl->dac1_in_rx_en) |
+ AUXDAC_INIT_BAR(ctrl->dac2_in_alert_en << 1 | ctrl->dac1_in_alert_en));
+
+ ad9361_spi_writef(spi, REG_AUXDAC_ENABLE_CTRL,
+ AUXDAC_AUTO_TX_BAR(~0) |
+ AUXDAC_AUTO_RX_BAR(~0) |
+ AUXDAC_INIT_BAR(~0),
+ tmp); /* Auto Control */
+
+ ad9361_spi_writef(spi, REG_EXTERNAL_LNA_CTRL,
+ AUXDAC_MANUAL_SELECT, ctrl->auxdac_manual_mode_en);
+ ad9361_spi_write(spi, REG_AUXDAC1_RX_DELAY, ctrl->dac1_rx_delay_us);
+ ad9361_spi_write(spi, REG_AUXDAC1_TX_DELAY, ctrl->dac1_tx_delay_us);
+ ad9361_spi_write(spi, REG_AUXDAC2_RX_DELAY, ctrl->dac2_rx_delay_us);
+ ad9361_spi_write(spi, REG_AUXDAC2_TX_DELAY, ctrl->dac2_tx_delay_us);
+
+ return 0;
+}
+
+/**
+ * Setup the AuxADC.
+ * @param phy The AD9361 state structure.
+ * @param ctrl The AuxADC settings.
+ * @param bbpll_freq The BBPLL frequency [Hz].
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_auxadc_setup(struct ad9361_rf_phy *phy,
+struct auxadc_control *ctrl,
+ uint32_t bbpll_freq)
+{
+ struct spi_device *spi = phy->spi;
+ uint32_t val;
+
+ dev_dbg(&phy->spi->dev, "%s", __func__);
+
+ val = DIV_ROUND_CLOSEST(ctrl->temp_time_inteval_ms *
+ (bbpll_freq / 1000UL), (1 << 29));
+
+ ad9361_spi_write(spi, REG_TEMP_OFFSET, ctrl->offset);
+ ad9361_spi_write(spi, REG_START_TEMP_READING, 0x00);
+ ad9361_spi_write(spi, REG_TEMP_SENSE2,
+ MEASUREMENT_TIME_INTERVAL(val) |
+ (ctrl->periodic_temp_measuremnt ?
+ TEMP_SENSE_PERIODIC_ENABLE : 0));
+ ad9361_spi_write(spi, REG_TEMP_SENSOR_CONFIG,
+ TEMP_SENSOR_DECIMATION(
+ ilog2(ctrl->temp_sensor_decimation) - 8));
+ ad9361_spi_write(spi, REG_AUXADC_CLOCK_DIVIDER,
+ bbpll_freq / ctrl->auxadc_clock_rate);
+ ad9361_spi_write(spi, REG_AUXADC_CONFIG,
+ AUX_ADC_DECIMATION(
+ ilog2(ctrl->auxadc_decimation) - 8));
+
+ return 0;
+}
+
+/**
+ * Get the measured temperature of the device.
+ * @param phy The AD9361 state structure.
+ * @return The measured temperature of the device.
+ */
+int32_t ad9361_get_temp(struct ad9361_rf_phy *phy)
+{
+ uint32_t val;
+
+ ad9361_spi_writef(phy->spi, REG_AUXADC_CONFIG, AUXADC_POWER_DOWN, 1);
+ val = ad9361_spi_read(phy->spi, REG_TEMPERATURE);
+ ad9361_spi_writef(phy->spi, REG_AUXADC_CONFIG, AUXADC_POWER_DOWN, 0);
+
+ return DIV_ROUND_CLOSEST(val * 1000000, 1140);
+}
+
+/**
+ * Get the Aux ADC value.
+ * @param phy The AD9361 state structure.
+ * @return The value in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_auxadc(struct ad9361_rf_phy *phy)
+{
+ uint8_t buf[2];
+
+ ad9361_spi_writef(phy->spi, REG_AUXADC_CONFIG, AUXADC_POWER_DOWN, 1);
+ ad9361_spi_readm(phy->spi, REG_AUXADC_LSB, buf, 2);
+ ad9361_spi_writef(phy->spi, REG_AUXADC_CONFIG, AUXADC_POWER_DOWN, 0);
+
+ return (buf[1] << 4) | AUXADC_WORD_LSB(buf[0]);
+}
+
+/**
+ * Setup the Control Output pins.
+ * @param phy The AD9361 state structure.
+ * @param ctrl The Control Output pins settings.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_ctrl_outs_setup(struct ad9361_rf_phy *phy,
+struct ctrl_outs_control *ctrl)
+{
+ struct spi_device *spi = phy->spi;
+
+ dev_dbg(&phy->spi->dev, "%s", __func__);
+
+ ad9361_spi_write(spi, REG_CTRL_OUTPUT_POINTER, ctrl->index); // Ctrl Out index
+ return ad9361_spi_write(spi, REG_CTRL_OUTPUT_ENABLE, ctrl->en_mask); // Ctrl Out [7:0] output enable
+}
+
+/**
+ * Setup the GPO pins.
+ * @param phy The AD9361 state structure.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_gpo_setup(struct ad9361_rf_phy *phy, struct gpo_control *ctrl)
+{
+ struct spi_device *spi = phy->spi;
+
+ dev_dbg(&phy->spi->dev, "%s", __func__);
+
+ ad9361_spi_write(spi, REG_AUTO_GPO,
+ GPO_ENABLE_AUTO_RX(ctrl->gpo0_slave_rx_en |
+ (ctrl->gpo1_slave_rx_en << 1) |
+ (ctrl->gpo2_slave_rx_en << 2) |
+ (ctrl->gpo3_slave_rx_en << 3)) |
+ GPO_ENABLE_AUTO_TX(ctrl->gpo0_slave_tx_en |
+ (ctrl->gpo1_slave_tx_en << 1) |
+ (ctrl->gpo2_slave_tx_en << 2) |
+ (ctrl->gpo3_slave_tx_en << 3)));
+
+ ad9361_spi_write(spi, REG_GPO_FORCE_AND_INIT,
+ GPO_INIT_STATE(ctrl->gpo0_inactive_state_high_en |
+ (ctrl->gpo1_inactive_state_high_en << 1) |
+ (ctrl->gpo2_inactive_state_high_en << 2) |
+ (ctrl->gpo3_inactive_state_high_en << 3)));
+
+ ad9361_spi_write(spi, REG_GPO0_RX_DELAY, ctrl->gpo0_rx_delay_us);
+ ad9361_spi_write(spi, REG_GPO0_TX_DELAY, ctrl->gpo0_tx_delay_us);
+ ad9361_spi_write(spi, REG_GPO1_RX_DELAY, ctrl->gpo1_rx_delay_us);
+ ad9361_spi_write(spi, REG_GPO1_TX_DELAY, ctrl->gpo1_tx_delay_us);
+ ad9361_spi_write(spi, REG_GPO2_RX_DELAY, ctrl->gpo2_rx_delay_us);
+ ad9361_spi_write(spi, REG_GPO2_TX_DELAY, ctrl->gpo2_tx_delay_us);
+ ad9361_spi_write(spi, REG_GPO3_RX_DELAY, ctrl->gpo3_rx_delay_us);
+ ad9361_spi_write(spi, REG_GPO3_TX_DELAY, ctrl->gpo3_tx_delay_us);
+
+ return 0;
+}
+
+/**
+ * Setup the RSSI.
+ * @param phy The AD9361 state structure.
+ * @param ctrl The RSSI settings.
+ * @param is_update True if update
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_rssi_setup(struct ad9361_rf_phy *phy,
+struct rssi_control *ctrl,
+ bool is_update)
+{
+ struct spi_device *spi = phy->spi;
+ uint32_t total_weight, weight[4], total_dur = 0, temp;
+ uint8_t dur_buf[4] = { 0 };
+ int32_t val, ret, i, j = 0;
+ uint32_t rssi_delay;
+ uint32_t rssi_wait;
+ int32_t rssi_duration;
+ uint32_t rate;
+
+ dev_dbg(&phy->spi->dev, "%s", __func__);
+
+ if (ctrl->rssi_unit_is_rx_samples) {
+ if (is_update)
+ return 0; /* no update required */
+
+ rssi_delay = ctrl->rssi_delay;
+ rssi_wait = ctrl->rssi_wait;
+ rssi_duration = ctrl->rssi_duration;
+ }
+ else {
+ /* update sample based on RX rate */
+ rate = DIV_ROUND_CLOSEST(
+ clk_get_rate(phy, phy->ref_clk_scale[RX_SAMPL_CLK]), 1000);
+ /* units are in us */
+ rssi_delay = DIV_ROUND_CLOSEST(ctrl->rssi_delay * rate, 1000);
+ rssi_wait = DIV_ROUND_CLOSEST(ctrl->rssi_wait * rate, 1000);
+ rssi_duration = DIV_ROUND_CLOSEST(
+ ctrl->rssi_duration * rate, 1000);
+ }
+
+ if (ctrl->restart_mode == EN_AGC_PIN_IS_PULLED_HIGH)
+ rssi_delay = 0;
+
+ rssi_delay = clamp(rssi_delay / 8, 0U, 255U);
+ rssi_wait = clamp(rssi_wait / 4, 0U, 255U);
+
+ do {
+ for (i = 14; rssi_duration > 0 && i >= 0; i--) {
+ val = 1 << i;
+ if ((int64_t)rssi_duration >= val) {
+ dur_buf[j++] = i;
+ total_dur += val;
+ rssi_duration -= val;
+ break;
+ }
+ }
+
+ } while (j < 4 && rssi_duration > 0);
+
+ for (i = 0, total_weight = 0; i < 4; i++) {
+ if (i < j)
+ total_weight += weight[i] =
+ DIV_ROUND_CLOSEST(RSSI_MAX_WEIGHT *
+ (1 << dur_buf[i]), total_dur);
+ else
+ total_weight += weight[i] = 0;
+ }
+
+ /* total of all weights must be 0xFF */
+ val = total_weight - 0xFF;
+ weight[j - 1] -= val;
+
+ ad9361_spi_write(spi, REG_MEASURE_DURATION_01,
+ (dur_buf[1] << 4) | dur_buf[0]); // RSSI Measurement Duration 0, 1
+ ad9361_spi_write(spi, REG_MEASURE_DURATION_23,
+ (dur_buf[3] << 4) | dur_buf[2]); // RSSI Measurement Duration 2, 3
+ ad9361_spi_write(spi, REG_RSSI_WEIGHT_0, weight[0]); // RSSI Weighted Multiplier 0
+ ad9361_spi_write(spi, REG_RSSI_WEIGHT_1, weight[1]); // RSSI Weighted Multiplier 1
+ ad9361_spi_write(spi, REG_RSSI_WEIGHT_2, weight[2]); // RSSI Weighted Multiplier 2
+ ad9361_spi_write(spi, REG_RSSI_WEIGHT_3, weight[3]); // RSSI Weighted Multiplier 3
+ ad9361_spi_write(spi, REG_RSSI_DELAY, rssi_delay); // RSSI Delay
+ ad9361_spi_write(spi, REG_RSSI_WAIT_TIME, rssi_wait); // RSSI Wait
+
+ temp = RSSI_MODE_SELECT(ctrl->restart_mode);
+ if (ctrl->restart_mode == SPI_WRITE_TO_REGISTER)
+ temp |= START_RSSI_MEAS;
+
+ if (rssi_duration == 0 && j == 1) /* Power of two */
+ temp |= DEFAULT_RSSI_MEAS_MODE;
+
+ ret = ad9361_spi_write(spi, REG_RSSI_CONFIG, temp); // RSSI Mode Select
+
+ if (ret < 0)
+ dev_err(&phy->spi->dev, "Unable to write rssi config");
+
+ return 0;
+}
+
+/**
+ * This function needs to be called whenever BBPLL changes.
+ * @param phy The AD9361 state structure.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_bb_clk_change_handler(struct ad9361_rf_phy *phy)
+{
+ int32_t ret;
+
+ ret = ad9361_gc_update(phy);
+ ret |= ad9361_rssi_setup(phy, &phy->pdata->rssi_ctrl, true);
+ ret |= ad9361_auxadc_setup(phy, &phy->pdata->auxadc_ctrl,
+ clk_get_rate(phy, phy->ref_clk_scale[BBPLL_CLK]));
+
+ return ret;
+}
+
+/**
+ * Set the desired Enable State Machine (ENSM) state.
+ * @param phy The AD9361 state structure.
+ * @param ensm_state The ENSM state [ENSM_STATE_SLEEP_WAIT, ENSM_STATE_ALERT,
+ * ENSM_STATE_TX, ENSM_STATE_TX_FLUSH, ENSM_STATE_RX,
+ * ENSM_STATE_RX_FLUSH, ENSM_STATE_FDD, ENSM_STATE_FDD_FLUSH].
+ * @param pinctrl Set true, will enable the ENSM pin control.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_ensm_set_state(struct ad9361_rf_phy *phy, uint8_t ensm_state,
+ bool pinctrl)
+{
+ struct spi_device *spi = phy->spi;
+ int32_t rc = 0;
+ uint32_t val;
+ uint32_t tmp;
+
+ dev_dbg(dev, "Device is in %x state, moving to %x", phy->curr_ensm_state,
+ ensm_state);
+
+
+ if (phy->curr_ensm_state == ENSM_STATE_SLEEP) {
+ ad9361_spi_write(spi, REG_CLOCK_ENABLE,
+ DIGITAL_POWER_UP | CLOCK_ENABLE_DFLT | BBPLL_ENABLE |
+ (phy->pdata->use_extclk ? XO_BYPASS : 0)); /* Enable Clocks */
+ udelay(20);
+ ad9361_spi_write(spi, REG_ENSM_CONFIG_1, TO_ALERT | FORCE_ALERT_STATE);
+ ad9361_trx_vco_cal_control(phy, false, true); /* Enable VCO Cal */
+ ad9361_trx_vco_cal_control(phy, true, true);
+ }
+
+ val = (phy->pdata->ensm_pin_pulse_mode ? 0 : LEVEL_MODE) |
+ (pinctrl ? ENABLE_ENSM_PIN_CTRL : 0) |
+ (phy->txmon_tdd_en ? ENABLE_RX_DATA_PORT_FOR_CAL : 0) |
+ TO_ALERT;
+
+ switch (ensm_state) {
+ case ENSM_STATE_TX:
+ val |= FORCE_TX_ON;
+ if (phy->pdata->fdd)
+ rc = -EINVAL;
+ else if (phy->curr_ensm_state != ENSM_STATE_ALERT)
+ rc = -EINVAL;
+ break;
+ case ENSM_STATE_RX:
+ val |= FORCE_RX_ON;
+ if (phy->pdata->fdd)
+ rc = -EINVAL;
+ else if (phy->curr_ensm_state != ENSM_STATE_ALERT)
+ rc = -EINVAL;
+ break;
+ case ENSM_STATE_FDD:
+ val |= FORCE_TX_ON;
+ if (!phy->pdata->fdd)
+ rc = -EINVAL;
+ break;
+ case ENSM_STATE_ALERT:
+ val &= ~(FORCE_TX_ON | FORCE_RX_ON);
+ val |= TO_ALERT | FORCE_ALERT_STATE;
+ break;
+ case ENSM_STATE_SLEEP_WAIT:
+ break;
+ case ENSM_STATE_SLEEP:
+ ad9361_trx_vco_cal_control(phy, false, false); /* Disable VCO Cal */
+ ad9361_trx_vco_cal_control(phy, true, false);
+ ad9361_spi_write(spi, REG_ENSM_CONFIG_1, 0); /* Clear To Alert */
+ ad9361_spi_write(spi, REG_ENSM_CONFIG_1,
+ phy->pdata->fdd ? FORCE_TX_ON : FORCE_RX_ON);
+ /* Delay Flush Time 384 ADC clock cycles */
+ udelay(384000000UL / clk_get_rate(phy, phy->ref_clk_scale[ADC_CLK]));
+ ad9361_spi_write(spi, REG_ENSM_CONFIG_1, 0); /* Move to Wait*/
+ udelay(1); /* Wait for ENSM settle */
+ ad9361_spi_write(spi, REG_CLOCK_ENABLE,
+ (phy->pdata->use_extclk ? XO_BYPASS : 0)); /* Turn off all clocks */
+ phy->curr_ensm_state = ensm_state;
+ return 0;
+
+ default:
+ dev_err(dev, "No handling for forcing %d ensm state",
+ ensm_state);
+ goto out;
+ }
+
+ if (rc) {
+ if ((phy->curr_ensm_state != ENSM_STATE_ALERT) && (val & (FORCE_RX_ON | FORCE_TX_ON))) {
+ uint32_t val2 = val;
+
+ val2 &= ~(FORCE_TX_ON | FORCE_RX_ON);
+ val2 |= TO_ALERT | FORCE_ALERT_STATE;
+ ad9361_spi_write(spi, REG_ENSM_CONFIG_1, val2);
+
+ ad9361_check_cal_done(phy, REG_STATE, ENSM_STATE(~0), ENSM_STATE_ALERT);
+ } else {
+ dev_err(dev, "Invalid ENSM state transition in %s mode",
+ phy->pdata->fdd ? "FDD" : "TDD");
+ goto out;
+ }
+ }
+
+ if (!phy->pdata->fdd && !pinctrl && !phy->pdata->tdd_use_dual_synth &&
+ (ensm_state == ENSM_STATE_TX || ensm_state == ENSM_STATE_RX)) {
+ ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2,
+ TXNRX_SPI_CTRL, ensm_state == ENSM_STATE_TX);
+
+ ad9361_check_cal_done(phy, (ensm_state == ENSM_STATE_TX) ?
+ REG_TX_CP_OVERRANGE_VCO_LOCK :
+ REG_RX_CP_OVERRANGE_VCO_LOCK,
+ VCO_LOCK, 1);
+ }
+
+ rc = ad9361_spi_write(spi, REG_ENSM_CONFIG_1, val);
+ if (rc)
+ dev_err(dev, "Failed to restore state");
+
+ if ((val & FORCE_RX_ON) &&
+ (phy->agc_mode[0] == RF_GAIN_MGC ||
+ phy->agc_mode[1] == RF_GAIN_MGC)) {
+ tmp = ad9361_spi_read(spi, REG_SMALL_LMT_OVERLOAD_THRESH);
+ ad9361_spi_write(spi, REG_SMALL_LMT_OVERLOAD_THRESH,
+ (tmp & SMALL_LMT_OVERLOAD_THRESH(~0)) |
+ (phy->agc_mode[0] == RF_GAIN_MGC ? FORCE_PD_RESET_RX1 : 0) |
+ (phy->agc_mode[1] == RF_GAIN_MGC ? FORCE_PD_RESET_RX2 : 0));
+ ad9361_spi_write(spi, REG_SMALL_LMT_OVERLOAD_THRESH,
+ tmp & SMALL_LMT_OVERLOAD_THRESH(~0));
+ }
+
+ phy->curr_ensm_state = ensm_state;
+
+out:
+ return rc;
+
+}
+
+/**
+ * Check if at least one of the clock rates is equal to the DATA_CLK (lvds) rate.
+ * @param phy The AD9361 state structure.
+ * @param rx_path_clks RX path rates buffer.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_validate_trx_clock_chain(struct ad9361_rf_phy *phy,
+ uint32_t *rx_path_clks)
+{
+ int32_t i;
+ uint32_t data_clk;
+
+ data_clk = (phy->pdata->rx2tx2 ? 4 : 2) /
+ ((phy->pdata->port_ctrl.pp_conf[2] & LVDS_MODE) ? 1 : 2) *
+ rx_path_clks[RX_SAMPL_FREQ];
+
+ /* CMOS Mode */
+ if (!(phy->pdata->port_ctrl.pp_conf[2] & LVDS_MODE) &&
+ (data_clk > 61440000UL)) {
+ dev_err(&phy->spi->dev,
+ "%s: Failed CMOS MODE DATA_CLK > 61.44MSPS", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 1; i <= 3; i++) {
+ if (abs(rx_path_clks[ADC_FREQ] / i - data_clk) < 4)
+ return 0;
+ }
+
+ for (i = 1; i <= 4; i++) {
+ if (abs((rx_path_clks[R2_FREQ] >> i) - data_clk) < 4)
+ return 0;
+ }
+
+ dev_err(&phy->spi->dev, "%s: Failed - at least one of the clock rates"
+ " must be equal to the DATA_CLK (lvds) rate", __func__);
+
+ return -EINVAL;
+}
+
+/**
+ * Set the RX and TX path rates.
+ * @param phy The AD9361 state structure.
+ * @param rx_path_clks RX path rates buffer.
+ * @param tx_path_clks TX path rates buffer.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_set_trx_clock_chain(struct ad9361_rf_phy *phy,
+ uint32_t *rx_path_clks,
+ uint32_t *tx_path_clks)
+{
+ int32_t ret, i, j, n;
+
+ dev_dbg(&phy->spi->dev, "%s", __func__);
+
+ if (!rx_path_clks || !tx_path_clks)
+ return -EINVAL;
+
+ dev_dbg(&phy->spi->dev, "%s: %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32,
+ __func__, rx_path_clks[BBPLL_FREQ], rx_path_clks[ADC_FREQ],
+ rx_path_clks[R2_FREQ], rx_path_clks[R1_FREQ],
+ rx_path_clks[CLKRF_FREQ], rx_path_clks[RX_SAMPL_FREQ]);
+
+ dev_dbg(&phy->spi->dev, "%s: %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32,
+ __func__, tx_path_clks[BBPLL_FREQ], tx_path_clks[ADC_FREQ],
+ tx_path_clks[R2_FREQ], tx_path_clks[R1_FREQ],
+ tx_path_clks[CLKRF_FREQ], tx_path_clks[RX_SAMPL_FREQ]);
+
+ ret = ad9361_validate_trx_clock_chain(phy, rx_path_clks);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_set_rate(phy, phy->ref_clk_scale[BBPLL_CLK], rx_path_clks[BBPLL_FREQ]);
+ if (ret < 0)
+ return ret;
+
+ for (i = ADC_CLK, j = DAC_CLK, n = ADC_FREQ;
+ i <= RX_SAMPL_CLK; i++, j++, n++) {
+ ret = clk_set_rate(phy, phy->ref_clk_scale[i], rx_path_clks[n]);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set BB ref clock rate (%"PRId32")",
+ ret);
+ return ret;
+ }
+ ret = clk_set_rate(phy, phy->ref_clk_scale[j], tx_path_clks[n]);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set BB ref clock rate (%"PRId32")",
+ ret);
+ return ret;
+ }
+ }
+
+ /*
+ * Workaround for clock framework since clocks don't change we
+ * manually need to enable the filter
+ */
+
+ if (phy->rx_fir_dec == 1 || phy->bypass_rx_fir) {
+ ad9361_spi_writef(phy->spi, REG_RX_ENABLE_FILTER_CTRL,
+ RX_FIR_ENABLE_DECIMATION(~0), !phy->bypass_rx_fir);
+ }
+
+ if (phy->tx_fir_int == 1 || phy->bypass_tx_fir) {
+ ad9361_spi_writef(phy->spi, REG_TX_ENABLE_FILTER_CTRL,
+ TX_FIR_ENABLE_INTERPOLATION(~0), !phy->bypass_tx_fir);
+ }
+
+ /* The FIR filter once enabled causes the interface timing to change.
+ * It's typically not a problem if the timing margin is big enough.
+ * However at 61.44 MSPS it causes problems on some systems.
+ * So we always run the digital tune in case the filter is enabled.
+ * If it is disabled we restore the values from the initial calibration.
+ */
+
+ if (!phy->pdata->dig_interface_tune_fir_disable &&
+ !(phy->bypass_tx_fir && phy->bypass_rx_fir))
+ ret = ad9361_dig_tune(phy, 0, SKIP_STORE_RESULT);
+
+ return ad9361_bb_clk_change_handler(phy);
+}
+
+/**
+ * Get the RX and TX path rates.
+ * @param phy The AD9361 state structure.
+ * @param rx_path_clks RX path rates buffer.
+ * @param tx_path_clks TX path rates buffer.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_trx_clock_chain(struct ad9361_rf_phy *phy, uint32_t *rx_path_clks,
+ uint32_t *tx_path_clks)
+{
+ int32_t i, j, n;
+ uint32_t bbpll_freq;
+
+ if (!rx_path_clks && !tx_path_clks)
+ return -EINVAL;
+
+ bbpll_freq = clk_get_rate(phy, phy->ref_clk_scale[BBPLL_CLK]);
+
+ if (rx_path_clks)
+ rx_path_clks[BBPLL_FREQ] = bbpll_freq;
+
+ if (tx_path_clks)
+ tx_path_clks[BBPLL_FREQ] = bbpll_freq;
+
+ for (i = ADC_CLK, j = DAC_CLK, n = ADC_FREQ;
+ i <= RX_SAMPL_CLK; i++, j++, n++) {
+ if (rx_path_clks)
+ rx_path_clks[n] = clk_get_rate(phy, phy->ref_clk_scale[i]);
+ if (tx_path_clks)
+ tx_path_clks[n] = clk_get_rate(phy, phy->ref_clk_scale[j]);
+ }
+
+ return 0;
+}
+
+/**
+ * Calculate the RX and TX path rates to obtain the desired sample rate.
+ * @param phy The AD9361 state structure.
+ * @param tx_sample_rate The desired sample rate.
+ * @param rate_gov The rate governor option.
+ * @param rx_path_clks RX path rates buffer.
+ * @param tx_path_clks TX path rates buffer.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_calculate_rf_clock_chain(struct ad9361_rf_phy *phy,
+ uint32_t tx_sample_rate,
+ uint32_t rate_gov,
+ uint32_t *rx_path_clks,
+ uint32_t *tx_path_clks)
+{
+ uint32_t clktf, clkrf, adc_rate = 0, dac_rate = 0;
+ uint64_t bbpll_rate;
+ int32_t i, index_rx = -1, index_tx = -1, tmp;
+ uint32_t div, tx_intdec, rx_intdec, recursion = 1;
+ const int8_t clk_dividers[][4] = {
+ { 12, 3, 2, 2 },
+ { 8, 2, 2, 2 },
+ { 6, 3, 1, 2 },
+ { 4, 2, 2, 1 },
+ { 3, 3, 1, 1 },
+ { 2, 2, 1, 1 },
+ { 1, 1, 1, 1 },
+ };
+
+ if (phy->bypass_rx_fir)
+ rx_intdec = 1;
+ else
+ rx_intdec = phy->rx_fir_dec;
+
+ if (phy->bypass_tx_fir)
+ tx_intdec = 1;
+ else
+ tx_intdec = phy->tx_fir_int;
+
+ if ((rate_gov == 1) && ((rx_intdec * tx_sample_rate * 8) < MIN_ADC_CLK)) {
+ recursion = 0;
+ rate_gov = 0;
+ }
+
+ dev_dbg(&phy->spi->dev, "%s: requested rate %"PRIu32" TXFIR int %"PRIu32" RXFIR dec %"PRIu32" mode %s",
+ __func__, tx_sample_rate, tx_intdec, rx_intdec,
+ rate_gov ? "Nominal" : "Highest OSR");
+
+ if (tx_sample_rate > 61440000UL)
+ return -EINVAL;
+
+ clktf = tx_sample_rate * tx_intdec;
+ clkrf = tx_sample_rate * rx_intdec * (phy->rx_eq_2tx ? 2 : 1);
+
+ for (i = rate_gov; i < 7; i++) {
+ adc_rate = clkrf * clk_dividers[i][0];
+ dac_rate = clktf * clk_dividers[i][0];
+
+ if ((adc_rate <= MAX_ADC_CLK) && (adc_rate >= MIN_ADC_CLK)) {
+
+
+ if (dac_rate > adc_rate)
+ tmp = (dac_rate / adc_rate) * -1;
+ else
+ tmp = adc_rate / dac_rate;
+
+ if (adc_rate <= MAX_DAC_CLK) {
+ index_rx = i;
+ index_tx = i - ((tmp == 1) ? 0 : tmp);
+ dac_rate = adc_rate; /* ADC_CLK */
+ break;
+ }
+ else {
+ dac_rate = adc_rate / 2; /* ADC_CLK/2 */
+ index_rx = i;
+
+ if (i == 4 && tmp >= 0)
+ index_tx = 7; /* STOP: 3/2 != 1 */
+ else
+ index_tx = i + ((i == 5 && tmp >= 0) ? 1 : 2) -
+ ((tmp == 1) ? 0 : tmp);
+
+ break;
+ }
+ }
+ }
+
+ if ((index_tx < 0 || index_tx > 6 || index_rx < 0 || index_rx > 6) && rate_gov < 7 && recursion) {
+ return ad9361_calculate_rf_clock_chain(phy, tx_sample_rate,
+ ++rate_gov, rx_path_clks, tx_path_clks);
+ }
+ else if ((index_tx < 0 || index_tx > 6 || index_rx < 0 || index_rx > 6)) {
+ dev_err(&phy->spi->dev, "%s: Failed to find suitable dividers: %s",
+ __func__, (adc_rate < MIN_ADC_CLK) ? "ADC clock below limit" : "BBPLL rate above limit");
+
+ return -EINVAL;
+ }
+
+ /* Calculate target BBPLL rate */
+ div = MAX_BBPLL_DIV;
+
+ do {
+ bbpll_rate = (uint64_t)adc_rate * div;
+ div >>= 1;
+
+ } while ((bbpll_rate > MAX_BBPLL_FREQ) && (div >= MIN_BBPLL_DIV));
+
+ rx_path_clks[BBPLL_FREQ] = bbpll_rate;
+ rx_path_clks[ADC_FREQ] = adc_rate;
+ rx_path_clks[R2_FREQ] = rx_path_clks[ADC_FREQ] / clk_dividers[index_rx][1];
+ rx_path_clks[R1_FREQ] = rx_path_clks[R2_FREQ] / clk_dividers[index_rx][2];
+ rx_path_clks[CLKRF_FREQ] = rx_path_clks[R1_FREQ] / clk_dividers[index_rx][3];
+ rx_path_clks[RX_SAMPL_FREQ] = rx_path_clks[CLKRF_FREQ] / rx_intdec;
+
+ tx_path_clks[BBPLL_FREQ] = bbpll_rate;
+ tx_path_clks[DAC_FREQ] = dac_rate;
+ tx_path_clks[T2_FREQ] = tx_path_clks[DAC_FREQ] / clk_dividers[index_tx][1];
+ tx_path_clks[T1_FREQ] = tx_path_clks[T2_FREQ] / clk_dividers[index_tx][2];
+ tx_path_clks[CLKTF_FREQ] = tx_path_clks[T1_FREQ] / clk_dividers[index_tx][3];
+ tx_path_clks[TX_SAMPL_FREQ] = tx_path_clks[CLKTF_FREQ] / tx_intdec;
+
+ return 0;
+}
+
+/**
+ * Set the desired sample rate.
+ * @param phy The AD9361 state structure.
+ * @param freq The desired sample rate.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_set_trx_clock_chain_freq(struct ad9361_rf_phy *phy,
+ uint32_t freq)
+{
+ uint32_t rx[6], tx[6];
+ int32_t ret;
+
+ ret = ad9361_calculate_rf_clock_chain(phy, freq,
+ phy->rate_governor, rx, tx);
+ if (ret < 0)
+ return ret;
+ return ad9361_set_trx_clock_chain(phy, rx, tx);
+}
+
+/**
+ * Internal ENSM mode options helper function.
+ * @param phy The AD9361 state structure.
+ * @param fdd
+ * @param pinctrl
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_set_ensm_mode(struct ad9361_rf_phy *phy, bool fdd, bool pinctrl)
+{
+ struct ad9361_phy_platform_data *pd = phy->pdata;
+ int32_t ret;
+ uint32_t val = 0;
+
+ ad9361_spi_write(phy->spi, REG_ENSM_MODE, fdd ? FDD_MODE : 0);
+
+ val = ad9361_spi_read(phy->spi, REG_ENSM_CONFIG_2);
+ val &= POWER_DOWN_RX_SYNTH | POWER_DOWN_TX_SYNTH;
+
+ if (fdd)
+ ret = ad9361_spi_write(phy->spi, REG_ENSM_CONFIG_2,
+ val | DUAL_SYNTH_MODE |
+ (pd->fdd_independent_mode ? FDD_EXTERNAL_CTRL_ENABLE : 0));
+ else
+ ret = ad9361_spi_write(phy->spi, REG_ENSM_CONFIG_2, val |
+ (pd->tdd_use_dual_synth ? DUAL_SYNTH_MODE : 0) |
+ (pd->tdd_use_dual_synth ? 0 :
+ (pinctrl ? SYNTH_ENABLE_PIN_CTRL_MODE : 0)));
+
+ return ret;
+}
+
+/**
+ * Fastlock read value.
+ * @param spi
+ * @param tx
+ * @param profile
+ * @param word
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_fastlock_readval(struct spi_device *spi, bool tx,
+ uint32_t profile, uint32_t word)
+{
+ uint32_t offs = 0;
+
+ if (tx)
+ offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP;
+
+ ad9361_spi_write(spi, REG_RX_FAST_LOCK_PROGRAM_ADDR + offs,
+ RX_FAST_LOCK_PROFILE_ADDR(profile) |
+ RX_FAST_LOCK_PROFILE_WORD(word));
+
+ return ad9361_spi_read(spi, REG_RX_FAST_LOCK_PROGRAM_READ + offs);
+}
+
+/**
+ * Fastlock write value.
+ * @param spi
+ * @param tx
+ * @param profile
+ * @param word
+ * @param val
+ * @param last
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_fastlock_writeval(struct spi_device *spi, bool tx,
+ uint32_t profile, uint32_t word, uint8_t val, bool last)
+{
+ uint32_t offs = 0;
+ int32_t ret;
+
+ if (tx)
+ offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP;
+
+ ret = ad9361_spi_write(spi, REG_RX_FAST_LOCK_PROGRAM_ADDR + offs,
+ RX_FAST_LOCK_PROFILE_ADDR(profile) |
+ RX_FAST_LOCK_PROFILE_WORD(word));
+ ret |= ad9361_spi_write(spi, REG_RX_FAST_LOCK_PROGRAM_DATA + offs, val);
+ ret |= ad9361_spi_write(spi, REG_RX_FAST_LOCK_PROGRAM_CTRL + offs,
+ RX_FAST_LOCK_PROGRAM_WRITE |
+ RX_FAST_LOCK_PROGRAM_CLOCK_ENABLE);
+
+ if (last) /* Stop Clocks */
+ ret |= ad9361_spi_write(spi,
+ REG_RX_FAST_LOCK_PROGRAM_CTRL + offs, 0);
+
+ return ret;
+}
+
+/**
+ * Fastlock load values.
+ * @param phy The AD9361 state structure.
+ * @param tx
+ * @param profile
+ * @param values
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_fastlock_load(struct ad9361_rf_phy *phy, bool tx,
+ uint32_t profile, uint8_t *values)
+{
+ uint32_t offs = 0;
+ int32_t i, ret = 0;
+ uint8_t buf[4];
+
+ dev_dbg(&phy->spi->dev, "%s: %s Profile %"PRIu32":",
+ __func__, tx ? "TX" : "RX", profile);
+
+ if (tx)
+ offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP;
+
+ buf[0] = values[0];
+ buf[1] = RX_FAST_LOCK_PROFILE_ADDR(profile) | RX_FAST_LOCK_PROFILE_WORD(0);
+ ad9361_spi_writem(phy->spi, REG_RX_FAST_LOCK_PROGRAM_DATA + offs, buf, 2);
+
+ for (i = 1; i < RX_FAST_LOCK_CONFIG_WORD_NUM; i++) {
+ buf[0] = RX_FAST_LOCK_PROGRAM_WRITE | RX_FAST_LOCK_PROGRAM_CLOCK_ENABLE;
+ buf[1] = 0;
+ buf[2] = values[i];
+ buf[3] = RX_FAST_LOCK_PROFILE_ADDR(profile) | RX_FAST_LOCK_PROFILE_WORD(i);
+ ad9361_spi_writem(phy->spi, REG_RX_FAST_LOCK_PROGRAM_CTRL + offs, buf, 4);
+ }
+
+ ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_PROGRAM_CTRL + offs,
+ RX_FAST_LOCK_PROGRAM_WRITE | RX_FAST_LOCK_PROGRAM_CLOCK_ENABLE);
+ ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_PROGRAM_CTRL + offs, 0);
+
+ phy->fastlock.entry[tx][profile].flags = FASTLOOK_INIT;
+ phy->fastlock.entry[tx][profile].alc_orig = values[15];
+ phy->fastlock.entry[tx][profile].alc_written = values[15];
+
+ return ret;
+}
+
+/**
+ * Fastlock store.
+ * @param phy The AD9361 state structure.
+ * @param tx
+ * @param profile
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_fastlock_store(struct ad9361_rf_phy *phy, bool tx, uint32_t profile)
+{
+ struct spi_device *spi = phy->spi;
+ uint8_t val[16];
+ uint32_t offs = 0, x, y;
+
+ dev_dbg(&phy->spi->dev, "%s: %s Profile %"PRIu32":",
+ __func__, tx ? "TX" : "RX", profile);
+
+ if (tx)
+ offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP;
+
+ val[0] = ad9361_spi_read(spi, REG_RX_INTEGER_BYTE_0 + offs);
+ val[1] = ad9361_spi_read(spi, REG_RX_INTEGER_BYTE_1 + offs);
+ val[2] = ad9361_spi_read(spi, REG_RX_FRACT_BYTE_0 + offs);
+ val[3] = ad9361_spi_read(spi, REG_RX_FRACT_BYTE_1 + offs);
+ val[4] = ad9361_spi_read(spi, REG_RX_FRACT_BYTE_2 + offs);
+
+ x = ad9361_spi_readf(spi, REG_RX_VCO_BIAS_1 + offs, VCO_BIAS_REF(~0));
+ y = ad9361_spi_readf(spi, REG_RX_ALC_VARACTOR + offs, VCO_VARACTOR(~0));
+ val[5] = (x << 4) | y;
+
+ x = ad9361_spi_readf(spi, REG_RX_VCO_BIAS_1 + offs, VCO_BIAS_TCF(~0));
+ y = ad9361_spi_readf(spi, REG_RX_CP_CURRENT + offs, CHARGE_PUMP_CURRENT(~0));
+ /* Wide BW option: N = 1
+ * Set init and steady state values to the same - let user space handle it
+ */
+ val[6] = (x << 3) | y;
+ val[7] = y;
+
+ x = ad9361_spi_readf(spi, REG_RX_LOOP_FILTER_3 + offs, LOOP_FILTER_R3(~0));
+ val[8] = (x << 4) | x;
+
+ x = ad9361_spi_readf(spi, REG_RX_LOOP_FILTER_2 + offs, LOOP_FILTER_C3(~0));
+ val[9] = (x << 4) | x;
+
+ x = ad9361_spi_readf(spi, REG_RX_LOOP_FILTER_1 + offs, LOOP_FILTER_C1(~0));
+ y = ad9361_spi_readf(spi, REG_RX_LOOP_FILTER_1 + offs, LOOP_FILTER_C2(~0));
+ val[10] = (x << 4) | y;
+
+ x = ad9361_spi_readf(spi, REG_RX_LOOP_FILTER_2 + offs, LOOP_FILTER_R1(~0));
+ val[11] = (x << 4) | x;
+
+ x = ad9361_spi_readf(spi, REG_RX_VCO_VARACTOR_CTRL_0 + offs,
+ VCO_VARACTOR_REFERENCE_TCF(~0));
+ y = ad9361_spi_readf(spi, REG_RFPLL_DIVIDERS,
+ tx ? TX_VCO_DIVIDER(~0) : RX_VCO_DIVIDER(~0));
+ val[12] = (x << 4) | y;
+
+ x = ad9361_spi_readf(spi, REG_RX_FORCE_VCO_TUNE_1 + offs, VCO_CAL_OFFSET(~0));
+ y = ad9361_spi_readf(spi, REG_RX_VCO_VARACTOR_CTRL_1 + offs, VCO_VARACTOR_REFERENCE(~0));
+ val[13] = (x << 4) | y;
+
+ val[14] = ad9361_spi_read(spi, REG_RX_FORCE_VCO_TUNE_0 + offs);
+
+ x = ad9361_spi_readf(spi, REG_RX_FORCE_ALC + offs, FORCE_ALC_WORD(~0));
+ y = ad9361_spi_readf(spi, REG_RX_FORCE_VCO_TUNE_1 + offs, FORCE_VCO_TUNE);
+ val[15] = (x << 1) | y;
+
+ return ad9361_fastlock_load(phy, tx, profile, val);
+}
+
+/**
+ * Fastlock prepare.
+ * @param phy The AD9361 state structure.
+ * @param tx
+ * @param profile
+ * @param prepare
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_fastlock_prepare(struct ad9361_rf_phy *phy, bool tx,
+ uint32_t profile, bool prepare)
+{
+ uint32_t offs, ready_mask;
+ bool is_prepared;
+
+ dev_dbg(&phy->spi->dev, "%s: %s Profile %"PRIu32": %s",
+ __func__, tx ? "TX" : "RX", profile,
+ prepare ? "Prepare" : "Un-Prepare");
+
+ if (tx) {
+ offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP;
+ ready_mask = TX_SYNTH_READY_MASK;
+ }
+ else {
+ offs = 0;
+ ready_mask = RX_SYNTH_READY_MASK;
+ }
+
+ is_prepared = !!phy->fastlock.current_profile[tx];
+
+ if (prepare && !is_prepared) {
+ ad9361_spi_write(phy->spi,
+ REG_RX_FAST_LOCK_SETUP_INIT_DELAY + offs,
+ (tx ? phy->pdata->tx_fastlock_delay_ns :
+ phy->pdata->rx_fastlock_delay_ns) / 250);
+ ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_SETUP + offs,
+ RX_FAST_LOCK_PROFILE(profile) |
+ RX_FAST_LOCK_MODE_ENABLE);
+ ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_PROGRAM_CTRL + offs,
+ 0);
+
+ ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, ready_mask, 1);
+ ad9361_trx_vco_cal_control(phy, tx, false);
+ }
+ else if (!prepare && is_prepared) {
+ ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_SETUP + offs, 0);
+
+ /* Workaround: Exiting Fastlock Mode */
+ ad9361_spi_writef(phy->spi, REG_RX_FORCE_ALC + offs, FORCE_ALC_ENABLE, 1);
+ ad9361_spi_writef(phy->spi, REG_RX_FORCE_VCO_TUNE_1 + offs, FORCE_VCO_TUNE, 1);
+ ad9361_spi_writef(phy->spi, REG_RX_FORCE_ALC + offs, FORCE_ALC_ENABLE, 0);
+ ad9361_spi_writef(phy->spi, REG_RX_FORCE_VCO_TUNE_1 + offs, FORCE_VCO_TUNE, 0);
+
+ ad9361_trx_vco_cal_control(phy, tx, true);
+ ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, ready_mask, 0);
+
+ phy->fastlock.current_profile[tx] = 0;
+ }
+
+ return 0;
+}
+
+/**
+ * Fastlock recall.
+ * @param phy The AD9361 state structure.
+ * @param tx
+ * @param profile
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_fastlock_recall(struct ad9361_rf_phy *phy, bool tx, uint32_t profile)
+{
+ uint32_t offs = 0;
+ uint8_t curr, _new, orig, current_profile;
+
+ dev_dbg(&phy->spi->dev, "%s: %s Profile %"PRIu32":",
+ __func__, tx ? "TX" : "RX", profile);
+
+ if (tx)
+ offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP;
+
+ if (phy->fastlock.entry[tx][profile].flags != FASTLOOK_INIT)
+ return -EINVAL;
+
+ /* Workaround: Lock problem with same ALC word */
+
+ current_profile = phy->fastlock.current_profile[tx];
+ _new = phy->fastlock.entry[tx][profile].alc_written;
+
+ if (current_profile == 0)
+ curr = ad9361_spi_readf(phy->spi, REG_RX_FORCE_ALC + offs,
+ FORCE_ALC_WORD(~0)) << 1;
+ else
+ curr = phy->fastlock.entry[tx][current_profile - 1].alc_written;
+
+ if ((curr >> 1) == (_new >> 1)) {
+ orig = phy->fastlock.entry[tx][profile].alc_orig;
+
+ if ((orig >> 1) == (_new >> 1))
+ phy->fastlock.entry[tx][profile].alc_written += 2;
+ else
+ phy->fastlock.entry[tx][profile].alc_written = orig;
+
+ ad9361_fastlock_writeval(phy->spi, tx, profile, 0xF,
+ phy->fastlock.entry[tx][profile].alc_written, true);
+ }
+
+ ad9361_fastlock_prepare(phy, tx, profile, true);
+ phy->fastlock.current_profile[tx] = profile + 1;
+
+ return ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_SETUP + offs,
+ RX_FAST_LOCK_PROFILE(profile) |
+ (phy->pdata->trx_fastlock_pinctrl_en[tx] ?
+ RX_FAST_LOCK_PROFILE_PIN_SELECT : 0) |
+ RX_FAST_LOCK_MODE_ENABLE);
+}
+
+/**
+ * Fastlock save.
+ * @param phy The AD9361 state structure.
+ * @param tx
+ * @param profile
+ * @param values
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_fastlock_save(struct ad9361_rf_phy *phy, bool tx,
+ uint32_t profile, uint8_t *values)
+{
+ int32_t i;
+
+ dev_dbg(&phy->spi->dev, "%s: %s Profile %"PRIu32":",
+ __func__, tx ? "TX" : "RX", profile);
+
+ for (i = 0; i < RX_FAST_LOCK_CONFIG_WORD_NUM; i++)
+ values[i] = ad9361_fastlock_readval(phy->spi, tx, profile, i);
+
+ return 0;
+}
+
+/**
+ * Multi Chip Sync (MCS) config.
+ * @param phy The AD9361 state structure.
+ * @param step MCS step.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_mcs(struct ad9361_rf_phy *phy, int32_t step)
+{
+ int32_t mcs_mask = MCS_RF_ENABLE | MCS_BBPLL_ENABLE |
+ MCS_DIGITAL_CLK_ENABLE | MCS_BB_ENABLE;
+
+ dev_dbg(&phy->spi->dev, "%s: MCS step %"PRId32, __func__, step);
+
+ switch (step) {
+ case 0:
+ /* REVIST:
+ * POWER_DOWN_TRX_SYNTH and MCS_RF_ENABLE somehow conflict
+ */
+ ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2,
+ POWER_DOWN_TX_SYNTH | POWER_DOWN_RX_SYNTH, 0);
+ case 1:
+ /* REVIST:
+ * POWER_DOWN_TRX_SYNTH and MCS_RF_ENABLE somehow conflict
+ */
+ ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2,
+ POWER_DOWN_TX_SYNTH | POWER_DOWN_RX_SYNTH, 0);
+
+ ad9361_spi_writef(phy->spi, REG_MULTICHIP_SYNC_AND_TX_MON_CTRL,
+ mcs_mask, MCS_BB_ENABLE | MCS_BBPLL_ENABLE | MCS_RF_ENABLE);
+ ad9361_spi_writef(phy->spi, REG_CP_BLEED_CURRENT,
+ MCS_REFCLK_SCALE_EN, 1);
+ break;
+ case 2:
+#ifdef NUAND_MODIFICATIONS
+ // use alternate gpio accessors
+ if(!gpio_is_valid(phy->gpio, phy->pdata->gpio_sync))
+ break;
+#else
+ if(!gpio_is_valid(phy->pdata->gpio_sync))
+ break;
+#endif // NUAND_MODIFICATIONS
+ /*
+ * NOTE: This is not a regular GPIO -
+ * HDL ensures Multi-chip Synchronization SYNC_IN Pulse Timing
+ * relative to rising and falling edge of REF_CLK
+ */
+#ifdef NUAND_MODIFICATIONS
+ // use alternate gpio accessors
+ if(!gpio_is_valid(phy->gpio, phy->pdata->gpio_sync))
+ break;
+ gpio_set_value(phy->gpio, phy->pdata->gpio_sync, 1);
+ gpio_set_value(phy->gpio, phy->pdata->gpio_sync, 0);
+#else
+ gpio_set_value(phy->pdata->gpio_sync, 1);
+ gpio_set_value(phy->pdata->gpio_sync, 0);
+ break;
+#endif // NUAND_MODIFICATIONS
+ case 3:
+ ad9361_spi_writef(phy->spi, REG_MULTICHIP_SYNC_AND_TX_MON_CTRL,
+ mcs_mask, MCS_BB_ENABLE | MCS_DIGITAL_CLK_ENABLE | MCS_RF_ENABLE);
+ break;
+ case 4:
+#ifdef NUAND_MODIFICATIONS
+ // use alternate gpio accessors
+ if(!gpio_is_valid(phy->gpio, phy->pdata->gpio_sync))
+ break;
+ gpio_set_value(phy->gpio, phy->pdata->gpio_sync, 1);
+ gpio_set_value(phy->gpio, phy->pdata->gpio_sync, 0);
+ break;
+#else
+ if(!gpio_is_valid(phy->pdata->gpio_sync))
+ break;
+ gpio_set_value(phy->pdata->gpio_sync, 1);
+ gpio_set_value(phy->pdata->gpio_sync, 0);
+ break;
+#endif // NUAND_MODIFICATIONS
+ case 5:
+ ad9361_spi_writef(phy->spi, REG_MULTICHIP_SYNC_AND_TX_MON_CTRL,
+ mcs_mask, MCS_RF_ENABLE);
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * Clear state.
+ * @param phy The AD9361 state structure.
+ * @return None.
+ */
+void ad9361_clear_state(struct ad9361_rf_phy *phy)
+{
+ phy->current_table = RXGAIN_TBLS_END;
+ phy->bypass_tx_fir = true;
+ phy->bypass_rx_fir = true;
+ phy->rate_governor = 1;
+ phy->rfdc_track_en = true;
+ phy->bbdc_track_en = true;
+ phy->quad_track_en = true;
+ phy->prev_ensm_state = 0;
+ phy->curr_ensm_state = 0;
+ phy->auto_cal_en = false;
+ phy->last_tx_quad_cal_freq = 0;
+ phy->flags = 0;
+ phy->current_rx_bw_Hz = 0;
+ phy->current_tx_bw_Hz = 0;
+ phy->rxbbf_div = 0;
+ phy->tx_fir_int = 0;
+ phy->tx_fir_ntaps = 0;
+ phy->rx_fir_dec = 0;
+ phy->rx_fir_ntaps = 0;
+ phy->ensm_pin_ctl_en = false;
+ phy->txmon_tdd_en = 0;
+ phy->current_tx_lo_freq = 0;
+ phy->current_rx_lo_freq = 0;
+ phy->current_tx_use_tdd_table = false;
+ phy->current_rx_use_tdd_table = false;
+
+ memset(&phy->fastlock, 0, sizeof(phy->fastlock));
+}
+
+/**
+ * Determine the reference frequency value.
+ * @param refin_Hz Maximum allowed frequency.
+ * @param max Reference in frequency value.
+ * @return Reference frequency value.
+ */
+static uint32_t ad9361_ref_div_sel(uint32_t refin_Hz, uint32_t max)
+{
+ if (refin_Hz <= (max / 2))
+ return 2 * refin_Hz;
+ else if (refin_Hz <= max)
+ return refin_Hz;
+ else if (refin_Hz <= (max * 2))
+ return refin_Hz / 2;
+ else if (refin_Hz <= (max * 4))
+ return refin_Hz / 4;
+ else
+ return 0;
+}
+
+/**
+ * Setup the AD9361 device.
+ * @param phy The AD9361 state structure.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_setup(struct ad9361_rf_phy *phy)
+{
+ uint32_t refin_Hz, ref_freq, bbpll_freq;
+ struct spi_device *spi = phy->spi;
+ struct ad9361_phy_platform_data *pd = phy->pdata;
+ int32_t ret;
+ uint32_t real_rx_bandwidth, real_tx_bandwidth;
+ bool tmp_use_ext_rx_lo = pd->use_ext_rx_lo;
+ bool tmp_use_ext_tx_lo = pd->use_ext_tx_lo;
+
+ dev_dbg(dev, "%s", __func__);
+
+ pd->rf_rx_bandwidth_Hz = ad9361_validate_rf_bw(phy, pd->rf_rx_bandwidth_Hz);
+ pd->rf_tx_bandwidth_Hz = ad9361_validate_rf_bw(phy, pd->rf_tx_bandwidth_Hz);
+
+ real_rx_bandwidth = pd->rf_rx_bandwidth_Hz / 2;
+ real_tx_bandwidth = pd->rf_tx_bandwidth_Hz / 2;
+
+ if (pd->fdd) {
+ pd->tdd_skip_vco_cal = false;
+ if (pd->ensm_pin_ctrl && pd->fdd_independent_mode) {
+ dev_warn(dev,
+ "%s: Either set ENSM PINCTRL or FDD Independent Mode",
+ __func__);
+ pd->ensm_pin_ctrl = false;
+ }
+ }
+
+ if (pd->port_ctrl.pp_conf[2] & FDD_RX_RATE_2TX_RATE)
+ phy->rx_eq_2tx = true;
+
+ ad9361_spi_write(spi, REG_CTRL, CTRL_ENABLE);
+ ad9361_spi_write(spi, REG_BANDGAP_CONFIG0, MASTER_BIAS_TRIM(0x0E)); /* Enable Master Bias */
+ ad9361_spi_write(spi, REG_BANDGAP_CONFIG1, BANDGAP_TEMP_TRIM(0x0E)); /* Set Bandgap Trim */
+
+ ad9361_set_dcxo_tune(phy, pd->dcxo_coarse, pd->dcxo_fine);
+
+ refin_Hz = phy->clk_refin->rate;
+
+ ref_freq = ad9361_ref_div_sel(refin_Hz, MAX_BBPLL_FREF);
+ if (!ref_freq)
+ return -EINVAL;
+
+ ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_1, RX_REF_RESET_BAR, 1);
+ ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_2, TX_REF_RESET_BAR, 1);
+ ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_2,
+ TX_REF_DOUBLER_FB_DELAY(~0), 3); /* FB DELAY */
+ ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_2,
+ RX_REF_DOUBLER_FB_DELAY(~0), 3); /* FB DELAY */
+
+ ad9361_spi_write(spi, REG_CLOCK_ENABLE,
+ DIGITAL_POWER_UP | CLOCK_ENABLE_DFLT | BBPLL_ENABLE |
+ (pd->use_extclk ? XO_BYPASS : 0)); /* Enable Clocks */
+
+ ret = clk_set_rate(phy, phy->ref_clk_scale[BB_REFCLK], ref_freq);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set BB ref clock rate (%"PRId32")",
+ ret);
+ return ret;
+ }
+
+ ret = ad9361_set_trx_clock_chain(phy, pd->rx_path_clks,
+ pd->tx_path_clks);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_prepare_enable(phy->clks[BB_REFCLK]);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable BB ref clock rate (%"PRId32")",
+ ret);
+ return ret;
+ }
+
+ if (!pd->rx2tx2) {
+ pd->rx1tx1_mode_use_tx_num =
+ clamp_t(uint32_t, pd->rx1tx1_mode_use_tx_num, TX_1, TX_2);
+ pd->rx1tx1_mode_use_rx_num =
+ clamp_t(uint32_t, pd->rx1tx1_mode_use_rx_num, RX_1, RX_2);
+
+ ad9361_en_dis_tx(phy, TX_1 | TX_2, pd->rx1tx1_mode_use_tx_num);
+ ad9361_en_dis_rx(phy, TX_1 | TX_2, pd->rx1tx1_mode_use_rx_num);
+ } else {
+ ad9361_en_dis_tx(phy, TX_1 | TX_2, TX_1 | TX_2);
+ ad9361_en_dis_rx(phy, RX_1 | RX_2, RX_1 | RX_2);
+ }
+
+ ret = ad9361_rf_port_setup(phy, true, pd->rf_rx_input_sel,
+ pd->rf_tx_output_sel);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_pp_port_setup(phy, false);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_auxdac_setup(phy, &pd->auxdac_ctrl);
+ if (ret < 0)
+ return ret;
+
+ bbpll_freq = clk_get_rate(phy, phy->ref_clk_scale[BBPLL_CLK]);
+ ret = ad9361_auxadc_setup(phy, &pd->auxadc_ctrl, bbpll_freq);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_ctrl_outs_setup(phy, &pd->ctrl_outs_ctrl);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_gpo_setup(phy, &pd->gpo_ctrl);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_set_ref_clk_cycles(phy, refin_Hz);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_setup_ext_lna(phy, &pd->elna_ctrl);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * This allows forcing a lower F_REF window
+ * (worse phase noise, better fractional spurs)
+ */
+ pd->trx_synth_max_fref = clamp_t(uint32_t, pd->trx_synth_max_fref,
+ MIN_SYNTH_FREF, MAX_SYNTH_FREF);
+
+ ref_freq = ad9361_ref_div_sel(refin_Hz, pd->trx_synth_max_fref);
+ if (!ref_freq)
+ return -EINVAL;
+
+ ret = clk_set_rate(phy, phy->ref_clk_scale[RX_REFCLK], ref_freq);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set RX Synth ref clock rate (%"PRId32")", ret);
+ return ret;
+ }
+
+ ret = clk_set_rate(phy, phy->ref_clk_scale[TX_REFCLK], ref_freq);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set TX Synth ref clock rate (%"PRId32")", ret);
+ return ret;
+ }
+
+ ret = ad9361_txrx_synth_cp_calib(phy, ref_freq, false); /* RXCP */
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_txrx_synth_cp_calib(phy, ref_freq, true); /* TXCP */
+ if (ret < 0)
+ return ret;
+
+ phy->pdata->use_ext_rx_lo = 0;
+ phy->pdata->use_ext_tx_lo = 0;
+
+ ret = clk_set_rate(phy, phy->ref_clk_scale[RX_RFPLL], ad9361_to_clk(pd->rx_synth_freq));
+ if (ret < 0) {
+ dev_err(dev, "Failed to set RX Synth rate (%"PRId32")",
+ ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(phy->clks[RX_REFCLK]);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable RX Synth ref clock (%"PRId32")", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(phy->clks[RX_RFPLL]);
+ if (ret < 0)
+ return ret;
+
+ /* Skip quad cal here we do it later again */
+ phy->last_tx_quad_cal_freq = pd->tx_synth_freq;
+ ret = clk_set_rate(phy, phy->ref_clk_scale[TX_RFPLL], ad9361_to_clk(pd->tx_synth_freq));
+ if (ret < 0) {
+ dev_err(dev, "Failed to set TX Synth rate (%"PRId32")",
+ ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(phy->clks[TX_REFCLK]);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable TX Synth ref clock (%"PRId32")", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(phy->clks[TX_RFPLL]);
+ if (ret < 0)
+ return ret;
+
+ phy->pdata->use_ext_rx_lo = tmp_use_ext_rx_lo;
+ phy->pdata->use_ext_tx_lo = tmp_use_ext_tx_lo;
+
+ ad9361_clk_mux_set_parent(phy->ref_clk_scale[RX_RFPLL],
+ pd->use_ext_rx_lo);
+
+ ad9361_clk_mux_set_parent(phy->ref_clk_scale[TX_RFPLL],
+ pd->use_ext_tx_lo);
+
+ ret = ad9361_load_mixer_gm_subtable(phy);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_gc_setup(phy, &pd->gain_ctrl);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_rx_bb_analog_filter_calib(phy,
+ real_rx_bandwidth,
+ bbpll_freq);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_tx_bb_analog_filter_calib(phy,
+ real_tx_bandwidth,
+ bbpll_freq);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_rx_tia_calib(phy, real_rx_bandwidth);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_tx_bb_second_filter_calib(phy, real_tx_bandwidth);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_rx_adc_setup(phy,
+ bbpll_freq,
+ clk_get_rate(phy, phy->ref_clk_scale[ADC_CLK]));
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_bb_dc_offset_calib(phy);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_rf_dc_offset_calib(phy,
+ ad9361_from_clk(clk_get_rate(phy, phy->ref_clk_scale[RX_RFPLL])));
+ if (ret < 0)
+ return ret;
+
+ phy->current_rx_bw_Hz = pd->rf_rx_bandwidth_Hz;
+ phy->current_tx_bw_Hz = pd->rf_tx_bandwidth_Hz;
+ phy->last_tx_quad_cal_phase = ~0;
+ ret = ad9361_tx_quad_calib(phy, real_rx_bandwidth, real_tx_bandwidth, -1);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_tracking_control(phy, phy->bbdc_track_en,
+ phy->rfdc_track_en, phy->quad_track_en);
+ if (ret < 0)
+ return ret;
+
+ if (!pd->fdd)
+ ad9361_run_calibration(phy, TXMON_CAL);
+
+ ad9361_pp_port_setup(phy, true);
+
+ ret = ad9361_set_ensm_mode(phy, pd->fdd, pd->ensm_pin_ctrl);
+ if (ret < 0)
+ return ret;
+
+ ad9361_spi_writef(phy->spi, REG_TX_ATTEN_OFFSET,
+ MASK_CLR_ATTEN_UPDATE, 0);
+
+ ret = ad9361_set_tx_atten(phy, pd->tx_atten,
+ pd->rx2tx2 ? true : pd->rx1tx1_mode_use_tx_num == 1,
+ pd->rx2tx2 ? true : pd->rx1tx1_mode_use_tx_num == 2, true);
+ if (ret < 0)
+ return ret;
+
+ if (!pd->rx2tx2) {
+ ret = ad9361_set_tx_atten(phy, 89750,
+ pd->rx1tx1_mode_use_tx_num == 2,
+ pd->rx1tx1_mode_use_tx_num == 1, true);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = ad9361_rssi_setup(phy, &pd->rssi_ctrl, false);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_clkout_control(phy, pd->ad9361_clkout_mode);
+ if (ret < 0)
+ return ret;
+
+
+ ret = ad9361_txmon_setup(phy, &pd->txmon_ctrl);
+ if (ret < 0)
+ return ret;
+
+ phy->curr_ensm_state = ad9361_spi_readf(spi, REG_STATE, ENSM_STATE(~0));
+ ad9361_ensm_set_state(phy, pd->fdd ? ENSM_STATE_FDD : ENSM_STATE_RX,
+ pd->ensm_pin_ctrl);
+
+ phy->auto_cal_en = true;
+ phy->cal_threshold_freq = 100000000ULL; /* 100 MHz */
+
+ return 0;
+
+}
+
+/**
+ * Perform the selected calibration
+ * @param phy The AD9361 state structure.
+ * @param cal The selected calibration.
+ * @param arg The argument of the calibration.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_do_calib_run(struct ad9361_rf_phy *phy, uint32_t cal, int32_t arg)
+{
+ int32_t ret;
+
+ dev_dbg(&phy->spi->dev, "%s: CAL %"PRIu32" ARG %"PRId32, __func__, cal, arg);
+
+ ret = ad9361_tracking_control(phy, false, false, false);
+ if (ret < 0)
+ return ret;
+
+ ad9361_ensm_force_state(phy, ENSM_STATE_ALERT);
+
+ switch (cal) {
+ case TX_QUAD_CAL:
+ ret = ad9361_tx_quad_calib(phy, phy->current_rx_bw_Hz / 2,
+ phy->current_tx_bw_Hz / 2, arg);
+ break;
+ case RFDC_CAL:
+ ret = ad9361_rf_dc_offset_calib(phy,
+ ad9361_from_clk(clk_get_rate(phy, phy->ref_clk_scale[RX_RFPLL])));
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ ret = ad9361_tracking_control(phy, phy->bbdc_track_en,
+ phy->rfdc_track_en, phy->quad_track_en);
+ ad9361_ensm_restore_prev_state(phy);
+
+ return ret;
+}
+
+/**
+ * Set the RF bandwidth.
+ * @param phy The AD9361 state structure.
+ * @param rf_rx_bw The desired RX bandwidth [Hz].
+ * @param rf_tx_bw The desired TX bandwidth [Hz].
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_update_rf_bandwidth(struct ad9361_rf_phy *phy,
+ uint32_t rf_rx_bw, uint32_t rf_tx_bw)
+{
+ int32_t ret;
+
+ ret = ad9361_tracking_control(phy, false, false, false);
+ if (ret < 0)
+ return ret;
+
+ ad9361_ensm_force_state(phy, ENSM_STATE_ALERT);
+
+ ret = __ad9361_update_rf_bandwidth(phy, rf_rx_bw, rf_tx_bw);
+ if (ret < 0)
+ return ret;
+
+ phy->current_rx_bw_Hz = rf_rx_bw;
+ phy->current_tx_bw_Hz = rf_tx_bw;
+
+ ret = ad9361_tx_quad_calib(phy, rf_rx_bw / 2, rf_tx_bw / 2, -1);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_tracking_control(phy, phy->bbdc_track_en,
+ phy->rfdc_track_en, phy->quad_track_en);
+ if (ret < 0)
+ return ret;
+
+ ad9361_ensm_restore_prev_state(phy);
+
+ return 0;
+}
+
+/**
+ * Verify the FIR filter coefficients.
+ * @param phy The AD9361 state structure.
+ * @param dest Destination identifier (RX1,2 / TX1,2).
+ * @param ntaps Number of filter Taps.
+ * @param coef Pointer to filter coefficients.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_verify_fir_filter_coef(struct ad9361_rf_phy *phy,
+ enum fir_dest dest,
+ uint32_t ntaps, short *coef)
+{
+ struct spi_device *spi = phy->spi;
+ uint32_t val, offs = 0, gain = 0, conf, sel, cnt;
+ int32_t ret = 0;
+
+#ifndef DEBUG
+ return 0;
+#endif
+ dev_dbg(&phy->spi->dev, "%s: TAPS %"PRIu32", dest %d",
+ __func__, ntaps, dest);
+
+ if (dest & FIR_IS_RX) {
+ gain = ad9361_spi_read(spi, REG_RX_FILTER_GAIN);
+ offs = REG_RX_FILTER_COEF_ADDR - REG_TX_FILTER_COEF_ADDR;
+ ad9361_spi_write(spi, REG_RX_FILTER_GAIN, 0);
+ }
+
+ conf = ad9361_spi_read(spi, REG_TX_FILTER_CONF + offs);
+
+ if ((dest & 3) == 3) {
+ sel = 1;
+ cnt = 2;
+ } else {
+ sel = (dest & 3);
+ cnt = 1;
+ }
+
+ for (; cnt > 0; cnt--, sel++) {
+
+ ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs,
+ FIR_NUM_TAPS(ntaps / 16 - 1) |
+ FIR_SELECT(sel) | FIR_START_CLK);
+ for (val = 0; val < ntaps; val++) {
+ short tmp;
+ ad9361_spi_write(spi, REG_TX_FILTER_COEF_ADDR + offs, val);
+
+ tmp = (ad9361_spi_read(spi, REG_TX_FILTER_COEF_READ_DATA_1 + offs) & 0xFF) |
+ (ad9361_spi_read(spi, REG_TX_FILTER_COEF_READ_DATA_2 + offs) << 8);
+
+ if (tmp != coef[val]) {
+ dev_err(&phy->spi->dev,"%s%"PRIu32" read verify failed TAP%"PRIu32" %d =! %d",
+ (dest & FIR_IS_RX) ? "RX" : "TX", sel,
+ val, tmp, coef[val]);
+ ret = -EIO;
+ }
+ }
+ }
+
+ if (dest & FIR_IS_RX) {
+ ad9361_spi_write(spi, REG_RX_FILTER_GAIN, gain);
+ }
+
+ ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, conf);
+
+ return ret;
+}
+
+/**
+ * Load the FIR filter coefficients.
+ * @param phy The AD9361 state structure.
+ * @param dest Destination identifier (RX1,2 / TX1,2).
+ * @param gain_dB Gain option.
+ * @param ntaps Number of filter Taps.
+ * @param coef Pointer to filter coefficients.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_load_fir_filter_coef(struct ad9361_rf_phy *phy,
+ enum fir_dest dest, int32_t gain_dB,
+ uint32_t ntaps, int16_t *coef)
+{
+ struct spi_device *spi = phy->spi;
+ uint32_t val, offs = 0, fir_conf = 0, fir_enable = 0;
+
+ dev_dbg(&phy->spi->dev, "%s: TAPS %"PRIu32", gain %"PRId32", dest %d",
+ __func__, ntaps, gain_dB, dest);
+
+ if (coef == NULL || !ntaps || ntaps > 128 || ntaps % 16) {
+ dev_err(&phy->spi->dev,
+ "%s: Invalid parameters: TAPS %"PRIu32", gain %"PRId32", dest 0x%X",
+ __func__, ntaps, gain_dB, dest);
+
+ return -EINVAL;
+ }
+
+ ad9361_ensm_force_state(phy, ENSM_STATE_ALERT);
+
+ if (dest & FIR_IS_RX) {
+ val = 3 - (gain_dB + 12) / 6;
+ ad9361_spi_write(spi, REG_RX_FILTER_GAIN, val & 0x3);
+ offs = REG_RX_FILTER_COEF_ADDR - REG_TX_FILTER_COEF_ADDR;
+ phy->rx_fir_ntaps = ntaps;
+ fir_enable = ad9361_spi_readf(phy->spi,
+ REG_RX_ENABLE_FILTER_CTRL, RX_FIR_ENABLE_DECIMATION(~0));
+ ad9361_spi_writef(phy->spi, REG_RX_ENABLE_FILTER_CTRL,
+ RX_FIR_ENABLE_DECIMATION(~0),
+ (phy->rx_fir_dec == 4) ? 3 : phy->rx_fir_dec);
+ }
+ else {
+ if (gain_dB == -6)
+ fir_conf = TX_FIR_GAIN_6DB;
+ phy->tx_fir_ntaps = ntaps;
+ fir_enable = ad9361_spi_readf(phy->spi,
+ REG_TX_ENABLE_FILTER_CTRL, TX_FIR_ENABLE_INTERPOLATION(~0));
+ ad9361_spi_writef(phy->spi, REG_TX_ENABLE_FILTER_CTRL,
+ TX_FIR_ENABLE_INTERPOLATION(~0),
+ (phy->tx_fir_int == 4) ? 3 : phy->tx_fir_int);
+ }
+
+ val = ntaps / 16 - 1;
+
+ fir_conf |= FIR_NUM_TAPS(val) | FIR_SELECT(dest) | FIR_START_CLK;
+
+ ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, fir_conf);
+
+ for (val = 0; val < ntaps; val++) {
+ ad9361_spi_write(spi, REG_TX_FILTER_COEF_ADDR + offs, val);
+ ad9361_spi_write(spi, REG_TX_FILTER_COEF_WRITE_DATA_1 + offs,
+ coef[val] & 0xFF);
+ ad9361_spi_write(spi, REG_TX_FILTER_COEF_WRITE_DATA_2 + offs,
+ coef[val] >> 8);
+ ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs,
+ fir_conf | FIR_WRITE);
+ ad9361_spi_write(spi, REG_TX_FILTER_COEF_READ_DATA_2 + offs, 0);
+ ad9361_spi_write(spi, REG_TX_FILTER_COEF_READ_DATA_2 + offs, 0);
+ }
+
+ ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, fir_conf);
+ fir_conf &= ~FIR_START_CLK;
+ ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, fir_conf);
+
+ if (dest & FIR_IS_RX)
+ ad9361_spi_writef(phy->spi, REG_RX_ENABLE_FILTER_CTRL,
+ RX_FIR_ENABLE_DECIMATION(~0), fir_enable);
+ else
+ ad9361_spi_writef(phy->spi, REG_TX_ENABLE_FILTER_CTRL,
+ TX_FIR_ENABLE_INTERPOLATION(~0), fir_enable);
+
+ ad9361_ensm_restore_prev_state(phy);
+
+ return ad9361_verify_fir_filter_coef(phy, dest, ntaps, coef);
+}
+
+#ifndef NUAND_MODIFICATIONS
+/**
+ * Parse the FIR filter file/buffer.
+ * @param phy The AD9361 state structure.
+ * @param data Pointer to buffer.
+ * @param size Buffer size.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_parse_fir(struct ad9361_rf_phy *phy,
+ char *data, uint32_t size)
+{
+ char *line;
+ int32_t i = 0, ret, txc, rxc;
+ int32_t tx = -1, tx_gain, tx_int;
+ int32_t rx = -1, rx_gain, rx_dec;
+ int32_t rtx = -1, rrx = -1;
+ int16_t coef_tx[128];
+ int16_t coef_rx[128];
+ char *ptr = data;
+
+ phy->filt_rx_bw_Hz = 0;
+ phy->filt_tx_bw_Hz = 0;
+ phy->filt_valid = false;
+
+ while ((line = strsep(&ptr, "\n"))) {
+ if (line >= data + size) {
+ break;
+ }
+
+ if (line[0] == '#')
+ continue;
+
+ if (tx < 0) {
+#ifdef WIN32
+ ret = sscanf_s(line, "TX %d GAIN %d INT %d",
+ &tx, &tx_gain, &tx_int);
+#else
+ ret = sscanf(line, "TX %"PRId32" GAIN %"PRId32" INT %"PRId32,
+ &tx, &tx_gain, &tx_int);
+#endif
+ if (ret == 3)
+ continue;
+ else
+ tx = -1;
+ }
+ if (rx < 0) {
+#ifdef WIN32
+ ret = sscanf_s(line, "RX %d GAIN %d DEC %d",
+ &rx, &rx_gain, &rx_dec);
+#else
+ ret = sscanf(line, "RX %"PRId32" GAIN %"PRId32" DEC %"PRId32,
+ &rx, &rx_gain, &rx_dec);
+#endif
+ if (ret == 3)
+ continue;
+ else
+ tx = -1;
+ }
+
+ if (rtx < 0) {
+#ifdef WIN32
+ ret = sscanf(line, "RTX %lu %lu %lu %lu %lu %lu",
+#else
+ ret = sscanf(line, "RTX %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32,
+#endif
+ &phy->filt_tx_path_clks[0],
+ &phy->filt_tx_path_clks[1],
+ &phy->filt_tx_path_clks[2],
+ &phy->filt_tx_path_clks[3],
+ &phy->filt_tx_path_clks[4],
+ &phy->filt_tx_path_clks[5]);
+ if (ret == 6) {
+ rtx = 0;
+ continue;
+ } else {
+ rtx = -1;
+ }
+ }
+
+ if (rrx < 0) {
+#ifdef WIN32
+ ret = sscanf(line, "RRX %lu %lu %lu %lu %lu %lu",
+#else
+ ret = sscanf(line, "RRX %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32,
+#endif
+ &phy->filt_rx_path_clks[0],
+ &phy->filt_rx_path_clks[1],
+ &phy->filt_rx_path_clks[2],
+ &phy->filt_rx_path_clks[3],
+ &phy->filt_rx_path_clks[4],
+ &phy->filt_rx_path_clks[5]);
+ if (ret == 6) {
+ rrx = 0;
+ continue;
+ } else {
+ rrx = -1;
+ }
+ }
+
+ if (!phy->filt_rx_bw_Hz) {
+#ifdef WIN32
+ ret = sscanf(line, "BWRX %d", &phy->filt_rx_bw_Hz);
+#else
+ ret = sscanf(line, "BWRX %"PRId32, &phy->filt_rx_bw_Hz);
+#endif
+ if (ret == 1)
+ continue;
+ else
+ phy->filt_rx_bw_Hz = 0;
+ }
+
+ if (!phy->filt_tx_bw_Hz) {
+#ifdef WIN32
+ ret = sscanf(line, "BWTX %d", &phy->filt_tx_bw_Hz);
+#else
+ ret = sscanf(line, "BWTX %"PRId32, &phy->filt_tx_bw_Hz);
+#endif
+ if (ret == 1)
+ continue;
+ else
+ phy->filt_tx_bw_Hz = 0;
+ }
+
+#ifdef WIN32
+ ret = sscanf_s(line, "%d,%d", &txc, &rxc);
+#else
+ ret = sscanf(line, "%"PRId32",%"PRId32, &txc, &rxc);
+#endif
+ if (ret == 1) {
+ coef_tx[i] = coef_rx[i] = (int16_t)txc;
+ i++;
+ continue;
+ }
+ else if (ret == 2) {
+ coef_tx[i] = (int16_t)txc;
+ coef_rx[i] = (int16_t)rxc;
+ i++;
+ continue;
+ }
+ }
+
+ switch (tx) {
+ case FIR_TX1:
+ case FIR_TX2:
+ case FIR_TX1_TX2:
+ phy->tx_fir_int = tx_int;
+ ret = ad9361_load_fir_filter_coef(phy, (enum fir_dest)tx, tx_gain, i, coef_tx);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ switch (rx | FIR_IS_RX) {
+ case FIR_RX1:
+ case FIR_RX2:
+ case FIR_RX1_RX2:
+ phy->rx_fir_dec = rx_dec;
+ ret = ad9361_load_fir_filter_coef(phy, (enum fir_dest)(rx | FIR_IS_RX),
+ rx_gain, i, coef_rx);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ if (!(rrx | rtx))
+ phy->filt_valid = true;
+
+ return size;
+}
+#endif // !NUAND_MODIFICATIONS
+
+/**
+ * Validate FIR filter configuration - on pass enable.
+ * @param phy The AD9361 state structure.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_validate_enable_fir(struct ad9361_rf_phy *phy)
+{
+ int32_t ret;
+ uint32_t rx[6], tx[6];
+ uint32_t max, min, valid;
+
+ dev_dbg(dev, "%s: TX FIR EN=%d/TAPS%d/INT%d, RX FIR EN=%d/TAPS%d/DEC%d",
+ __func__, !phy->bypass_tx_fir, phy->tx_fir_ntaps, phy->tx_fir_int,
+ !phy->bypass_rx_fir, phy->rx_fir_ntaps, phy->rx_fir_dec);
+
+ if (!phy->bypass_tx_fir) {
+ if (!(phy->tx_fir_int == 1 || phy->tx_fir_int == 2 ||
+ phy->tx_fir_int == 4)) {
+ dev_err(dev,
+ "%s: Invalid: Interpolation %d in filter config",
+ __func__, phy->tx_fir_int);
+ return -EINVAL;
+ }
+
+
+ if (phy->tx_fir_int == 1 && phy->tx_fir_ntaps > 64) {
+ dev_err(dev,
+ "%s: Invalid: TAPS > 64 and Interpolation = 1",
+ __func__);
+ return -EINVAL;
+ }
+ }
+
+ if (!phy->bypass_rx_fir) {
+ if (!(phy->rx_fir_dec == 1 || phy->rx_fir_dec == 2 ||
+ phy->rx_fir_dec == 4)) {
+ dev_err(dev,
+ "%s: Invalid: Decimation %d in filter config",
+ __func__, phy->rx_fir_dec);
+
+ return -EINVAL;
+ }
+ }
+
+ if (!phy->filt_valid || phy->bypass_rx_fir || phy->bypass_tx_fir) {
+ ret = ad9361_calculate_rf_clock_chain(phy,
+ clk_get_rate(phy, phy->ref_clk_scale[TX_SAMPL_CLK]),
+ phy->rate_governor, rx, tx);
+ if (ret < 0) {
+ min = phy->rate_governor ? 1500000U : 1000000U;
+ dev_err(dev,
+ "%s: Calculating filter rates failed %"PRId32
+ " using min frequency",__func__, ret);
+ ret = ad9361_calculate_rf_clock_chain(phy, min,
+ phy->rate_governor, rx, tx);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ valid = false;
+ } else {
+#ifndef ALTERA_PLATFORM
+ memcpy(rx, phy->filt_rx_path_clks, sizeof(rx));
+ memcpy(tx, phy->filt_tx_path_clks, sizeof(tx));
+#else
+ int32_t i;
+ uint32_t num;
+#ifdef NUAND_MODIFICATIONS
+ // fix memory overrun :x
+ num = sizeof(rx)/sizeof(rx[0]);
+#else
+ num = sizeof(rx);
+#endif // NUAND_MODIFICATIONS
+ for (i = 0; i < num; i++)
+ rx[i] = phy->filt_rx_path_clks[i];
+#ifdef NUAND_MODIFICATIONS
+ // fix memory overrun :x
+ num = sizeof(tx)/sizeof(tx[0]);
+#else
+ num = sizeof(tx);
+#endif // NUAND_MODIFICATIONS
+ for (i = 0; i < num; i++)
+ tx[i] = phy->filt_tx_path_clks[i];
+#endif
+ valid = true;
+
+ }
+
+#ifdef _DEBUG
+ dev_dbg(&phy->spi->dev, "%s:RX %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32,
+ __func__, rx[BBPLL_FREQ], rx[ADC_FREQ],
+ rx[R2_FREQ], rx[R1_FREQ],
+ rx[CLKRF_FREQ], rx[RX_SAMPL_FREQ]);
+
+ dev_dbg(&phy->spi->dev, "%s:TX %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32,
+ __func__, tx[BBPLL_FREQ], tx[ADC_FREQ],
+ tx[R2_FREQ], tx[R1_FREQ],
+ tx[CLKRF_FREQ], tx[RX_SAMPL_FREQ]);
+#endif
+
+ if (!phy->bypass_tx_fir) {
+ max = (tx[DAC_FREQ] / tx[TX_SAMPL_FREQ]) * 16;
+ if (phy->tx_fir_ntaps > max) {
+ dev_err(dev,
+ "%s: Invalid: ratio ADC/2 / TX_SAMPL * 16 > TAPS"
+ "(max %"PRIu32", adc %"PRIu32", tx %"PRIu32")",
+ __func__, max, rx[ADC_FREQ], tx[TX_SAMPL_FREQ]);
+ return -EINVAL;
+ }
+ }
+
+ if (!phy->bypass_rx_fir) {
+ max = ((rx[ADC_FREQ] / ((rx[ADC_FREQ] == rx[R2_FREQ]) ? 1 : 2)) /
+ rx[RX_SAMPL_FREQ]) * 16;
+ if (phy->rx_fir_ntaps > max) {
+ dev_err(dev,
+ "%s: Invalid: ratio ADC/2 / RX_SAMPL * 16 > TAPS (max %"PRIu32")",
+ __func__, max);
+ return -EINVAL;
+ }
+ }
+
+ ret = ad9361_set_trx_clock_chain(phy, rx, tx);
+ if (ret < 0)
+ return ret;
+
+ /* See also: ad9361_set_trx_clock_chain() */
+ if (!phy->pdata->dig_interface_tune_fir_disable &&
+ phy->bypass_tx_fir && phy->bypass_rx_fir)
+ ad9361_dig_tune(phy, 0, RESTORE_DEFAULT);
+
+ return ad9361_update_rf_bandwidth(phy,
+ valid ? phy->filt_rx_bw_Hz : phy->current_rx_bw_Hz,
+ valid ? phy->filt_tx_bw_Hz : phy->current_tx_bw_Hz);
+}
+
+/*
+* AD9361 Clocks
+*/
+
+/**
+* Set the multiplier and the divider for the selected refclk_scale structure.
+* @param priv The selected refclk_scale structure.
+* @param mul The multiplier value.
+* @param div The divider value.
+* @return 0 in case of success, negative error code otherwise.
+*/
+static inline int32_t ad9361_set_muldiv(struct refclk_scale *priv, uint32_t mul, uint32_t div)
+{
+ priv->mult = mul;
+ priv->div = div;
+ return 0;
+}
+
+/**
+ * Get the clk scaler for the selected refclk_scale structure.
+ * @param priv The selected refclk_scale structure.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_get_clk_scaler(struct refclk_scale *clk_priv)
+{
+ struct spi_device *spi = clk_priv->spi;
+ uint32_t tmp, tmp1;
+
+ switch (clk_priv->source) {
+ case BB_REFCLK:
+ tmp = ad9361_spi_read(spi, REG_CLOCK_CTRL);
+ tmp &= 0x3;
+ break;
+ case RX_REFCLK:
+ tmp = ad9361_spi_readf(spi, REG_REF_DIVIDE_CONFIG_1,
+ RX_REF_DIVIDER_MSB);
+ tmp1 = ad9361_spi_readf(spi, REG_REF_DIVIDE_CONFIG_2,
+ RX_REF_DIVIDER_LSB);
+ tmp = (tmp << 1) | tmp1;
+ break;
+ case TX_REFCLK:
+ tmp = ad9361_spi_readf(spi, REG_REF_DIVIDE_CONFIG_2,
+ TX_REF_DIVIDER(~0));
+ break;
+ case ADC_CLK:
+ tmp = ad9361_spi_read(spi, REG_BBPLL);
+ return ad9361_set_muldiv(clk_priv, 1, 1 << (tmp & 0x7));
+ case R2_CLK:
+ tmp = ad9361_spi_readf(spi, REG_RX_ENABLE_FILTER_CTRL,
+ DEC3_ENABLE_DECIMATION(~0));
+ return ad9361_set_muldiv(clk_priv, 1, tmp + 1);
+ case R1_CLK:
+ tmp = ad9361_spi_readf(spi, REG_RX_ENABLE_FILTER_CTRL, RHB2_EN);
+ return ad9361_set_muldiv(clk_priv, 1, tmp + 1);
+ case CLKRF_CLK:
+ tmp = ad9361_spi_readf(spi, REG_RX_ENABLE_FILTER_CTRL, RHB1_EN);
+ return ad9361_set_muldiv(clk_priv, 1, tmp + 1);
+ case RX_SAMPL_CLK:
+ tmp = ad9361_spi_readf(spi, REG_RX_ENABLE_FILTER_CTRL,
+ RX_FIR_ENABLE_DECIMATION(~0));
+
+ if (!tmp)
+ tmp = 1; /* bypass filter */
+ else
+ tmp = (1 << (tmp - 1));
+
+ return ad9361_set_muldiv(clk_priv, 1, tmp);
+ case DAC_CLK:
+ tmp = ad9361_spi_readf(spi, REG_BBPLL, BIT(3));
+ return ad9361_set_muldiv(clk_priv, 1, tmp + 1);
+ case T2_CLK:
+ tmp = ad9361_spi_readf(spi, REG_TX_ENABLE_FILTER_CTRL,
+ THB3_ENABLE_INTERP(~0));
+ return ad9361_set_muldiv(clk_priv, 1, tmp + 1);
+ case T1_CLK:
+ tmp = ad9361_spi_readf(spi, REG_TX_ENABLE_FILTER_CTRL, THB2_EN);
+ return ad9361_set_muldiv(clk_priv, 1, tmp + 1);
+ case CLKTF_CLK:
+ tmp = ad9361_spi_readf(spi, REG_TX_ENABLE_FILTER_CTRL, THB1_EN);
+ return ad9361_set_muldiv(clk_priv, 1, tmp + 1);
+ case TX_SAMPL_CLK:
+ tmp = ad9361_spi_readf(spi, REG_TX_ENABLE_FILTER_CTRL,
+ TX_FIR_ENABLE_INTERPOLATION(~0));
+
+ if (!tmp)
+ tmp = 1; /* bypass filter */
+ else
+ tmp = (1 << (tmp - 1));
+
+ return ad9361_set_muldiv(clk_priv, 1, tmp);
+ default:
+ return -EINVAL;
+ }
+
+ /* REFCLK Scaler */
+ switch (tmp) {
+ case 0:
+ ad9361_set_muldiv(clk_priv, 1, 1);
+ break;
+ case 1:
+ ad9361_set_muldiv(clk_priv, 1, 2);
+ break;
+ case 2:
+ ad9361_set_muldiv(clk_priv, 1, 4);
+ break;
+ case 3:
+ ad9361_set_muldiv(clk_priv, 2, 1);
+ break;
+ default:
+ return -EINVAL;
+
+ }
+
+ return 0;
+}
+
+/**
+ * Calculate the REFCLK Scaler for the selected refclk_scale structure.
+ * Note: REFCLK Scaler values - 00: x1; 01: x½; 10: x¼; 11: x2.
+ * @param clk_priv The selected refclk_scale structure.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_to_refclk_scaler(struct refclk_scale *clk_priv)
+{
+ /* REFCLK Scaler */
+ switch (((clk_priv->mult & 0xF) << 4) | (clk_priv->div & 0xF)) {
+ case 0x11:
+ return 0;
+ case 0x12:
+ return 1;
+ case 0x14:
+ return 2;
+ case 0x21:
+ return 3;
+ default:
+ return -EINVAL;
+ }
+};
+
+/**
+ * Set clk scaler for the selected refclk_scale structure.
+ * @param clk_priv The selected refclk_scale structure.
+ * @param set Set true, the reference clock frequency will be scaled before
+ * it enters the BBPLL.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_set_clk_scaler(struct refclk_scale *clk_priv, bool set)
+{
+ struct spi_device *spi = clk_priv->spi;
+ uint32_t tmp;
+ int32_t ret;
+
+ switch (clk_priv->source) {
+ case BB_REFCLK:
+ ret = ad9361_to_refclk_scaler(clk_priv);
+ if (ret < 0)
+ return ret;
+ if (set)
+ return ad9361_spi_writef(spi, REG_CLOCK_CTRL,
+ REF_FREQ_SCALER(~0), ret);
+ break;
+
+ case RX_REFCLK:
+ ret = ad9361_to_refclk_scaler(clk_priv);
+ if (ret < 0)
+ return ret;
+ if (set) {
+ tmp = ret;
+ ret = ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_1,
+ RX_REF_DIVIDER_MSB, tmp >> 1);
+ ret |= ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_2,
+ RX_REF_DIVIDER_LSB, tmp & 1);
+ return ret;
+ }
+ break;
+ case TX_REFCLK:
+ ret = ad9361_to_refclk_scaler(clk_priv);
+ if (ret < 0)
+ return ret;
+ if (set)
+ return ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_2,
+ TX_REF_DIVIDER(~0), ret);
+ break;
+ case ADC_CLK:
+ tmp = ilog2((uint8_t)clk_priv->div);
+ if (clk_priv->mult != 1 || tmp > 6 || tmp < 1)
+ return -EINVAL;
+
+ if (set)
+ return ad9361_spi_writef(spi, REG_BBPLL, 0x7, tmp);
+ break;
+ case R2_CLK:
+ if (clk_priv->mult != 1 || clk_priv->div > 3 || clk_priv->div < 1)
+ return -EINVAL;
+ if (set)
+ return ad9361_spi_writef(spi, REG_RX_ENABLE_FILTER_CTRL,
+ DEC3_ENABLE_DECIMATION(~0),
+ clk_priv->div - 1);
+ break;
+ case R1_CLK:
+ if (clk_priv->mult != 1 || clk_priv->div > 2 || clk_priv->div < 1)
+ return -EINVAL;
+ if (set)
+ return ad9361_spi_writef(spi, REG_RX_ENABLE_FILTER_CTRL,
+ RHB2_EN, clk_priv->div - 1);
+ break;
+ case CLKRF_CLK:
+ if (clk_priv->mult != 1 || clk_priv->div > 2 || clk_priv->div < 1)
+ return -EINVAL;
+ if (set)
+ return ad9361_spi_writef(spi, REG_RX_ENABLE_FILTER_CTRL,
+ RHB1_EN, clk_priv->div - 1);
+ break;
+ case RX_SAMPL_CLK:
+ if (clk_priv->mult != 1 || clk_priv->div > 4 ||
+ clk_priv->div < 1 || clk_priv->div == 3)
+ return -EINVAL;
+
+ if (clk_priv->phy->bypass_rx_fir)
+ tmp = 0;
+ else
+ tmp = ilog2(clk_priv->div) + 1;
+
+ if (set)
+ return ad9361_spi_writef(spi, REG_RX_ENABLE_FILTER_CTRL,
+ RX_FIR_ENABLE_DECIMATION(~0), tmp);
+ break;
+ case DAC_CLK:
+ if (clk_priv->mult != 1 || clk_priv->div > 2 || clk_priv->div < 1)
+ return -EINVAL;
+ if (set)
+ return ad9361_spi_writef(spi, REG_BBPLL,
+ BIT(3), clk_priv->div - 1);
+ break;
+ case T2_CLK:
+ if (clk_priv->mult != 1 || clk_priv->div > 3 || clk_priv->div < 1)
+ return -EINVAL;
+ if (set)
+ return ad9361_spi_writef(spi, REG_TX_ENABLE_FILTER_CTRL,
+ THB3_ENABLE_INTERP(~0),
+ clk_priv->div - 1);
+ break;
+ case T1_CLK:
+ if (clk_priv->mult != 1 || clk_priv->div > 2 || clk_priv->div < 1)
+ return -EINVAL;
+ if (set)
+ return ad9361_spi_writef(spi, REG_TX_ENABLE_FILTER_CTRL,
+ THB2_EN, clk_priv->div - 1);
+ break;
+ case CLKTF_CLK:
+ if (clk_priv->mult != 1 || clk_priv->div > 2 || clk_priv->div < 1)
+ return -EINVAL;
+ if (set)
+ return ad9361_spi_writef(spi, REG_TX_ENABLE_FILTER_CTRL,
+ THB1_EN, clk_priv->div - 1);
+ break;
+ case TX_SAMPL_CLK:
+ if (clk_priv->mult != 1 || clk_priv->div > 4 ||
+ clk_priv->div < 1 || clk_priv->div == 3)
+ return -EINVAL;
+
+ if (clk_priv->phy->bypass_tx_fir)
+ tmp = 0;
+ else
+ tmp = ilog2(clk_priv->div) + 1;
+
+ if (set)
+ return ad9361_spi_writef(spi, REG_TX_ENABLE_FILTER_CTRL,
+ TX_FIR_ENABLE_INTERPOLATION(~0), tmp);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * Recalculate the clock rate.
+ * @param refclk_scale The refclk_scale structure.
+ * @param parent_rate The parent clock rate.
+ * @return The clock rate.
+ */
+uint32_t ad9361_clk_factor_recalc_rate(struct refclk_scale *clk_priv,
+ uint32_t parent_rate)
+{
+ uint64_t rate;
+
+ ad9361_get_clk_scaler(clk_priv);
+ rate = (parent_rate * clk_priv->mult) / clk_priv->div;
+
+ return (uint32_t)rate;
+}
+
+/**
+ * Calculate the closest possible clock rate that can be set.
+ * @param refclk_scale The refclk_scale structure.
+ * @param rate The clock rate.
+ * @param parent_rate The parent clock rate.
+ * @return The closest possible clock rate that can be set.
+ */
+int32_t ad9361_clk_factor_round_rate(struct refclk_scale *clk_priv, uint32_t rate,
+ uint32_t *prate)
+{
+ int32_t ret;
+
+ if (rate >= *prate) {
+ clk_priv->mult = DIV_ROUND_CLOSEST(rate, *prate);
+ clk_priv->div = 1;
+
+ }
+ else {
+ clk_priv->div = DIV_ROUND_CLOSEST(*prate, rate);
+ clk_priv->mult = 1;
+ if (!clk_priv->div) {
+ dev_err(&clk_priv->spi->dev, "%s: divide by zero",
+ __func__);
+ clk_priv->div = 1;
+ }
+ }
+
+ ret = ad9361_set_clk_scaler(clk_priv, false);
+ if (ret < 0)
+ return ret;
+
+ return (*prate / clk_priv->div) * clk_priv->mult;
+}
+
+/**
+ * Set the clock rate.
+ * @param refclk_scale The refclk_scale structure.
+ * @param rate The clock rate.
+ * @param parent_rate The parent clock rate.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_clk_factor_set_rate(struct refclk_scale *clk_priv, uint32_t rate,
+ uint32_t parent_rate)
+{
+ dev_dbg(&clk_priv->spi->dev, "%s: Rate %"PRIu32" Hz Parent Rate %"PRIu32" Hz",
+ __func__, rate, parent_rate);
+
+ if (rate >= parent_rate) {
+ clk_priv->mult = DIV_ROUND_CLOSEST(rate, parent_rate);
+ clk_priv->div = 1;
+ }
+ else {
+ clk_priv->div = DIV_ROUND_CLOSEST(parent_rate, rate);
+ clk_priv->mult = 1;
+ if (!clk_priv->div) {
+ dev_err(&clk_priv->spi->dev, "%s: divide by zero",
+ __func__);
+ clk_priv->div = 1;
+ }
+ }
+
+ return ad9361_set_clk_scaler(clk_priv, true);
+}
+
+/*
+ * BBPLL
+ */
+/**
+ * Recalculate the clock rate.
+ * @param refclk_scale The refclk_scale structure.
+ * @param parent_rate The parent clock rate.
+ * @return The clock rate.
+ */
+uint32_t ad9361_bbpll_recalc_rate(struct refclk_scale *clk_priv,
+ uint32_t parent_rate)
+{
+ uint64_t rate;
+ uint32_t fract, integer;
+ uint8_t buf[4];
+
+ ad9361_spi_readm(clk_priv->spi, REG_INTEGER_BB_FREQ_WORD, &buf[0],
+ REG_INTEGER_BB_FREQ_WORD - REG_FRACT_BB_FREQ_WORD_1 + 1);
+
+ fract = (buf[3] << 16) | (buf[2] << 8) | buf[1];
+ integer = buf[0];
+
+ rate = ((uint64_t)parent_rate * fract);
+ do_div(&rate, BBPLL_MODULUS);
+ rate += (uint64_t)parent_rate * integer;
+
+ return (uint32_t)rate;
+}
+
+/**
+ * Calculate the closest possible clock rate that can be set.
+ * @param refclk_scale The refclk_scale structure.
+ * @param rate The clock rate.
+ * @param parent_rate The parent clock rate.
+ * @return The closest possible clock rate that can be set.
+ */
+int32_t ad9361_bbpll_round_rate(struct refclk_scale *clk_priv, uint32_t rate,
+ uint32_t *prate)
+{
+ uint64_t tmp;
+ uint32_t fract, integer;
+ uint64_t temp;
+
+ if (clk_priv) {
+ // Unused variable - fix compiler warning
+ }
+
+ if (rate > MAX_BBPLL_FREQ)
+ return MAX_BBPLL_FREQ;
+
+ if (rate < MIN_BBPLL_FREQ)
+ return MIN_BBPLL_FREQ;
+
+ temp = rate;
+ tmp = do_div(&temp, *prate);
+ rate = temp;
+ tmp = tmp * BBPLL_MODULUS + (*prate >> 1);
+ do_div(&tmp, *prate);
+
+ integer = rate;
+ fract = tmp;
+
+ tmp = *prate * (uint64_t)fract;
+ do_div(&tmp, BBPLL_MODULUS);
+ tmp += *prate * integer;
+
+ return tmp;
+}
+
+/**
+ * Set the clock rate.
+ * @param refclk_scale The refclk_scale structure.
+ * @param rate The clock rate.
+ * @param parent_rate The parent clock rate.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_bbpll_set_rate(struct refclk_scale *clk_priv, uint32_t rate,
+ uint32_t parent_rate)
+{
+ struct spi_device *spi = clk_priv->spi;
+ uint64_t tmp;
+ uint32_t fract, integer;
+ int32_t icp_val;
+ uint8_t lf_defaults[3] = { 0x35, 0x5B, 0xE8 };
+ uint64_t temp;
+
+ dev_dbg(&spi->dev, "%s: Rate %"PRIu32" Hz Parent Rate %"PRIu32" Hz",
+ __func__, rate, parent_rate);
+
+ /*
+ * Setup Loop Filter and CP Current
+ * Scale is 150uA @ (1280MHz BBPLL, 40MHz REFCLK)
+ */
+ tmp = (rate >> 7) * 150ULL;
+ do_div(&tmp, (parent_rate >> 7) * 32UL);
+
+ /* 25uA/LSB, Offset 25uA */
+ icp_val = DIV_ROUND_CLOSEST((uint32_t)tmp, 25U) - 1;
+
+ icp_val = clamp(icp_val, 1, 64);
+
+ ad9361_spi_write(spi, REG_CP_CURRENT, icp_val);
+ ad9361_spi_writem(spi, REG_LOOP_FILTER_3, lf_defaults,
+ ARRAY_SIZE(lf_defaults));
+
+ /* Allow calibration to occur and set cal count to 1024 for max accuracy */
+ ad9361_spi_write(spi, REG_VCO_CTRL,
+ FREQ_CAL_ENABLE | FREQ_CAL_COUNT_LENGTH(3));
+ /* Set calibration clock to REFCLK/4 for more accuracy */
+ ad9361_spi_write(spi, REG_SDM_CTRL, 0x10);
+
+ /* Calculate and set BBPLL frequency word */
+ temp = rate;
+ tmp = do_div(&temp, parent_rate);
+ rate = temp;
+ tmp = tmp *(uint64_t)BBPLL_MODULUS + (parent_rate >> 1);
+ do_div(&tmp, parent_rate);
+
+ integer = rate;
+ fract = tmp;
+
+ ad9361_spi_write(spi, REG_INTEGER_BB_FREQ_WORD, integer);
+ ad9361_spi_write(spi, REG_FRACT_BB_FREQ_WORD_3, fract);
+ ad9361_spi_write(spi, REG_FRACT_BB_FREQ_WORD_2, fract >> 8);
+ ad9361_spi_write(spi, REG_FRACT_BB_FREQ_WORD_1, fract >> 16);
+
+ ad9361_spi_write(spi, REG_SDM_CTRL_1, INIT_BB_FO_CAL | BBPLL_RESET_BAR); /* Start BBPLL Calibration */
+ ad9361_spi_write(spi, REG_SDM_CTRL_1, BBPLL_RESET_BAR); /* Clear BBPLL start calibration bit */
+
+ ad9361_spi_write(spi, REG_VCO_PROGRAM_1, 0x86); /* Increase BBPLL KV and phase margin */
+ ad9361_spi_write(spi, REG_VCO_PROGRAM_2, 0x01); /* Increase BBPLL KV and phase margin */
+ ad9361_spi_write(spi, REG_VCO_PROGRAM_2, 0x05); /* Increase BBPLL KV and phase margin */
+
+ return ad9361_check_cal_done(clk_priv->phy, REG_CH_1_OVERFLOW,
+ BBPLL_LOCK, 1);
+}
+
+/*
+ * RFPLL
+ */
+
+/**
+ * Calculate the RFPLL frequency.
+ * @param parent_rate The parent clock rate.
+ * @param integer The integer value.
+ * @param fract The fractional value.
+ * @param vco_div The VCO divider.
+ * @return The RFPLL frequency.
+ */
+static uint64_t ad9361_calc_rfpll_int_freq(uint64_t parent_rate,
+ uint64_t integer,
+ uint64_t fract, uint32_t vco_div)
+{
+ uint64_t rate;
+
+ rate = parent_rate * fract;
+ do_div(&rate, RFPLL_MODULUS);
+ rate += parent_rate * integer;
+
+ return rate >> (vco_div + 1);
+}
+
+/**
+ * Calculate the RFPLL dividers.
+ * @param freq The RFPLL frequency.
+ * @param parent_rate The parent clock rate.
+ * @param integer The integer value.
+ * @param fract The fractional value.
+ * @param vco_div The VCO divider.
+ * @param vco_freq The VCO frequency.
+ * @return The RFPLL frequency.
+ */
+static int32_t ad9361_calc_rfpll_int_divder(struct ad9361_rf_phy *phy,
+ uint64_t freq, uint64_t parent_rate, uint32_t *integer,
+ uint32_t *fract, int32_t *vco_div, uint64_t *vco_freq)
+{
+ uint64_t tmp;
+ int32_t div, ret;
+
+ ret = ad9361_validate_rfpll(phy, freq);
+ if (ret)
+ return ret;
+
+ div = -1;
+
+ while (freq <= MIN_VCO_FREQ_HZ) {
+ freq <<= 1;
+ div++;
+ }
+
+ *vco_div = div;
+ *vco_freq = freq;
+ tmp = do_div(&freq, parent_rate);
+ tmp = tmp * RFPLL_MODULUS + (parent_rate >> 1);
+ do_div(&tmp, parent_rate);
+ *integer = freq;
+ *fract = tmp;
+
+ return 0;
+}
+
+/**
+ * Recalculate the clock rate.
+ * @param refclk_scale The refclk_scale structure.
+ * @param parent_rate The parent clock rate.
+ * @return The clock rate.
+ */
+uint32_t ad9361_rfpll_int_recalc_rate(struct refclk_scale *clk_priv,
+ uint32_t parent_rate)
+{
+ struct ad9361_rf_phy *phy = clk_priv->phy;
+ uint32_t fract, integer;
+ uint8_t buf[5];
+ uint32_t reg, div_mask, vco_div, profile;
+
+ dev_dbg(&clk_priv->spi->dev, "%s: Parent Rate %"PRIu32" Hz",
+ __func__, parent_rate);
+
+ switch (clk_priv->source) {
+ case RX_RFPLL_INT:
+ reg = REG_RX_FRACT_BYTE_2;
+ div_mask = RX_VCO_DIVIDER(~0);
+ profile = phy->fastlock.current_profile[0];
+ break;
+ case TX_RFPLL_INT:
+ reg = REG_TX_FRACT_BYTE_2;
+ div_mask = TX_VCO_DIVIDER(~0);
+ profile = phy->fastlock.current_profile[1];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (profile) {
+ bool tx = clk_priv->source == TX_RFPLL_INT;
+ profile = profile - 1;
+
+ buf[0] = ad9361_fastlock_readval(phy->spi, tx, profile, 4);
+ buf[1] = ad9361_fastlock_readval(phy->spi, tx, profile, 3);
+ buf[2] = ad9361_fastlock_readval(phy->spi, tx, profile, 2);
+ buf[3] = ad9361_fastlock_readval(phy->spi, tx, profile, 1);
+ buf[4] = ad9361_fastlock_readval(phy->spi, tx, profile, 0);
+ vco_div = ad9361_fastlock_readval(phy->spi, tx, profile, 12) & 0xF;
+
+ }
+ else {
+ ad9361_spi_readm(clk_priv->spi, reg, &buf[0], ARRAY_SIZE(buf));
+ vco_div = ad9361_spi_readf(clk_priv->spi, REG_RFPLL_DIVIDERS, div_mask);
+ }
+
+ fract = (SYNTH_FRACT_WORD(buf[0]) << 16) | (buf[1] << 8) | buf[2];
+ integer = (SYNTH_INTEGER_WORD(buf[3]) << 8) | buf[4];
+
+ return ad9361_to_clk(ad9361_calc_rfpll_int_freq(parent_rate, integer,
+ fract, vco_div));
+}
+
+/**
+ * Calculate the closest possible clock rate that can be set.
+ * @param refclk_scale The refclk_scale structure.
+ * @param rate The clock rate.
+ * @param parent_rate The parent clock rate.
+ * @return The closest possible clock rate that can be set.
+ */
+int32_t ad9361_rfpll_int_round_rate(struct refclk_scale *clk_priv, uint32_t rate,
+ uint32_t *prate)
+{
+ dev_dbg(&clk_priv->spi->dev, "%s: Rate %"PRIu32" Hz", __func__, rate);
+
+ if (prate) {
+ // Unused variable - fix compiler warning
+ }
+
+ if (clk_priv) {
+ // Unused variable - fix compiler warning
+ }
+
+ if (ad9361_from_clk(rate) > MAX_CARRIER_FREQ_HZ ||
+ ad9361_from_clk(rate) < MIN_CARRIER_FREQ_HZ)
+ return -EINVAL;
+
+ return rate;
+}
+
+/**
+ * Set the clock rate.
+ * @param refclk_scale The refclk_scale structure.
+ * @param rate The clock rate.
+ * @param parent_rate The parent clock rate.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_rfpll_int_set_rate(struct refclk_scale *clk_priv, uint32_t rate,
+ uint32_t parent_rate)
+{
+ struct ad9361_rf_phy *phy = clk_priv->phy;
+ uint64_t vco = 0;
+ uint8_t buf[5];
+ uint32_t reg, div_mask, lock_reg, fract = 0, integer = 0;
+ int32_t vco_div, ret, fixup_other;
+
+ dev_dbg(&clk_priv->spi->dev, "%s: %s Rate %"PRIu32" Hz Parent Rate %"PRIu32" Hz",
+ __func__, clk_priv->source == TX_RFPLL_INT ? "TX" : "RX",
+ rate, parent_rate);
+
+ ad9361_fastlock_prepare(phy, clk_priv->source == TX_RFPLL_INT, 0, false);
+
+ ret = ad9361_calc_rfpll_int_divder(phy, ad9361_from_clk(rate), parent_rate,
+ &integer, &fract, &vco_div, &vco);
+ if (ret < 0)
+ return ret;
+
+ switch (clk_priv->source) {
+ case RX_RFPLL_INT:
+ reg = REG_RX_FRACT_BYTE_2;
+ lock_reg = REG_RX_CP_OVERRANGE_VCO_LOCK;
+ div_mask = RX_VCO_DIVIDER(~0);
+ phy->cached_rx_rfpll_div = vco_div;
+ phy->current_rx_lo_freq = rate;
+ break;
+ case TX_RFPLL_INT:
+ reg = REG_TX_FRACT_BYTE_2;
+ lock_reg = REG_TX_CP_OVERRANGE_VCO_LOCK;
+ div_mask = TX_VCO_DIVIDER(~0);
+ phy->cached_tx_rfpll_div = vco_div;
+ phy->current_tx_lo_freq = rate;
+ break;
+ default:
+ return -EINVAL;
+
+ }
+
+ /* Option to skip VCO cal in TDD mode when moving from TX/RX to Alert */
+ if (phy->pdata->tdd_skip_vco_cal)
+ ad9361_trx_vco_cal_control(phy, clk_priv->source == TX_RFPLL_INT,
+ true);
+
+ do {
+ fixup_other = 0;
+ ad9361_rfpll_vco_init(phy, div_mask == TX_VCO_DIVIDER(~0),
+ vco, parent_rate);
+
+ buf[0] = SYNTH_FRACT_WORD(fract >> 16);
+ buf[1] = fract >> 8;
+ buf[2] = fract & 0xFF;
+ buf[3] = SYNTH_INTEGER_WORD(integer >> 8) |
+ (~SYNTH_INTEGER_WORD(~0) &
+ ad9361_spi_read(clk_priv->spi, reg - 3));
+ buf[4] = integer & 0xFF;
+
+ ad9361_spi_writem(clk_priv->spi, reg, buf, 5);
+ ad9361_spi_writef(clk_priv->spi, REG_RFPLL_DIVIDERS, div_mask, vco_div);
+
+ ret = ad9361_check_cal_done(phy, lock_reg, VCO_LOCK, 1);
+
+ /* In FDD mode with RX LO == TX LO frequency we use TDD tables to
+ * reduce VCO pulling
+ */
+
+ if (((phy->pdata->fdd && !phy->pdata->fdd_independent_mode) &&
+ (phy->current_tx_lo_freq == phy->current_rx_lo_freq) &&
+ (phy->current_tx_use_tdd_table != phy->current_rx_use_tdd_table)) ||
+ ((phy->pdata->fdd && !phy->pdata->fdd_independent_mode) &&
+ (phy->current_tx_lo_freq != phy->current_rx_lo_freq) &&
+ (phy->current_tx_use_tdd_table || phy->current_rx_use_tdd_table))) {
+ unsigned long _rate;
+
+ switch (clk_priv->source) {
+ case RX_RFPLL_INT:
+ reg = REG_TX_FRACT_BYTE_2;
+ lock_reg = REG_TX_CP_OVERRANGE_VCO_LOCK;
+ div_mask = TX_VCO_DIVIDER(~0);
+ _rate = phy->current_tx_lo_freq;
+ break;
+ case TX_RFPLL_INT:
+ reg = REG_RX_FRACT_BYTE_2;
+ lock_reg = REG_RX_CP_OVERRANGE_VCO_LOCK;
+ div_mask = RX_VCO_DIVIDER(~0);
+ _rate = phy->current_rx_lo_freq;
+ break;
+ default:
+ return -EINVAL;
+
+ }
+
+ if (phy->current_tx_lo_freq != phy->current_rx_lo_freq) {
+ ad9361_calc_rfpll_int_divder(phy, ad9361_from_clk(_rate),
+ parent_rate, &integer, &fract, &vco_div, &vco);
+
+ ad9361_fastlock_prepare(phy, clk_priv->source == RX_RFPLL_INT, 0, false);
+ }
+
+ fixup_other = 1;
+ }
+
+ } while (fixup_other);
+
+ if (phy->pdata->tdd_skip_vco_cal)
+ ad9361_trx_vco_cal_control(phy, clk_priv->source == TX_RFPLL_INT,
+ false);
+
+ return ret;
+}
+
+/**
+ * Recalculate the clock rate.
+ * @param refclk_scale The refclk_scale structure.
+ * @param parent_rate The parent clock rate.
+ * @return The clock rate.
+ */
+uint32_t ad9361_rfpll_dummy_recalc_rate(struct refclk_scale *clk_priv)
+{
+ struct ad9361_rf_phy *phy = clk_priv->phy;
+
+ return phy->clks[clk_priv->source]->rate;
+}
+
+/**
+ * Set the clock rate.
+ * @param refclk_scale The refclk_scale structure.
+ * @param rate The clock rate.
+ * @param parent_rate The parent clock rate.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_rfpll_dummy_set_rate(struct refclk_scale *clk_priv, uint32_t rate)
+{
+ struct ad9361_rf_phy *phy = clk_priv->phy;
+
+ phy->clks[clk_priv->source]->rate = rate;
+
+ return 0;
+}
+
+/**
+ * Recalculate the clock rate.
+ * @param refclk_scale The refclk_scale structure.
+ * @param parent_rate The parent clock rate.
+ * @return The clock rate.
+ */
+uint32_t ad9361_rfpll_recalc_rate(struct refclk_scale *clk_priv)
+{
+ struct ad9361_rf_phy *phy = clk_priv->phy;
+ uint32_t rate;
+
+ switch (clk_priv->source) {
+ case RX_RFPLL:
+ if (phy->pdata->use_ext_rx_lo) {
+ if (phy->ad9361_rfpll_ext_recalc_rate)
+ rate = phy->ad9361_rfpll_ext_recalc_rate(clk_priv);
+ else
+ rate = ad9361_rfpll_dummy_recalc_rate(phy->ref_clk_scale[RX_RFPLL_DUMMY]);
+ } else {
+ rate = ad9361_rfpll_int_recalc_rate(phy->ref_clk_scale[RX_RFPLL_INT],
+ phy->clks[RX_REFCLK]->rate);
+ }
+ break;
+ case TX_RFPLL:
+ if (phy->pdata->use_ext_tx_lo) {
+ if (phy->ad9361_rfpll_ext_recalc_rate)
+ rate = phy->ad9361_rfpll_ext_recalc_rate(clk_priv);
+ else
+ rate = ad9361_rfpll_dummy_recalc_rate(phy->ref_clk_scale[TX_RFPLL_DUMMY]);
+ } else {
+ rate = ad9361_rfpll_int_recalc_rate(phy->ref_clk_scale[TX_RFPLL_INT],
+ phy->clks[TX_REFCLK]->rate);
+ }
+ break;
+ default:
+ rate = 0;
+ break;
+ }
+
+ return rate;
+}
+
+/**
+ * Calculate the closest possible clock rate that can be set.
+ * @param refclk_scale The refclk_scale structure.
+ * @param rate The clock rate.
+ * @param parent_rate The parent clock rate.
+ * @return The closest possible clock rate that can be set.
+ */
+int32_t ad9361_rfpll_round_rate(struct refclk_scale *clk_priv, uint32_t rate)
+{
+ struct ad9361_rf_phy *phy = clk_priv->phy;
+ int32_t round_rate;
+
+ switch (clk_priv->source) {
+ case RX_RFPLL:
+ if (phy->pdata->use_ext_rx_lo) {
+ if (phy->ad9361_rfpll_ext_round_rate)
+ round_rate = phy->ad9361_rfpll_ext_round_rate(clk_priv, rate);
+ else
+ round_rate = rate;
+ } else {
+ round_rate = ad9361_rfpll_int_round_rate(phy->ref_clk_scale[RX_RFPLL_INT], rate,
+ &phy->clks[phy->ref_clk_scale[RX_RFPLL_INT]->parent_source]->rate);
+ }
+ case TX_RFPLL:
+ if (phy->pdata->use_ext_tx_lo) {
+ if (phy->ad9361_rfpll_ext_round_rate)
+ round_rate = phy->ad9361_rfpll_ext_round_rate(clk_priv, rate);
+ else
+ round_rate = rate;
+ } else {
+ round_rate = ad9361_rfpll_int_round_rate(phy->ref_clk_scale[TX_RFPLL_INT], rate,
+ &phy->clks[phy->ref_clk_scale[TX_RFPLL_INT]->parent_source]->rate);
+ }
+ break;
+ default:
+ round_rate = 0;
+ break;
+ }
+
+ return round_rate;
+}
+
+/**
+ * Set the clock rate.
+ * @param refclk_scale The refclk_scale structure.
+ * @param rate The clock rate.
+ * @param parent_rate The parent clock rate.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_rfpll_set_rate(struct refclk_scale *clk_priv, uint32_t rate)
+{
+ struct ad9361_rf_phy *phy = clk_priv->phy;
+ int32_t ret;
+
+ switch (clk_priv->source) {
+ case RX_RFPLL:
+ if (phy->pdata->use_ext_rx_lo) {
+ if (phy->ad9361_rfpll_ext_set_rate)
+ phy->ad9361_rfpll_ext_set_rate(clk_priv, rate);
+ else
+ ad9361_rfpll_dummy_set_rate(phy->ref_clk_scale[RX_RFPLL_DUMMY], rate);
+ } else {
+ ad9361_rfpll_int_set_rate(phy->ref_clk_scale[RX_RFPLL_INT], rate,
+ phy->clks[phy->ref_clk_scale[RX_RFPLL_INT]->parent_source]->rate);
+ }
+ /* Load Gain Table */
+ ret = ad9361_load_gt(phy, ad9361_from_clk(rate), GT_RX1 + GT_RX2);
+ if (ret < 0)
+ return ret;
+ break;
+ case TX_RFPLL:
+ if (phy->pdata->use_ext_tx_lo) {
+ if (phy->ad9361_rfpll_ext_set_rate)
+ phy->ad9361_rfpll_ext_set_rate(clk_priv, rate);
+ else
+ ad9361_rfpll_dummy_set_rate(phy->ref_clk_scale[TX_RFPLL_DUMMY], rate);
+ } else {
+ ad9361_rfpll_int_set_rate(phy->ref_clk_scale[TX_RFPLL_INT], rate,
+ phy->clks[phy->ref_clk_scale[TX_RFPLL_INT]->parent_source]->rate);
+ }
+ /* For RX LO we typically have the tracking option enabled
+ * so for now do nothing here.
+ */
+ if (phy->auto_cal_en && (clk_priv->source == TX_RFPLL_INT))
+ if (abs((int64_t)(phy->last_tx_quad_cal_freq - ad9361_from_clk(rate))) >
+ (int64_t)phy->cal_threshold_freq) {
+ ret = ad9361_do_calib_run(phy, TX_QUAD_CAL, -1);
+ if (ret < 0)
+ dev_err(&phy->spi->dev,
+ "%s: TX QUAD cal failed", __func__);
+ phy->last_tx_quad_cal_freq = ad9361_from_clk(rate);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * Set clock mux parent.
+ * @param refclk_scale The refclk_scale structure.
+ * @param index Index - Enable (1), disable (0) ext lo.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_clk_mux_set_parent(struct refclk_scale *clk_priv, uint8_t index)
+{
+ struct ad9361_rf_phy *phy = clk_priv->phy;
+ int32_t ret;
+
+ dev_dbg(&clk_priv->spi->dev, "%s: index %d", __func__, index);
+
+ ad9361_ensm_force_state(phy, ENSM_STATE_ALERT);
+
+ ret = ad9361_trx_ext_lo_control(phy, clk_priv->source == TX_RFPLL, index == 1);
+ if (ret >= 0)
+ clk_priv->mult = index;
+
+ ad9361_ensm_restore_prev_state(phy);
+
+ return ret;
+}
+
+/**
+ * Register and initialize a new clock.
+ * @param phy The AD9361 state structure.
+ * @param name The name of the new clock.
+ * @param parent_name The name of the parent clock.
+ * @param flags The flags.
+ * @param source The source of the new clock.
+ * @param parent_source The source of the parent clock.
+ * @return A struct clk for the new clock or a negative error code.
+ */
+static struct clk *ad9361_clk_register(struct ad9361_rf_phy *phy, const char *name,
+ const char *parent_name, uint32_t flags,
+ uint32_t source, uint32_t parent_source)
+{
+ struct refclk_scale *clk_priv;
+ struct clk *clk;
+
+ if (name) {
+ // Unused variable - fix compiler warning
+ }
+ if (parent_name) {
+ // Unused variable - fix compiler warning
+ }
+ if (flags) {
+ // Unused variable - fix compiler warning
+ }
+
+ clk_priv = (struct refclk_scale *)malloc(sizeof(*clk_priv));
+ if (!clk_priv) {
+ dev_err(&phy->spi->dev, "ad9361_clk_register: could not allocate fixed factor clk");
+ return (struct clk *)ERR_PTR(-ENOMEM);
+ }
+
+ /* struct refclk_scale assignments */
+ clk_priv->source = (enum ad9361_clocks)source;
+ clk_priv->parent_source = (enum ad9361_clocks)parent_source;
+ clk_priv->spi = phy->spi;
+ clk_priv->phy = phy;
+
+ phy->ref_clk_scale[source] = clk_priv;
+
+ clk = (struct clk *)malloc(sizeof(*clk));
+ if (!clk) {
+ free(clk_priv);
+ return (struct clk *)ERR_PTR(-ENOMEM);
+ }
+
+ switch (source) {
+ case TX_REFCLK:
+ clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clk_refin->rate);
+ break;
+ case RX_REFCLK:
+ clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clk_refin->rate);
+ break;
+ case BB_REFCLK:
+ clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clk_refin->rate);
+ break;
+ case BBPLL_CLK:
+ clk->rate = ad9361_bbpll_recalc_rate(clk_priv, phy->clks[BB_REFCLK]->rate);
+ break;
+ case ADC_CLK:
+ clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[BBPLL_CLK]->rate);
+ break;
+ case R2_CLK:
+ clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[ADC_CLK]->rate);
+ break;
+ case R1_CLK:
+ clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[R2_CLK]->rate);
+ break;
+ case CLKRF_CLK:
+ clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[R1_CLK]->rate);
+ break;
+ case RX_SAMPL_CLK:
+ clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[CLKRF_CLK]->rate);
+ break;
+ case DAC_CLK:
+ clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[ADC_CLK]->rate);
+ break;
+ case T2_CLK:
+ clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[DAC_CLK]->rate);
+ break;
+ case T1_CLK:
+ clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[T2_CLK]->rate);
+ break;
+ case CLKTF_CLK:
+ clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[T1_CLK]->rate);
+ break;
+ case TX_SAMPL_CLK:
+ clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[CLKTF_CLK]->rate);
+ break;
+ case RX_RFPLL_INT:
+ clk->rate = ad9361_rfpll_int_recalc_rate(clk_priv, phy->clks[RX_REFCLK]->rate);
+ break;
+ case TX_RFPLL_INT:
+ clk->rate = ad9361_rfpll_int_recalc_rate(clk_priv, phy->clks[TX_REFCLK]->rate);
+ break;
+ case RX_RFPLL_DUMMY:
+ clk->rate = phy->pdata->rx_synth_freq;
+ break;
+ case TX_RFPLL_DUMMY:
+ clk->rate = phy->pdata->tx_synth_freq;
+ break;
+ case RX_RFPLL:
+ clk->rate = ad9361_rfpll_recalc_rate(clk_priv);
+ break;
+ case TX_RFPLL:
+ clk->rate = ad9361_rfpll_recalc_rate(clk_priv);
+ }
+
+ return clk;
+}
+
+/**
+ * Register and initialize all the system clocks.
+ * @param phy The AD9361 state structure.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t register_clocks(struct ad9361_rf_phy *phy)
+{
+ uint32_t flags = CLK_GET_RATE_NOCACHE;
+
+ phy->clk_data.clks = (struct clk **)malloc(sizeof(*phy->clk_data.clks) *
+ NUM_AD9361_CLKS);
+ if (!phy->clk_data.clks) {
+ dev_err(&phy->spi->dev, "could not allocate memory");
+ return -ENOMEM;
+ }
+
+ phy->clk_data.clk_num = NUM_AD9361_CLKS;
+
+ /* Scaled Reference Clocks */
+ phy->clks[TX_REFCLK] = ad9361_clk_register(phy,
+ "tx_refclk", "ad9361_ext_refclk",
+ flags | CLK_IGNORE_UNUSED,
+ TX_REFCLK, EXT_REF_CLK);
+
+ phy->clks[RX_REFCLK] = ad9361_clk_register(phy,
+ "rx_refclk", "ad9361_ext_refclk",
+ flags | CLK_IGNORE_UNUSED,
+ RX_REFCLK, EXT_REF_CLK);
+
+ phy->clks[BB_REFCLK] = ad9361_clk_register(phy,
+ "bb_refclk", "ad9361_ext_refclk",
+ flags | CLK_IGNORE_UNUSED,
+ BB_REFCLK, EXT_REF_CLK);
+
+ /* Base Band PLL Clock */
+ phy->clks[BBPLL_CLK] = ad9361_clk_register(phy,
+ "bbpll_clk", "bb_refclk",
+ flags | CLK_IGNORE_UNUSED,
+ BBPLL_CLK, BB_REFCLK);
+
+ phy->clks[ADC_CLK] = ad9361_clk_register(phy,
+ "adc_clk", "bbpll_clk",
+ flags | CLK_IGNORE_UNUSED,
+ ADC_CLK, BBPLL_CLK);
+
+ phy->clks[R2_CLK] = ad9361_clk_register(phy,
+ "r2_clk", "adc_clk",
+ flags | CLK_IGNORE_UNUSED,
+ R2_CLK, ADC_CLK);
+
+ phy->clks[R1_CLK] = ad9361_clk_register(phy,
+ "r1_clk", "r2_clk",
+ flags | CLK_IGNORE_UNUSED,
+ R1_CLK, R2_CLK);
+
+ phy->clks[CLKRF_CLK] = ad9361_clk_register(phy,
+ "clkrf_clk", "r1_clk",
+ flags | CLK_IGNORE_UNUSED,
+ CLKRF_CLK, R1_CLK);
+
+ phy->clks[RX_SAMPL_CLK] = ad9361_clk_register(phy,
+ "rx_sampl_clk", "clkrf_clk",
+ flags | CLK_IGNORE_UNUSED,
+ RX_SAMPL_CLK, CLKRF_CLK);
+
+
+ phy->clks[DAC_CLK] = ad9361_clk_register(phy,
+ "dac_clk", "adc_clk",
+ flags | CLK_IGNORE_UNUSED,
+ DAC_CLK, ADC_CLK);
+
+ phy->clks[T2_CLK] = ad9361_clk_register(phy,
+ "t2_clk", "dac_clk",
+ flags | CLK_IGNORE_UNUSED,
+ T2_CLK, DAC_CLK);
+
+ phy->clks[T1_CLK] = ad9361_clk_register(phy,
+ "t1_clk", "t2_clk",
+ flags | CLK_IGNORE_UNUSED,
+ T1_CLK, T2_CLK);
+
+ phy->clks[CLKTF_CLK] = ad9361_clk_register(phy,
+ "clktf_clk", "t1_clk",
+ flags | CLK_IGNORE_UNUSED,
+ CLKTF_CLK, T1_CLK);
+
+ phy->clks[TX_SAMPL_CLK] = ad9361_clk_register(phy,
+ "tx_sampl_clk", "clktf_clk",
+ flags | CLK_IGNORE_UNUSED,
+ TX_SAMPL_CLK, CLKTF_CLK);
+
+ phy->clks[RX_RFPLL_INT] = ad9361_clk_register(phy,
+ "rx_rfpll", "rx_refclk",
+ flags | CLK_IGNORE_UNUSED,
+ RX_RFPLL_INT, RX_REFCLK);
+
+ phy->clks[TX_RFPLL_INT] = ad9361_clk_register(phy,
+ "tx_rfpll", "tx_refclk",
+ flags | CLK_IGNORE_UNUSED,
+ TX_RFPLL_INT, TX_REFCLK);
+
+ phy->clks[RX_RFPLL_DUMMY] = ad9361_clk_register(phy,
+ "rx_rfpll_dummy", NULL,
+ flags | CLK_IGNORE_UNUSED,
+ RX_RFPLL_DUMMY, 0);
+
+ phy->clks[TX_RFPLL_DUMMY] = ad9361_clk_register(phy,
+ "tx_rfpll_dummy", NULL,
+ flags | CLK_IGNORE_UNUSED,
+ TX_RFPLL_DUMMY, 0);
+
+ phy->clks[RX_RFPLL] = ad9361_clk_register(phy,
+ "rx_rfpll", NULL,
+ flags | CLK_IGNORE_UNUSED,
+ RX_RFPLL, 0);
+
+ phy->clks[TX_RFPLL] = ad9361_clk_register(phy,
+ "tx_rfpll", NULL,
+ flags | CLK_IGNORE_UNUSED,
+ TX_RFPLL, 0);
+
+ return 0;
+}
+
+/**
+ * Perform an RSSI gain step calibration.
+ * Note: Before running the function, provide a single tone within the channel
+ * bandwidth and monitor the received data. Adjust the tone amplitude until
+ * the received data is within a few dB of full scale but not overloading.
+ * @param phy The AD9361 state structure.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_rssi_gain_step_calib(struct ad9361_rf_phy *phy)
+{
+ uint32_t lna_error[4];
+ uint32_t mixer_error[15];
+ uint64_t lo_freq_hz;
+ uint8_t lo_index;
+ uint8_t i;
+
+ lo_freq_hz = ad9361_from_clk(clk_get_rate(phy,
+ phy->ref_clk_scale[RX_RFPLL]));
+ if (lo_freq_hz < 1300000000ULL)
+ lo_index = 0;
+ else
+ if (lo_freq_hz < 3300000000ULL)
+ lo_index = 1;
+ else
+ if (lo_freq_hz < 4100000000ULL)
+ lo_index = 2;
+ else
+ lo_index = 3;
+
+ /* Put the AD9361 into the Alert state. */
+ ad9361_ensm_force_state(phy, ENSM_STATE_ALERT);
+
+ /* Program the directly-addressable register values. */
+ ad9361_spi_write(phy->spi, REG_MAX_MIXER_CALIBRATION_GAIN_INDEX,
+ MAX_MIXER_CALIBRATION_GAIN_INDEX(0x0F));
+ ad9361_spi_write(phy->spi, REG_MEASURE_DURATION,
+ GAIN_CAL_MEAS_DURATION(0x0E));
+ ad9361_spi_write(phy->spi, REG_SETTLE_TIME,
+ SETTLE_TIME(0x3F));
+ ad9361_spi_write(phy->spi, REG_RSSI_CONFIG,
+ RSSI_MODE_SELECT(0x3) | DEFAULT_RSSI_MEAS_MODE);
+ ad9361_spi_write(phy->spi, REG_MEASURE_DURATION_01,
+ MEASUREMENT_DURATION_0(0x0E));
+ ad9361_spi_write(phy->spi, REG_LNA_GAIN,
+ gain_step_calib_reg_val[lo_index][0]);
+
+ /* Program the LNA gain step words into the internal table. */
+ ad9361_spi_write(phy->spi, REG_CONFIG,
+ CALIB_TABLE_SELECT(0x3) | START_CALIB_TABLE_CLOCK);
+ for(i = 0; i < 4; i++) {
+ ad9361_spi_write(phy->spi, REG_WORD_ADDRESS, i);
+ ad9361_spi_write(phy->spi, REG_GAIN_DIFF_WORDERROR_WRITE,
+ gain_step_calib_reg_val[lo_index][i+1]);
+ ad9361_spi_write(phy->spi, REG_CONFIG,
+ CALIB_TABLE_SELECT(0x3) | WRITE_LNA_GAIN_DIFF | START_CALIB_TABLE_CLOCK);
+ udelay(3); //Wait for data to fully write to internal table
+ }
+
+ ad9361_spi_write(phy->spi, REG_CONFIG, START_CALIB_TABLE_CLOCK);
+ ad9361_spi_write(phy->spi, REG_CONFIG, 0x00);
+
+ /* Run and wait until the calibration completes. */
+ ad9361_run_calibration(phy, RX_GAIN_STEP_CAL);
+
+ /* Read the LNA and Mixer error terms into nonvolatile memory. */
+ ad9361_spi_write(phy->spi, REG_CONFIG, CALIB_TABLE_SELECT(0x1) | READ_SELECT);
+ for(i = 0; i < 4; i++) {
+ ad9361_spi_write(phy->spi, REG_WORD_ADDRESS, i);
+ lna_error[i] = ad9361_spi_read(phy->spi, REG_GAIN_ERROR_READ);
+ }
+ ad9361_spi_write(phy->spi, REG_CONFIG, CALIB_TABLE_SELECT(0x1));
+ for(i = 0; i < 15; i++) {
+ ad9361_spi_write(phy->spi, REG_WORD_ADDRESS, i);
+ mixer_error[i] = ad9361_spi_read(phy->spi, REG_GAIN_ERROR_READ);
+ }
+ ad9361_spi_write(phy->spi, REG_CONFIG, 0x00);
+
+ /* Programming gain step errors into the AD9361 in the field */
+ ad9361_spi_write(phy->spi,
+ REG_CONFIG, CALIB_TABLE_SELECT(0x3) | START_CALIB_TABLE_CLOCK);
+ for(i = 0; i < 4; i++) {
+ ad9361_spi_write(phy->spi, REG_WORD_ADDRESS, i);
+ ad9361_spi_write(phy->spi, REG_GAIN_DIFF_WORDERROR_WRITE, lna_error[i]);
+ ad9361_spi_write(phy->spi, REG_CONFIG,
+ CALIB_TABLE_SELECT(0x3) | WRITE_LNA_ERROR_TABLE | START_CALIB_TABLE_CLOCK);
+ }
+ ad9361_spi_write(phy->spi, REG_CONFIG,
+ CALIB_TABLE_SELECT(0x3) | START_CALIB_TABLE_CLOCK);
+ for(i = 0; i < 15; i++) {
+ ad9361_spi_write(phy->spi, REG_WORD_ADDRESS, i);
+ ad9361_spi_write(phy->spi, REG_GAIN_DIFF_WORDERROR_WRITE, mixer_error[i]);
+ ad9361_spi_write(phy->spi, REG_CONFIG,
+ CALIB_TABLE_SELECT(0x3) | WRITE_MIXER_ERROR_TABLE | START_CALIB_TABLE_CLOCK);
+ }
+ ad9361_spi_write(phy->spi, REG_CONFIG, 0x00);
+
+ ad9361_ensm_restore_prev_state(phy);
+
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/common/thirdparty/ad936x/ad9361.h b/Radio/HW/BladeRF/common/thirdparty/ad936x/ad9361.h
new file mode 100644
index 0000000..1a3d45d
--- /dev/null
+++ b/Radio/HW/BladeRF/common/thirdparty/ad936x/ad9361.h
@@ -0,0 +1,3521 @@
+/***************************************************************************//**
+ * @file ad9361.h
+ * @brief Header file of AD9361 Driver.
+********************************************************************************
+ * Copyright 2014(c) Analog Devices, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * - Neither the name of Analog Devices, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * - The use of this software may or may not infringe the patent rights
+ * of one or more patent holders. This license does not release you
+ * from the requirement that you obtain separate licenses from these
+ * patent holders to use this software.
+ * - Use of the software either in source or binary form, must be run
+ * on or directly connected to an Analog Devices Inc. component.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*******************************************************************************/
+#ifndef IIO_FREQUENCY_AD9361_H_
+#define IIO_FREQUENCY_AD9361_H_
+
+/******************************************************************************/
+/***************************** Include Files **********************************/
+/******************************************************************************/
+#include <stdint.h>
+#include "common.h"
+
+/******************************************************************************/
+/********************** Macros and Constants Definitions **********************/
+/******************************************************************************/
+#define REG_SPI_CONF 0x000 /* SPI Configuration */
+#define REG_MULTICHIP_SYNC_AND_TX_MON_CTRL 0x001 /* Multi-Chip Sync and Tx Mon Control */
+#define REG_TX_ENABLE_FILTER_CTRL 0x002 /* Tx Enable & Filter Control */
+#define REG_RX_ENABLE_FILTER_CTRL 0x003 /* Rx Enable & Filter Control */
+#define REG_INPUT_SELECT 0x004 /* Input Select */
+#define REG_RFPLL_DIVIDERS 0x005 /* RFPLL Dividers */
+#define REG_RX_CLOCK_DATA_DELAY 0x006 /* Rx Clock & Data Delay */
+#define REG_TX_CLOCK_DATA_DELAY 0x007 /* Tx Clock & Data Delay */
+#define REG_CLOCK_ENABLE 0x009 /* Clock Enable */
+#define REG_BBPLL 0x00A /* BBPLL */
+#define REG_TEMP_OFFSET 0x00B /* Offset */
+#define REG_START_TEMP_READING 0x00C /* Start Temp Reading */
+#define REG_TEMP_SENSE2 0x00D /* Temp Sense2 */
+#define REG_TEMPERATURE 0x00E /* Temperature */
+#define REG_TEMP_SENSOR_CONFIG 0x00F /* Temp Sensor Config */
+#define REG_PARALLEL_PORT_CONF_1 0x010 /* Parallel Port Configuration 1 */
+#define REG_PARALLEL_PORT_CONF_2 0x011 /* Parallel Port Configuration 2 */
+#define REG_PARALLEL_PORT_CONF_3 0x012 /* Parallel Port Configuration 3 */
+#define REG_ENSM_MODE 0x013 /* ENSM Mode */
+#define REG_ENSM_CONFIG_1 0x014 /* ENSM Config 1 */
+#define REG_ENSM_CONFIG_2 0x015 /* ENSM Config 2 */
+#define REG_CALIBRATION_CTRL 0x016 /* Calibration Control */
+#define REG_STATE 0x017 /* State */
+#define REG_AUXDAC_1_WORD 0x018 /* AuxDAC 1 Word */
+#define REG_AUXDAC_2_WORD 0x019 /* AuxDAC 2 Word */
+#define REG_AUXDAC_1_CONFIG 0x01A /* AuxDAC 1 Config */
+#define REG_AUXDAC_2_CONFIG 0x01B /* AuxDAC 2 Config */
+#define REG_AUXADC_CLOCK_DIVIDER 0x01C /* AuxADC Clock Divider */
+#define REG_AUXADC_CONFIG 0x01D /* Aux ADC Config */
+#define REG_AUXADC_WORD_MSB 0x01E /* AuxADC Word MSB */
+#define REG_AUXADC_LSB 0x01F /* AuxADC LSB */
+#define REG_AUTO_GPO 0x020 /* Auto GPO */
+#define REG_AGC_GAIN_LOCK_DELAY 0x021 /* AGC Gain Lock Delay */
+#define REG_AGC_ATTACK_DELAY 0x022 /* AGC Attack Delay */
+#define REG_AUXDAC_ENABLE_CTRL 0x023 /* AuxDAC Enable Control */
+#define REG_RX_LOAD_SYNTH_DELAY 0x024 /* RX Load Synth Delay */
+#define REG_TX_LOAD_SYNTH_DELAY 0x025 /* TX Load Synth Delay */
+#define REG_EXTERNAL_LNA_CTRL 0x026 /* External LNA control */
+#define REG_GPO_FORCE_AND_INIT 0x027 /* GPO Force and Init */
+#define REG_GPO0_RX_DELAY 0x028 /* GPO0 Rx delay */
+#define REG_GPO1_RX_DELAY 0x029 /* GPO1 Rx delay */
+#define REG_GPO2_RX_DELAY 0x02A /* GPO2 Rx delay */
+#define REG_GPO3_RX_DELAY 0x02B /* GPO3 Rx delay */
+#define REG_GPO0_TX_DELAY 0x02C /* GPO0 Tx Delay */
+#define REG_GPO1_TX_DELAY 0x02D /* GPO1 Tx Delay */
+#define REG_GPO2_TX_DELAY 0x02E /* GPO2 Tx Delay */
+#define REG_GPO3_TX_DELAY 0x02F /* GPO3 Tx Delay */
+#define REG_AUXDAC1_RX_DELAY 0x030 /* AuxDAC1 Rx Delay */
+#define REG_AUXDAC1_TX_DELAY 0x031 /* AuxDAC1 Tx Delay */
+#define REG_AUXDAC2_RX_DELAY 0x032 /* AuxDAC2 Rx Delay */
+#define REG_AUXDAC2_TX_DELAY 0x033 /* AuxDAC2 Tx Delay */
+#define REG_CTRL_OUTPUT_POINTER 0x035 /* Control Output Pointer */
+#define REG_CTRL_OUTPUT_ENABLE 0x036 /* Control Output Enable */
+#define REG_PRODUCT_ID 0x037 /* Product ID */
+#define REG_REFERENCE_CLOCK_CYCLES 0x03A /* Reference Clock Cycles */
+#define REG_DIGITAL_IO_CTRL 0x03B /* Digital I/O Control */
+#define REG_LVDS_BIAS_CTRL 0x03C /* LVDS Bias control */
+#define REG_LVDS_INVERT_CTRL1 0x03D /* LVDS Invert control1 */
+#define REG_LVDS_INVERT_CTRL2 0x03E /* LVDS Invert control2 */
+#define REG_SDM_CTRL_1 0x03F /* SDM Control 1 */
+#define REG_FRACT_BB_FREQ_WORD_1 0x041 /* Fractional BB Freq Word 1 */
+#define REG_FRACT_BB_FREQ_WORD_2 0x042 /* Fractional BB Freq Word 2 */
+#define REG_FRACT_BB_FREQ_WORD_3 0x043 /* Fractional BB Freq Word 3 */
+#define REG_INTEGER_BB_FREQ_WORD 0x044 /* Integer BB Freq Word */
+#define REG_CLOCK_CTRL 0x045 /* Clock Control */
+#define REG_CP_CURRENT 0x046 /* CP Current */
+#define REG_CP_BLEED_CURRENT 0x047 /* CP Bleed Current */
+#define REG_LOOP_FILTER_1 0x048 /* Loop Filter 1 */
+#define REG_LOOP_FILTER_2 0x049 /* Loop Filter 2 */
+#define REG_LOOP_FILTER_3 0x04A /* Loop Filter 3 */
+#define REG_VCO_CTRL 0x04B /* VCO Control */
+#define REG_VCO_PROGRAM_1 0x04C
+#define REG_VCO_PROGRAM_2 0x04D
+#define REG_SDM_CTRL 0x04E /* SDM Control */
+#define REG_RX_SYNTH_POWER_DOWN_OVERRIDE 0x050 /* Rx Synth Power Down Override */
+#define REG_TX_SYNTH_POWER_DOWN_OVERRIDE 0x051 /* TX Synth Power Down Override */
+#define REG_RX_ANALOG_POWER_DOWN_OVERRIDE_1 0x052 /* Rx Analog Power Down Override 1 */
+#define REG_RX_ANALOG_POWER_DOWN_OVERRIDE_2 0x053 /* Rx Analog Power Down Override 2 */
+#define REG_RX1_ADC_POWER_DOWN_OVERRIDE 0x054 /* Rx1 ADC Power Down Override */
+#define REG_RX2_ADC_POWER_DOWN_OVERRIDE 0x055 /* Rx2 ADC Power Down Override */
+#define REG_TX_ANALOG_POWER_DOWN_OVERRIDE_1 0x056 /* Tx Analog Power Down Override 1 */
+#define REG_ANALOG_POWER_DOWN_OVERRIDE 0x057 /* Analog Power Down Override */
+#define REG_MISC_POWER_DOWN_OVERRIDE 0x058 /* Misc Power Down Override */
+#define REG_CH_1_OVERFLOW 0x05E /* CH 1 Overflow */
+#define REG_CH_2_OVERFLOW 0x05F /* CH 2 Overflow */
+#define REG_TX_FILTER_COEF_ADDR 0x060 /* TX Filter Coefficient Address */
+#define REG_TX_FILTER_COEF_WRITE_DATA_1 0x061 /* TX Filter Coefficient Write Data 1 */
+#define REG_TX_FILTER_COEF_WRITE_DATA_2 0x062 /* TX Filter Coefficient Write Data 2 */
+#define REG_TX_FILTER_COEF_READ_DATA_1 0x063 /* TX Filter Coefficient Read Data 1 */
+#define REG_TX_FILTER_COEF_READ_DATA_2 0x064 /* TX Filter Coefficient Read Data 2 */
+#define REG_TX_FILTER_CONF 0x065 /* TX Filter Configuration */
+#define REG_TX_MON_LOW_GAIN 0x067 /* Tx Mon Low Gain */
+#define REG_TX_MON_HIGH_GAIN 0x068 /* Tx Mon High Gain */
+#define REG_TX_MON_DELAY 0x069 /* Tx Mon Delay */
+#define REG_TX_LEVEL_THRESH 0x06A /* Tx Level Threshold */
+#define REG_TX_RSSI1 0x06B /* TX RSSI1 */
+#define REG_TX_RSSI2 0x06C /* TX RSSI2 */
+#define REG_TX_RSSI_LSB 0x06D /* TX RSSI LSB */
+#define REG_TPM_MODE_ENABLE 0x06E /* TPM Mode Enable */
+#define REG_TX_MON_TEMP_GAIN_COEF 0x06F /* Temp Gain Coefficient */
+#define REG_TX_MON_1_CONFIG 0x070 /* Tx Mon 1 Config */
+#define REG_TX_MON_2_CONFIG 0x071 /* Tx Mon 2 Config */
+#define REG_TX1_ATTEN_0 0x073 /* Tx1 Atten 0 */
+#define REG_TX1_ATTEN_1 0x074 /* Tx1 Atten 1 */
+#define REG_TX2_ATTEN_0 0x075 /* Tx2 Atten 0 */
+#define REG_TX2_ATTEN_1 0x076 /* Tx2 Atten 1 */
+#define REG_TX_ATTEN_OFFSET 0x077 /* Tx Atten Offset */
+#define REG_TX_ATTEN_THRESH 0x078 /* Tx Atten Threshold */
+#define REG_TX1_DIG_ATTEN 0x079 /* Tx1 Dig Attenuation */
+#define REG_TX2_DIG_ATTEN 0x07C /* Tx2 Dig Attenuation */
+#define REG_TX1_SYMBOL_ATTEN 0x07F /* TX1 Symbol Attenuation */
+#define REG_TX2_SYMBOL_ATTEN 0x080 /* TX2 Symbol Attenuation */
+#define REG_TX_SYMBOL_ATTEN_CONFIG 0x081 /* TX Symbol Atten Config */
+#define REG_TX1_OUT_1_PHASE_CORR 0x08E /* Tx1 Out 1 Phase Corr */
+#define REG_TX1_OUT_1_GAIN_CORR 0x08F /* Tx1 Out 1 Gain Corr */
+#define REG_TX2_OUT_1_PHASE_CORR 0x090 /* Tx2 Out 1 Phase Corr */
+#define REG_TX2_OUT_1_GAIN_CORR 0x091 /* Tx2 Out 1 Gain Corr */
+#define REG_TX1_OUT_1_OFFSET_I 0x092 /* Tx1 Out 1 Offset I */
+#define REG_TX1_OUT_1_OFFSET_Q 0x093 /* Tx1 Out 1 Offset Q */
+#define REG_TX2_OUT_1_OFFSET_I 0x094 /* Tx2 Out 1 Offset I */
+#define REG_TX2_OUT_1_OFFSET_Q 0x095 /* Tx2 Out 1 Offset Q */
+#define REG_TX1_OUT_2_PHASE_CORR 0x096 /* Tx1 Out 2 Phase Corr */
+#define REG_TX1_OUT_2_GAIN_CORR 0x097 /* Tx1 Out 2 Gain Corr */
+#define REG_TX2_OUT_2_PHASE_CORR 0x098 /* Tx2 Out 2 Phase Corr */
+#define REG_TX2_OUT_2_GAIN_CORR 0x099 /* Tx2 Out 2 Gain Corr */
+#define REG_TX1_OUT_2_OFFSET_I 0x09A /* Tx1 Out 2 Offset I */
+#define REG_TX1_OUT_2_OFFSET_Q 0x09B /* Tx1 Out 2 Offset Q */
+#define REG_TX2_OUT_2_OFFSET_I 0x09C /* Tx2 Out 2 Offset I */
+#define REG_TX2_OUT_2_OFFSET_Q 0x09D /* Tx2 Out 2 Offset Q */
+#define REG_TX_FORCE_BITS 0x09F /* Force Bits */
+#define REG_QUAD_CAL_NCO_FREQ_PHASE_OFFSET 0x0A0 /* Quad Cal NCO Freq & Phase Offset */
+#define REG_QUAD_CAL_CTRL 0x0A1 /* Quad Cal Control */
+#define REG_KEXP_1 0x0A2 /* Kexp 1 */
+#define REG_KEXP_2 0x0A3 /* Kexp 2 */
+#define REG_QUAD_SETTLE_COUNT 0x0A4 /* QUAD Settle count */
+#define REG_MAG_FTEST_THRESH 0x0A5 /* Mag. Ftest Thresh */
+#define REG_MAG_FTEST_THRESH_2 0x0A6 /* Mag. Ftest Thresh 2 */
+#define REG_QUAD_CAL_STATUS_TX1 0x0A7 /* Quad cal status Tx1 */
+#define REG_QUAD_CAL_STATUS_TX2 0x0A8 /* Quad cal status Tx2 */
+#define REG_QUAD_CAL_COUNT 0x0A9 /* Quad cal Count */
+#define REG_TX_QUAD_FULL_LMT_GAIN 0x0AA /* Tx Quad Full/LMT Gain */
+#define REG_SQUARER_CONFIG 0x0AB /* Squarer Config */
+#define REG_TX_QUAD_CAL_ATTEN 0x0AC /* TX Quad Cal Atten */
+#define REG_THRESH_ACCUM 0x0AD /* Thresh Accum */
+#define REG_TX_QUAD_LPF_GAIN 0x0AE /* Tx Quad LPF Gain */
+#define REG_TXDAC_VDS_I 0x0B0 /* TxDAC Vds I */
+#define REG_TXDAC_VDS_Q 0x0B1 /* TxDAC Vds Q */
+#define REG_TXDAC_GN_I 0x0B2 /* TxDAC gn I */
+#define REG_TXDAC_GN_Q 0x0B3 /* TxDAC gn Q */
+#define REG_TXBBF_OPAMP_A 0x0C0 /* TxBBF OpAmp A */
+#define REG_TXBBF_OPAMP_B 0x0C1 /* TxBBF OpAmp B */
+#define REG_TX_BBF_R1 0x0C2 /* Tx BBF R1 */
+#define REG_TX_BBF_R2 0x0C3 /* Tx BBF R2 */
+#define REG_TX_BBF_R3 0x0C4 /* Tx BBF R3 */
+#define REG_TX_BBF_R4 0x0C5 /* Tx BBF R4 */
+#define REG_TX_BBF_RP 0x0C6 /* Tx BBF RP */
+#define REG_TX_BBF_C1 0x0C7 /* Tx BBF C1 */
+#define REG_TX_BBF_C2 0x0C8 /* Tx BBF C2 */
+#define REG_TX_BBF_CP 0x0C9 /* Tx BBF Cp */
+#define REG_TX_TUNE_CTRL 0x0CA /* Tx Tune Control */
+#define REG_TX_BBF_R2B 0x0CB /* Tx BBF R2b */
+#define REG_TX_BBF_TUNE 0x0CC /* Tx BBF Tune */
+#define REG_CONFIG0 0x0D0 /* Config0 */
+#define REG_RESISTOR 0x0D1 /* Resistor */
+#define REG_CAPACITOR 0x0D2 /* Capacitor */
+#define REG_LO_CM 0x0D3 /* LO CM */
+#define REG_TX_BBF_TUNE_DIVIDER 0x0D6 /* TX BBF Tune Divider */
+#define REG_TX_BBF_TUNE_MODE 0x0D7 /* TX BBF Tune Mode */
+#define REG_RX_FILTER_COEF_ADDR 0x0F0 /* Rx Filter Coeff Addr */
+#define REG_RX_FILTER_COEF_DATA_1 0x0F1 /* Rx Filter Coeff Data 1 */
+#define REG_RX_FILTER_COEF_DATA_2 0x0F2 /* Rx Filter Coeff Data 2 */
+#define REG_RX_FILTER_COEF_READ_DATA_1 0x0F3 /* Rx Filter Coeff Read Data 1 */
+#define REG_RX_FILTER_COEF_READ_DATA_2 0x0F4 /* Rx Filter Coeff Read Data 2 */
+#define REG_RX_FILTER_CONFIG 0x0F5 /* Rx Filter Config */
+#define REG_RX_FILTER_GAIN 0x0F6 /* Rx Filter Gain */
+#define REG_AGC_CONFIG_1 0x0FA /* AGC Config1 */
+#define REG_AGC_CONFIG_2 0x0FB /* AGC config2 */
+#define REG_AGC_CONFIG_3 0x0FC /* AGC Config3 */
+#define REG_MAX_LMT_FULL_GAIN 0x0FD /* Max LMT/Full Gain */
+#define REG_PEAK_WAIT_TIME 0x0FE /* Peak Wait Time */
+#define REG_DIGITAL_GAIN 0x100 /* Digital Gain */
+#define REG_AGC_LOCK_LEVEL 0x101 /* AGC Lock Level */
+#define REG_ADC_NOISE_CORRECTION_FACTOR 0x102 /* ADC noise Correction Factor */
+#define REG_GAIN_STP_CONFIG1 0x103 /* Gain Step Config1 */
+#define REG_ADC_SMALL_OVERLOAD_THRESH 0x104 /* ADC Small Overload Threshold */
+#define REG_ADC_LARGE_OVERLOAD_THRESH 0x105 /* ADC Large Overload Threshold */
+#define REG_GAIN_STP_CONFIG_2 0x106 /* Gain Step Config 2 */
+#define REG_SMALL_LMT_OVERLOAD_THRESH 0x107 /* Small LMT Overload Threshold */
+#define REG_LARGE_LMT_OVERLOAD_THRESH 0x108 /* Large LMT Overload Threshold */
+#define REG_RX1_MANUAL_LMT_FULL_GAIN 0x109 /* Rx1 Manual LMT/Full Gain */
+#define REG_RX1_MANUAL_LPF_GAIN 0x10A /* Rx1 Manual LPF gain */
+#define REG_RX1_MANUAL_DIGITALFORCED_GAIN 0x10B /* Rx1 Manual Digital/Forced Gain */
+#define REG_RX2_MANUAL_LMT_FULL_GAIN 0x10C /* Rx2 Manual LMT/Full Gain */
+#define REG_RX2_MANUAL_LPF_GAIN 0x10D /* Rx2 Manual LPF Gain */
+#define REG_RX2_MANUAL_DIGITALFORCED_GAIN 0x10E /* Rx2 Manual Digital/Forced Gain */
+#define REG_FAST_CONFIG_1 0x110 /* Config 1 */
+#define REG_FAST_CONFIG_2_SETTLING_DELAY 0x111 /* Config 2 & Settling Delay */
+#define REG_FAST_ENERGY_LOST_THRESH 0x112 /* Energy Lost Threshold */
+#define REG_FAST_STRONGER_SIGNAL_THRESH 0x113 /* Stronger Signal Threshold */
+#define REG_FAST_LOW_POWER_THRESH 0x114 /* Low Power Threshold */
+#define REG_FAST_STRONG_SIGNAL_FREEZE 0x115 /* Strong Signal Freeze */
+#define REG_FAST_FINAL_OVER_RANGE_AND_OPT_GAIN 0x116 /* Final Over Range and Opt Gain */
+#define REG_FAST_ENERGY_DETECT_COUNT 0x117 /* Energy Detect Count */
+#define REG_FAST_AGCLL_UPPER_LIMIT 0x118 /* AGCLL Upper Limit */
+#define REG_FAST_GAIN_LOCK_EXIT_COUNT 0x119 /* Gain Lock Exit Count */
+#define REG_FAST_INITIAL_LMT_GAIN_LIMIT 0x11A /* Initial LMT Gain Limit */
+#define REG_FAST_INCREMENT_TIME 0x11B /* Increment Time */
+#define REG_AGC_INNER_LOW_THRESH 0x120 /* AGC Inner Low Threshold */
+#define REG_LMT_OVERLOAD_COUNTERS 0x121 /* LMT Overload Counters */
+#define REG_ADC_OVERLOAD_COUNTERS 0x122 /* ADC Overload Counters */
+#define REG_GAIN_STP1 0x123 /* Gain Step1 */
+#define REG_GAIN_UPDATE_COUNTER1 0x124 /* Gain Update Counter1 */
+#define REG_GAIN_UPDATE_COUNTER2 0x125 /* Gain Update Counter2 */
+#define REG_DIGITAL_SAT_COUNTER 0x128 /* Digital Sat Counter */
+#define REG_OUTER_POWER_THRESHS 0x129 /* Outer Power Thresholds */
+#define REG_GAIN_STP_2 0x12A /* Gain Step 2 */
+#define REG_EXT_LNA_HIGH_GAIN 0x12C /* Ext LNA High Gain */
+#define REG_EXT_LNA_LOW_GAIN 0x12D /* Ext LNA Low Gain */
+#define REG_GAIN_TABLE_ADDRESS 0x130 /* Gain Table Address */
+#define REG_GAIN_TABLE_WRITE_DATA1 0x131 /* Gain Table Write Data1 */
+#define REG_GAIN_TABLE_WRITE_DATA2 0x132 /* Gain Table Write Data2 */
+#define REG_GAIN_TABLE_WRITE_DATA3 0x133 /* Gain Table Write Data 3 */
+#define REG_GAIN_TABLE_READ_DATA1 0x134 /* Gain Table Read Data 1 */
+#define REG_GAIN_TABLE_READ_DATA2 0x135 /* Gain Table Read Data 2 */
+#define REG_GAIN_TABLE_READ_DATA3 0x136 /* Gain Table Read Data 3 */
+#define REG_GAIN_TABLE_CONFIG 0x137 /* Gain Table Config */
+#define REG_GM_SUB_TABLE_ADDRESS 0x138 /* Gm Sub Table Address */
+#define REG_GM_SUB_TABLE_GAIN_WRITE 0x139 /* Gm Sub Table Gain Word Write */
+#define REG_GM_SUB_TABLE_BIAS_WRITE 0x13A /* Gm Sub Table Bias Word Write */
+#define REG_GM_SUB_TABLE_CTRL_WRITE 0x13B /* Gm Sub Table Control Word Write */
+#define REG_GM_SUB_TABLE_GAIN_READ 0x13C /* Gm Sub Table Gain Word Read */
+#define REG_GM_SUB_TABLE_BIAS_READ 0x13D /* Gm Sub Table Bias Word Read */
+#define REG_GM_SUB_TABLE_CTRL_READ 0x13E /* Gm Sub Table Control Word Read */
+#define REG_GM_SUB_TABLE_CONFIG 0x13F /* Gm Sub Table Config */
+#define REG_WORD_ADDRESS 0x140 /* Word Address */
+#define REG_GAIN_DIFF_WORDERROR_WRITE 0x141 /* Gain Diff Word/Error Write */
+#define REG_GAIN_ERROR_READ 0x142 /* Gain Error Read */
+#define REG_CONFIG 0x143 /* Config */
+#define REG_LNA_GAIN_DIFF_READ_BACK 0x144 /* LNA Gain Diff Read Back */
+#define REG_MAX_MIXER_CALIBRATION_GAIN_INDEX 0x145 /* Max Mixer Calibration Gain Index */
+#define REG_TEMP_GAIN_COEF 0x146 /* Temp Gain Coefficient */
+#define REG_SETTLE_TIME 0x147 /* Settle Time */
+#define REG_MEASURE_DURATION 0x148 /* Measure Duration */
+#define REG_CAL_TEMP_SENSOR_WORD 0x149 /* Cal Temp sensor word */
+#define REG_MEASURE_DURATION_01 0x150 /* Measure Duration 0&1 */
+#define REG_MEASURE_DURATION_23 0x151 /* Measure Duration 2&3 */
+#define REG_RSSI_WEIGHT_0 0x152 /* RSSI Weight 0 */
+#define REG_RSSI_WEIGHT_1 0x153 /* RSSI Weight 1 */
+#define REG_RSSI_WEIGHT_2 0x154 /* RSSI Weight 2 */
+#define REG_RSSI_WEIGHT_3 0x155 /* RSSI Weight 3 */
+#define REG_RSSI_DELAY 0x156 /* RSSI delay */
+#define REG_RSSI_WAIT_TIME 0x157 /* RSSI wait time */
+#define REG_RSSI_CONFIG 0x158 /* RSSI Config */
+#define REG_ADC_MEASURE_DURATION_01 0x159 /* ADC Measure Duration 0&1 */
+#define REG_ADC_WEIGHT_0 0x15A /* ADC Weight 0 */
+#define REG_ADC_WEIGHT_1 0x15B /* ADC Weight 1 */
+#define REG_DEC_POWER_MEASURE_DURATION_0 0x15C /* Dec Power Measure Duration 0 */
+#define REG_LNA_GAIN 0x15D /* LNA Gain */
+#define REG_CH1_ADC_POWER 0x160 /* CH1 ADC Power */
+#define REG_CH1_RX_FILTER_POWER 0x161 /* CH1 Rx filter Power */
+#define REG_CH2_ADC_POWER 0x162 /* CH2 ADC Power */
+#define REG_CH2_RX_FILTER_POWER 0x163 /* CH2 Rx filter Power */
+#define REG_RX_QUAD_CAL_LEVEL 0x168 /* Rx Quad Cal Level */
+#define REG_CALIBRATION_CONFIG_1 0x169 /* Calibration Config 1 */
+#define REG_CALIBRATION_CONFIG_2 0x16A /* Calibration config2 */
+#define REG_CALIBRATION_CONFIG_3 0x16B /* Calibration config3 */
+#define REG_CALIB_COUNT 0x16C /* Calib count */
+#define REG_SETTLE_COUNT 0x16D /* Settle count */
+#define REG_RX_QUAD_GAIN1 0x16E /* Rx Quad gain1 */
+#define REG_RX_QUAD_GAIN2 0x16F /* Rx Quad gain2 */
+#define REG_RX1_INPUT_A_PHASE_CORR 0x170 /* Rx1 Input A Phase Corr */
+#define REG_RX1_INPUT_A_GAIN_CORR 0x171 /* Rx1 Input A Gain Corr */
+#define REG_RX2_INPUT_A_PHASE_CORR 0x172 /* Rx2 Input A Phase Corr */
+#define REG_RX2_INPUT_A_GAIN_CORR 0x173 /* Rx2 Input A Gain Corr */
+#define REG_RX1_INPUT_A_Q_OFFSET 0x174 /* Rx1 Input A Q" Offset */
+#define REG_RX1_INPUT_A_OFFSETS 0x175 /* Rx1 Input A Offsets */
+#define REG_INPUT_A_OFFSETS_1 0x176 /* Input A Offsets 1 */
+#define REG_RX2_INPUT_A_OFFSETS 0x177 /* Rx2 Input A Offsets */
+#define REG_RX2_INPUT_A_I_OFFSET 0x178 /* Rx2 Input A "I" Offset */
+#define REG_RX1_INPUT_BC_PHASE_CORR 0x179 /* Rx1 Input B&C Phase Corr */
+#define REG_RX1_INPUT_BC_GAIN_CORR 0x17A /* Rx1 Input B&C Gain Corr */
+#define REG_RX2_INPUT_BC_PHASE_CORR 0x17B /* Rx2 Input B&C Phase Corr */
+#define REG_RX2_INPUT_BC_GAIN_CORR 0x17C /* Rx2 Input B&C Gain Corr */
+#define REG_RX1_INPUT_BC_Q_OFFSET 0x17D /* Rx1 Input B&C "Q" Offset */
+#define REG_RX1_INPUT_BC_OFFSETS 0x17E /* Rx1 Input B&C Offsets */
+#define REG_INPUT_BC_OFFSETS_1 0x17F /* Input B&C Offsets 1 */
+#define REG_RX2_INPUT_BC_OFFSETS 0x180 /* Rx2 Input B&C Offsets */
+#define REG_RX2_INPUT_BC_I_OFFSET 0x181 /* Rx2 Input B&C "I" Offset */
+#define REG_FORCE_BITS 0x182 /* Force Bits */
+#define REG_WAIT_COUNT 0x185 /* Wait Count */
+#define REG_RF_DC_OFFSET_COUNT 0x186 /* RF DC Offset Count */
+#define REG_RF_DC_OFFSET_CONFIG_1 0x187 /* RF DC Offset Config1 */
+#define REG_RF_DC_OFFSET_ATTEN 0x188 /* RF DC Offset Attenuation */
+#define REG_INVERT_BITS 0x189 /* Invert Bits */
+#define REG_DC_OFFSET_CONFIG2 0x18B /* DC Offset Config2 */
+#define REG_RF_CAL_GAIN_INDEX 0x18C /* RF Cal Gain Index */
+#define REG_SOI_THRESH 0x18D /* SOI Threshold */
+#define REG_BB_DC_OFFSET_SHIFT 0x190 /* BB DC Offset Shift */
+#define REG_BB_DC_OFFSET_FAST_SETTLE_SHIFT 0x191 /* BB DC Offset Fast Settle Shift */
+#define REG_BB_FAST_SETTLE_DUR 0x192 /* BB Fast Settle Dur */
+#define REG_BB_DC_OFFSET_COUNT 0x193 /* BB DC Offset Count */
+#define REG_BB_DC_OFFSET_ATTEN 0x194 /* BB DC Offset Attenuation */
+#define REG_RX1_BB_DC_WORD_I_MSB 0x19A /* RX1 BB DC word I MSB */
+#define REG_RX1_BB_DC_WORD_I_LSB 0x19B /* RX1 BB DC word I LSB */
+#define REG_RX1_BB_DC_WORD_Q_MSB 0x19C /* RX1 BB DC word Q MSB */
+#define REG_RX1_BB_DC_WORD_Q_LSB 0x19D /* RX1 BB DC word Q LSB */
+#define REG_RX2_BB_DC_WORD_I_MSB 0x19E /* RX2 BB DC word I MSB */
+#define REG_RX2_BB_DC_WORD_I_LSB 0x19F /* RX2 BB DC word I LSB */
+#define REG_RX2_BB_DC_WORD_Q_MSB 0x1A0 /* RX2 BB DC word Q MSB */
+#define REG_RX2_BB_DC_WORD_Q_LSB 0x1A1 /* RX2 BB DC word Q LSB */
+#define REG_BB_TRACK_CORR_WORD_I_MSB 0x1A2 /* BB Track corr word I MSB */
+#define REG_BB_TRACK_CORR_WORD_I_LSB 0x1A3 /* BB Track corr word I LSB */
+#define REG_BB_TRACK_CORR_WORD_Q_MSB 0x1A4 /* BB Track corr word Q MSB */
+#define REG_BB_TRACK_CORR_WORD_Q_LSB 0x1A5 /* BB Track corr word Q LSB */
+#define REG_RX1_RSSI_SYMBOL 0x1A7 /* Rx1 RSSI Symbol */
+#define REG_RX1_RSSI_PREAMBLE 0x1A8 /* Rx1 RSSI preamble */
+#define REG_RX2_RSSI_SYMBOL 0x1A9 /* Rx2 RSSI symbol */
+#define REG_RX2_RSSI_PREAMBLE 0x1AA /* Rx2 RSSI preamble */
+#define REG_SYMBOL_LSB 0x1AB /* Symbol LSB */
+#define REG_PREAMBLE_LSB 0x1AC /* Preamble LSB */
+#define REG_RX_PATH_GAIN_MSB 0x1AD /* Rx Path Gain */
+#define REG_RX_PATH_GAIN_LSB 0x1AE /* Rx Path Gain */
+#define REG_RX_DIFF_LNA_FORCE 0x1B0 /* Rx Diff LNA Force */
+#define REG_RX_LNA_BIAS_COARSE 0x1B1 /* Rx LNA Bias Coarse */
+#define REG_RX_LNA_BIAS_FINE_0 0x1B2 /* Rx LNA Bias Fine 0 */
+#define REG_RX_LNA_BIAS_FINE_1 0x1B3 /* Rx LNA Bias Fine 1 */
+#define REG_RX_MIX_GM_CONFIG 0x1C0 /* Rx Mix Gm Config */
+#define REG_RX1_MIX_GM_FORCE 0x1C1 /* Rx1 Mix Gm Force */
+#define REG_RX1_MIX_GM_BIAS_FORCE 0x1C2 /* Rx1 Mix Gm Bias (Force) */
+#define REG_RX2_MIX_GM_FORCE 0x1C3 /* Rx2 Mix Gm Force */
+#define REG_RX2_MIX_GM_BIAS_FORCE 0x1C4 /* Rx2 Mix Gm Bias (Force) */
+#define REG_INPUT_A_MSBS 0x1C8 /* Input A MSBs */
+#define REG_INPUT_A_RX1_I 0x1C9 /* Input A RX1 I */
+#define REG_INPUT_A_RX1_Q 0x1CA /* Input A RX1 Q */
+#define REG_INPUT_A_RX2_I 0x1CB /* Input A RX2 I */
+#define REG_INPUT_A_RX2_Q 0x1CC /* Input A RX2 Q */
+#define REG_INPUTS_BC_RX1_I 0x1CD /* Inputs B&C RX1 I */
+#define REG_BAND1_RX1_Q 0x1CE /* Band1 RX1 Q */
+#define REG_INPUTS_BC_RX2_I 0x1CF /* Inputs B&C RX2 I */
+#define REG_INPUTS_BC_RX2_Q 0x1D0 /* Inputs B&C RX2 Q */
+#define REG_INPUTS_BC_MSBS 0x1D1 /* Inputs B&C MSBs */
+#define REG_FORCE_OS_DAC 0x1D2 /* Force OS DAC */
+#define REG_RX_MIX_LO_CM 0x1D5 /* Rx Mix LO CM */
+#define REG_RX_CGB_SEG_ENABLE 0x1D6 /* Rx CGB Seg Enable */
+#define REG_RX_MIX_INPUTBIAS 0x1D7 /* Rx Mix Input/Bias */
+#define REG_RX_TIA_CONFIG 0x1DB /* Rx TIA Config */
+#define REG_TIA1_C_LSB 0x1DC /* TIA1 C LSB */
+#define REG_TIA1_C_MSB 0x1DD /* TIA1 C MSB */
+#define REG_TIA2_C_LSB 0x1DE /* TIA2 C LSB */
+#define REG_TIA2_C_MSB 0x1DF /* TIA2 C MSB */
+#define REG_RX1_BBF_R1A 0x1E0 /* Rx1 BBF R1A */
+#define REG_RX2_BBF_R1A 0x1E1 /* Rx2 BBF R1A */
+#define REG_RX1_TUNE_CTRL 0x1E2 /* Rx1 Tune Control */
+#define REG_RX2_TUNE_CTRL 0x1E3 /* Rx2 Tune Control */
+#define REG_RX1_BBF_R5 0x1E4 /* Rx1 BBF R5 */
+#define REG_RX2_BBF_R5 0x1E5 /* Rx2 BBF R5 */
+#define REG_RX_BBF_R2346 0x1E6 /* Rx BBF R2346 */
+#define REG_RX_BBF_C1_MSB 0x1E7 /* Rx BBF C1 MSB */
+#define REG_RX_BBF_C1_LSB 0x1E8 /* Rx BBF C1 LSB */
+#define REG_RX_BBF_C2_MSB 0x1E9 /* Rx BBF C2 MSB */
+#define REG_RX_BBF_C2_LSB 0x1EA /* Rx BBF C2 LSB */
+#define REG_RX_BBF_C3_MSB 0x1EB /* Rx BBF C3 MSB */
+#define REG_RX_BBF_C3_LSB 0x1EC /* Rx BBF C3 LSB */
+#define REG_RX_BBF_CC1_CTR 0x1ED /* Rx BBF CC1 Ctr */
+#define REG_RX_BBF_POW_RZ_BYTE0 0x1EE /* Rx BBF Pow Rz Byte0 */
+#define REG_RX_BBF_CC2_CTR 0x1EF /* Rx BBF CC2 Ctr */
+#define REG_RX_BBF_POW_RZ_BYTE1 0x1F0 /* Rx BBF Pow Rz Byte1 */
+#define REG_RX_BBF_CC3_CTR 0x1F1 /* Rx BBF CC3 Ctr */
+#define REG_RX_BBF_R5_TUNE 0x1F2 /* Rx BBF R5 Tune */
+#define REG_RX_BBF_TUNE 0x1F3 /* Rx BBF Tune */
+#define REG_RX1_BBF_MAN_GAIN 0x1F4 /* Rx1 BBF Man Gain */
+#define REG_RX2_BBF_MAN_GAIN 0x1F5 /* Rx2 BBF Man Gain */
+#define REG_RX_BBF_TUNE_DIVIDE 0x1F8 /* RX BBF Tune Divide */
+#define REG_RX_BBF_TUNE_CONFIG 0x1F9 /* RX BBF Tune Config */
+#define REG_POLE_GAIN 0x1FA /* Pole gain */
+#define REG_RX_BBBW_MHZ 0x1FB /* Rx BBBW MHz */
+#define REG_RX_BBBW_KHZ 0x1FC /* Rx BBBW kHz */
+#define REG_FB_DAC_CLK_DELAY1 0x201 /* FB DAC Clk Delay1 */
+#define REG_FB_DAC_CLK_DELAY2 0x202 /* FB DAC Clk Delay2 */
+#define REG_FLASH_SAMPLE_CLK_DELAY_3P 0x203 /* Flash Sample Clk Delay 3p */
+#define REG_FLASH_SAMPLE_CLK_DELAY_3N 0x204 /* Flash Sample Clk Delay 3n */
+#define REG_TEST_MUX_2I 0x205 /* Test MUX 2i */
+#define REG_TEST_MUX_2Q 0x206 /* Test MUX 2q */
+#define REG_INTEGRATOR_1_RESISTANCE 0x207 /* Integrator 1 Resistance */
+#define REG_INTEGRATOR_1_CAPACITANCE 0x208 /* Integrator 1 Capacitance */
+#define REG_INTEGRATOR_23_RESISTANCE 0x209 /* Integrator 23 Resistance */
+#define REG_INTEGRATOR_2_RESISTANCE 0x20A /* Integrator 2 Resistance */
+#define REG_INTEGRATOR_2_CAPACITANCE 0x20B /* Integrator 2 Capacitance */
+#define REG_INTEGRATOR_3_RESISTANCE 0x20C /* Integrator 3 Resistance */
+#define REG_INTEGRATOR_3_CAPACITANCE 0x20D /* Integrator 3 Capacitance */
+#define REG_INTEGRATOR_AMP_CC 0x20E /* Integrator Amp Cc */
+#define REG_INT_1_FB_DAC_NMOS_CURRENT_SOURCE 0x20F /* Int 1 FB DAC NMOS Current Source */
+#define REG_INT_1_FB_DAC_NMOS_CASOADE_BIAS_CURRENT 0x210 /* Int 1 FB DAC NMOS Casoade Bias Current */
+#define REG_INT_1_FB_DAC_PMOS_CURRENT_SOURCE 0x211 /* Int 1 FB DAC PMOS Current Source */
+#define REG_INT_2_FB_DAC_NMOS_CURRENT_SOURCE 0x212 /* Int 2 FB DAC NMOS Current Source */
+#define REG_INT_2_FB_DAC_NMOS_CASCODE_BIAS_CURRENT 0x213 /* Int 2 FB DAC NMOS Cascode Bias Current */
+#define REG_INT_2_FB_DAC_PMOS_CURRENT_SOURCE 0x214 /* Int 2 FB DAC PMOS Current Source */
+#define REG_INT_3_FB_DAC_NMOS_CURRENT_SOURCE 0x215 /* Int 3 FB DAC NMOS Current Source */
+#define REG_INT_3_FB_DAC_NMOS_CASCODE_BIAS_CURRENT 0x216 /* Int 3 FB DAC NMOS Cascode Bias Current */
+#define REG_INT_3_FB_DAC_PMOS_CURRENT_SOURCE 0x217 /* Int 3 FB DAC PMOS Current Source */
+#define REG_FB_DAC_BIAS_CURRENT 0x218 /* FB DAC Bias Current */
+#define REG_INT_1_1ST_STAGE_CURRENT 0x219 /* Int 1 1st Stage Current */
+#define REG_INT_1_1ST_STAGE_CASCODE_CURRENT 0x21A /* Int 1 1st Stage Cascode Current */
+#define REG_INT_1_2ND_STAGE_CURRENT 0x21B /* Int 1 2nd Stage Current */
+#define REG_INTEGRATOR_2_1ST_STAGE_CURRENT 0x21C /* Integrator 2 1st Stage Current */
+#define REG_INT_2_1ST_STAGE_CASCODE_CURRENT 0x21D /* Int 2 1st Stage Cascode Current */
+#define REG_INT_2_2ND_STAGE_CURRENT 0x21E /* Int 2 2nd Stage Current */
+#define REG_INT_3_1ST_STAGE_CURRENT 0x21F /* Int 3 1st Stage Current */
+#define REG_INT_3_1ST_STAGE_CASCODE_CURRENT 0x220 /* Int 3 1st Stage Cascode Current */
+#define REG_INT_3_2ND_STAGE_CURRENT 0x221 /* Int 3 2nd Stage Current */
+#define REG_FLASH_BIAS_CURRENT 0x222 /* Flash Bias Current */
+#define REG_FLASH_LADDER_BIAS 0x223 /* Flash Ladder Bias */
+#define REG_FLASH_LADDER_CASCODE_CURRENT 0x224 /* Flash Ladder Cascode Current */
+#define REG_FLASH_LADDER_BIAS2 0x225 /* Flash Ladder Bias2 */
+#define REG_RESET 0x226 /* Reset */
+#define REG_RX_PFD_CONFIG 0x230 /* RX PFD Config */
+#define REG_RX_INTEGER_BYTE_0 0x231 /* RX Integer Byte 0 */
+#define REG_RX_INTEGER_BYTE_1 0x232 /* RX Integer Byte 1 */
+#define REG_RX_FRACT_BYTE_0 0x233 /* RX Fractional Byte 0 */
+#define REG_RX_FRACT_BYTE_1 0x234 /* RX Fractional Byte 1 */
+#define REG_RX_FRACT_BYTE_2 0x235 /* RX Fractional Byte 2 */
+#define REG_RX_FORCE_ALC 0x236 /* RX Force ALC */
+#define REG_RX_FORCE_VCO_TUNE_0 0x237 /* RX Force VCO Tune 0 */
+#define REG_RX_FORCE_VCO_TUNE_1 0x238 /* RX Force VCO Tune 1 */
+#define REG_RX_ALC_VARACTOR 0x239 /* RX ALC/Varactor */
+#define REG_RX_VCO_OUTPUT 0x23A /* RX VCO Output */
+#define REG_RX_CP_CURRENT 0x23B /* RX CP Current */
+#define REG_RX_CP_OFFSET 0x23C /* RX CP Offset */
+#define REG_RX_CP_CONFIG 0x23D /* RX CP Config */
+#define REG_RX_LOOP_FILTER_1 0x23E /* RX Loop Filter 1 */
+#define REG_RX_LOOP_FILTER_2 0x23F /* RX Loop Filter 2 */
+#define REG_RX_LOOP_FILTER_3 0x240 /* RX Loop Filter 3 */
+#define REG_RX_DITHERCP_CAL 0x241 /* RX Dither/CP Cal */
+#define REG_RX_VCO_BIAS_1 0x242 /* RX VCO Bias 1 */
+#define REG_RX_CAL_STATUS 0x244 /* RX Cal Status */
+#define REG_RX_VCO_CAL_REF 0x245 /* RX VCO Cal Ref */
+#define REG_RX_VCO_PD_OVERRIDES 0x246 /* RX VCO Pd Overrides */
+#define REG_RX_CP_OVERRANGE_VCO_LOCK 0x247 /* RX CP Over Range/VCO Lock */
+#define REG_RX_VCO_LDO 0x248 /* RX VCO LDO */
+#define REG_RX_VCO_CAL 0x249 /* RX VCO Cal */
+#define REG_RX_LOCK_DETECT_CONFIG 0x24A /* RX Lock Detect Config */
+#define REG_RX_CP_LEVEL_DETECT 0x24B /* RX CP Level Detect */
+#define REG_RX_DSM_SETUP_0 0x24C /* RX DSM Setup 0 */
+#define REG_RX_DSM_SETUP_1 0x24D /* RX DSM Setup 1 */
+#define REG_RX_CORRECTION_WORD0 0x24E /* RX Correction Word0 */
+#define REG_RX_CORRECTION_WORD1 0x24F /* RX Correction Word1 */
+#define REG_RX_VCO_VARACTOR_CTRL_0 0x250 /* RX VCO Varactor Control 0 */
+#define REG_RX_VCO_VARACTOR_CTRL_1 0x251 /* RX VCO Varactor Control 1 */
+#define REG_RX_FAST_LOCK_SETUP 0x25A /* Rx Fast Lock Setup */
+#define REG_RX_FAST_LOCK_SETUP_INIT_DELAY 0x25B /* Rx Fast Lock Setup Init Delay */
+#define REG_RX_FAST_LOCK_PROGRAM_ADDR 0x25C /* Rx Fast Lock Program Addr */
+#define REG_RX_FAST_LOCK_PROGRAM_DATA 0x25D /* Rx Fast Lock Program Data */
+#define REG_RX_FAST_LOCK_PROGRAM_READ 0x25E /* Rx Fast Lock Program Read */
+#define REG_RX_FAST_LOCK_PROGRAM_CTRL 0x25F /* Rx Fast Lock Program Control */
+#define REG_RX_LO_GEN_POWER_MODE 0x261 /* Rx LO Gen Power Mode */
+#define REG_TX_PFD_CONFIG 0x270 /* TX PFD Config */
+#define REG_TX_INTEGER_BYTE_0 0x271 /* TX Integer Byte 0 */
+#define REG_TX_INTEGER_BYTE_1 0x272 /* TX Integer Byte 1 */
+#define REG_TX_FRACT_BYTE_0 0x273 /* TX Fractional Byte 0 */
+#define REG_TX_FRACT_BYTE_1 0x274 /* TX Fractional Byte 1 */
+#define REG_TX_FRACT_BYTE_2 0x275 /* TX Fractional Byte 2 */
+#define REG_TX_FORCE_ALC 0x276 /* TX Force ALC */
+#define REG_TX_FORCE_VCO_TUNE_0 0x277 /* TX Force VCO Tune 0 */
+#define REG_TX_FORCE_VCO_TUNE_1 0x278 /* TX Force VCO Tune 1 */
+#define REG_TX_ALCVARACT_OR 0x279 /* TX ALC/Varact or */
+#define REG_TX_VCO_OUTPUT 0x27A /* TX VCO Output */
+#define REG_TX_CP_CURRENT 0x27B /* TX CP Current */
+#define REG_TX_CP_OFFSET 0x27C /* TX CP Offset */
+#define REG_TX_CP_CONFIG 0x27D /* TX CP Config */
+#define REG_TX_LOOP_FILTER_1 0x27E /* TX Loop Filter 1 */
+#define REG_TX_LOOP_FILTER_2 0x27F /* TX Loop Filter 2 */
+#define REG_TX_LOOP_FILTER_3 0x280 /* TX Loop Filter 3 */
+#define REG_TX_DITHERCP_CAL 0x281 /* TX Dither/CP Cal */
+#define REG_TX_VCO_BIAS_1 0x282 /* TX VCO Bias 1 */
+#define REG_TX_VCO_BIAS_2 0x283 /* TX VCO Bias 2 */
+#define REG_TX_CAL_STATUS 0x284 /* TX Cal Status */
+#define REG_TX_VCO_CAL_REF 0x285 /* TX VCO Cal Ref */
+#define REG_TX_VCO_PD_OVERRIDES 0x286 /* TX VCO Pd Overrides */
+#define REG_TX_CP_OVERRANGE_VCO_LOCK 0x287 /* TX CP Over Range/VCO Lock */
+#define REG_TX_VCO_LDO 0x288 /* TX VCO LDO */
+#define REG_TX_VCO_CAL 0x289 /* TX VCO Cal */
+#define REG_TX_LOCK_DETECT_CONFIG 0x28A /* TX Lock Detect Config */
+#define REG_TX_CP_LEVEL_DETECT 0x28B /* TX CP Level Detect */
+#define REG_TX_DSM_SETUP_0 0x28C /* TX DSM Setup 0 */
+#define REG_TX_DSM_SETUP_1 0x28D /* TX DSM Setup 1 */
+#define REG_TX_CORRECTION_WORD0 0x28E /* TX Correction Word0 */
+#define REG_TX_CORRECTION_WORD1 0x28F /* TX Correction Word1 */
+#define REG_TX_VCO_VARACTOR_CTRL_0 0x290 /* TX VCO Varactor Control 0 */
+#define REG_TX_VCO_VARACTOR_CTRL_1 0x291 /* TX VCO Varactor Control 1 */
+#define REG_DCXO_COARSE_TUNE 0x292 /* DCXO Coarse Tune */
+#define REG_DCXO_FINE_TUNE_HIGH 0x293 /* DCXO Fine Tune2 */
+#define REG_DCXO_FINE_TUNE_LOW 0x294 /* DCXO Fine Tune1 */
+#define REG_DCXO_CONFIG 0x295 /* DCXO Config */
+#define REG_DCXO_TEMPCO_WRITE 0x296 /* DCXO Tempco Write */
+#define REG_DCXO_TEMPCO_READ 0x297 /* DCXO Tempco Read */
+#define REG_DCXO_TEMPCO_ADDR 0x298 /* DCXO Tempco Addr */
+#define REG_DELTA_T_READ 0x299 /* Delta T Read */
+#define REG_TX_FAST_LOCK_SETUP 0x29A /* Tx Fast Lock Setup */
+#define REG_TX_FAST_LOCK_SETUP_INIT_DELAY 0x29B /* Tx Fast Lock Setup Init Delay */
+#define REG_TX_FAST_LOCK_PROGRAM_ADDR 0x29C /* Tx Fast Lock Program Addr */
+#define REG_TX_FAST_LOCK_PROGRAM_DATA 0x29D /* Tx Fast Lock Program Data */
+#define REG_TX_FAST_LOCK_PROGRAM_READ 0x29E /* Tx Fast Lock Program Read */
+#define REG_TX_FAST_LOCK_PROGRAM_CTRL 0x29F /* Tx Fast Lock Program Ctrl */
+#define REG_TX_LO_GEN_POWER_MODE 0x2A1 /* Tx LO Gen Power Mode */
+#define REG_BANDGAP_CONFIG0 0x2A6 /* Bandgap Config0 */
+#define REG_BANDGAP_CONFIG1 0x2A8 /* Bandgap Config1 */
+#define REG_REF_DIVIDE_CONFIG_1 0x2AB /* Ref Divide Config 1 */
+#define REG_REF_DIVIDE_CONFIG_2 0x2AC /* Ref Divide Config 2 */
+#define REG_GAIN_RX1 0x2B0 /* Gain Rx1 */
+#define REG_LPF_GAIN_RX1 0x2B1 /* LPF Gain Rx1 */
+#define REG_DIG_GAIN_RX1 0x2B2 /* Dig gain Rx1 */
+#define REG_FAST_ATTACK_STATE 0x2B3 /* Fast Attack State */
+#define REG_SLOW_LOOP_STATE 0x2B4 /* Slow Loop State */
+#define REG_GAIN_RX2 0x2B5 /* Gain Rx2 */
+#define REG_LPF_GAIN_RX2 0x2B6 /* LPF Gain Rx2 */
+#define REG_DIG_GAIN_RX2 0x2B7 /* Dig Gain Rx2 */
+#define REG_OVRG_SIGS_RX1 0x2B8 /* Ovrg Sigs Rx1 */
+#define REG_OVRG_SIGS_RX2 0x2B9 /* Ovrg Sigs Rx2 */
+#define REG_CTRL 0x3DF /* Control */
+#define REG_BIST_CONFIG 0x3F4 /* BIST Config */
+#define REG_OBSERVE_CONFIG 0x3F5 /* Observe Config */
+#define REG_BIST_AND_DATA_PORT_TEST_CONFIG 0x3F6 /* BIST and Data Port Test Config */
+#define REG_DAC_TEST_0 0x3FC /* DAC Test 0 */
+#define REG_DAC_TEST_1 0x3FD /* DAC Test 1 */
+#define REG_DAC_TEST_2 0x3FE /* DAC Test 2 */
+
+/*
+* REG_SPI_CONF
+*/
+#define SOFT_RESET (1 << 7) /* Soft Reset */
+#define WIRE3_SPI (1 << 6) /* 3-Wire SPI */
+#define LSB_FIRST (1 << 5) /* LSB First */
+#define _LSB_FIRST (1 << 2) /* LSB First */
+#define _WIRE3_SPI (1 << 1) /* 3-Wire SPI */
+#define _SOFT_RESET (1 << 0) /* Soft reset */
+
+/*
+* REG_MULTICHIP_SYNC_AND_TX_MON_CTRL
+*/
+#define TX2_MONITOR_ENABLE (1 << 6) /* Tx2 Monitor Enable */
+#define TX1_MONITOR_ENABLE (1 << 5) /* Tx1 Monitor Enable */
+#define MCS_RF_ENABLE (1 << 3) /* MCS RF Enable */
+#define MCS_BBPLL_ENABLE (1 << 2) /* MCS BBPLL enable */
+#define MCS_DIGITAL_CLK_ENABLE (1 << 1) /* MCS Digital CLK Enable */
+#define MCS_BB_ENABLE (1 << 0) /* MCS BB Enable */
+
+/*
+* REG_TX_ENABLE_FILTER_CTRL
+*/
+#define THB2_EN (1 << 3) /* THB2 Enable */
+#define THB1_EN (1 << 2) /* THB1 Enable */
+#define TX_CHANNEL_ENABLE(x) (((x) & 0x3) << 6) /* Tx channel Enable<1:0> */
+#define THB3_ENABLE_INTERP(x) (((x) & 0x3) << 4) /* THB3 Enable & Interp<1:0> */
+#define TX_FIR_ENABLE_INTERPOLATION(x) (((x) & 0x3) << 0) /* Tx FIR Enable & Interpolation<1:0> */
+#define TX_1 1
+#define TX_2 2
+#define TX_ENABLE 1
+#define TX_DISABLE 0
+
+/*
+* REG_RX_ENABLE_FILTER_CTRL
+*/
+#define RHB2_EN (1 << 3) /* RHB2 Enable */
+#define RHB1_EN (1 << 2) /* RHB1 Enable */
+#define RX_CHANNEL_ENABLE(x) (((x) & 0x3) << 6) /* Rx channel Enable<1:0> */
+#define DEC3_ENABLE_DECIMATION(x) (((x) & 0x3) << 4) /* DEC3 Enable & Decimation<1:0> */
+#define RX_FIR_ENABLE_DECIMATION(x) (((x) & 0x3) << 0) /* Rx FIR Enable & Decimation<1:0> */
+#define RX_1 1
+#define RX_2 2
+#define RX_ENABLE 1
+#define RX_DISABLE 0
+
+/*
+* REG_INPUT_SELECT
+*/
+#define TX_OUTPUT (1 << 6) /* TX Output */
+#define RX_INPUT(x) (((x) & 0x3F) << 0) /* RX Input <5:0> */
+
+/*
+* REG_RFPLL_DIVIDERS
+*/
+#define TX_VCO_DIVIDER(x) (((x) & 0xF) << 4) /* TX VCO Divider<3:0> */
+#define RX_VCO_DIVIDER(x) (((x) & 0xF) << 0) /* RX VCO Divider<3:0> */
+
+/*
+* REG_RX_CLOCK_DATA_DELAY
+*/
+#define DATA_CLK_DELAY(x) (((x) & 0xF) << 4) /* DATA_CLK Delay<3:0> */
+#define RX_DATA_DELAY(x) (((x) & 0xF) << 0) /* Rx Data Delay <3:0> */
+
+/*
+* REG_TX_CLOCK_DATA_DELAY
+*/
+#define FB_CLK_DELAY(x) (((x) & 0xF) << 4) /* FB_CLK Delay<3:0> */
+#define TX_DATA_DELAY(x) (((x) & 0xF) << 0) /* Tx Data Delay <3:0> */
+
+/*
+* REG_CLOCK_ENABLE
+*/
+#define XO_BYPASS (1 << 4) /* XO Bypass */
+#define DIGITAL_POWER_UP (1 << 2) /* Digital Power Up */
+#define CLOCK_ENABLE_DFLT (1 << 1) /* Set to 1 */
+#define BBPLL_ENABLE (1 << 0) /* BBPLL Enable */
+
+/*
+* REG_BBPLL
+*/
+#define CLKOUT_ENABLE (1 << 4) /* CLKOUT Enable */
+#define DAC_CLK_DIV2 (1 << 3) /* DAC Clk div2 */
+#define CLKOUT_SELECT(x) (((x) & 0x7) << 5) /* CLKOUT Select<2:0> */
+#define BBPLL_DIVIDER(x) (((x) & 0x7) << 0) /* BBPLL Divider <2:0> */
+
+/*
+* REG_START_TEMP_READING
+*/
+#define START_TEMP_READING (1 << 0) /* Start Temp Reading */
+
+/*
+* REG_TEMP_SENSE2
+*/
+#define TEMP_SENSE_PERIODIC_ENABLE (1 << 0) /* Temp Sense Periodic Enable */
+#define MEASUREMENT_TIME_INTERVAL(x) (((x) & 0x7F) << 1) /* Measurement Time Interval<6:0> */
+
+/*
+* REG_TEMP_SENSOR_CONFIG
+*/
+#define TEMP_SENSOR_DECIMATION(x) (((x) & 0x7) << 0) /* Temp Sensor Decimation<2:0> */
+
+/*
+* REG_PARALLEL_PORT_CONF_1
+*/
+#define PP_TX_SWAP_IQ (1 << 7) /* PP Tx Swap IQ */
+#define PP_RX_SWAP_IQ (1 << 6) /* PP Rx Swap IQ */
+#define TX_CHANNEL_SWAP (1 << 5) /* Tx Channel swap */
+#define RX_CHANNEL_SWAP (1 << 4) /* Rx Channel swap */
+#define RX_FRAME_PULSE_MODE (1 << 3) /* Rx Frame Pulse Mode */
+#define R2T2_TIMING (1 << 2) /* 2R2T Timing */
+#define INVERT_DATA_BUS (1 << 1) /* Invert data bus */
+#define INVERT_DATA_CLK (1 << 0) /* Invert DATA CLK */
+
+/*
+* REG_PARALLEL_PORT_CONF_2
+*/
+#define FDD_ALT_WORD_ORDER (1 << 7) /* FDD Alt Word Order */
+#define INVERT_RX1 (1 << 6) /* Invert Rx1 */
+#define INVERT_RX2 (1 << 5) /* Invert Rx2 */
+#define INVERT_TX1 (1 << 4) /* Invert Tx1 */
+#define INVERT_TX2 (1 << 3) /* Invert Tx2 */
+#define INVERT_RX_FRAME (1 << 2) /* Invert Rx Frame */
+#define DELAY_RX_DATA(x) (((x) & 0x3) << 0) /* Delay Rx Data<1:0> */
+
+/*
+* REG_PARALLEL_PORT_CONF_3
+*/
+#define FDD_RX_RATE_2TX_RATE (1 << 7) /* FDD Rx Rate = 2*Tx Rate */
+#define SWAP_PORTS (1 << 6) /* Swap Ports */
+#define SINGLE_DATA_RATE (1 << 5) /* Single Data Rate */
+#define LVDS_MODE (1 << 4) /* LVDS Mode */
+#define HALF_DUPLEX_MODE (1 << 3) /* Half Duplex Mode */
+#define SINGLE_PORT_MODE (1 << 2) /* Single Port Mode */
+#define FULL_PORT (1 << 1) /* Full Port */
+#define FULL_DUPLEX_SWAP_BITS (1 << 0) /* Full Duplex Swap Bits */
+
+/*
+* REG_ENSM_MODE
+*/
+#define FDD_MODE (1 << 0) /* FDD Mode */
+
+/*
+* REG_ENSM_CONFIG_1
+*/
+#define ENABLE_RX_DATA_PORT_FOR_CAL (1 << 7) /* Enable Rx Data Port for Cal */
+#define FORCE_RX_ON (1 << 6) /* Force Rx On */
+#define FORCE_TX_ON (1 << 5) /* Force Tx On */
+#define ENABLE_ENSM_PIN_CTRL (1 << 4) /* Enable ENSM Pin Control */
+#define LEVEL_MODE (1 << 3) /* Level Mode */
+#define FORCE_ALERT_STATE (1 << 2) /* Force Alert State */
+#define AUTO_GAIN_LOCK (1 << 1) /* Auto Gain Lock */
+#define TO_ALERT (1 << 0) /* To Alert */
+
+/*
+* REG_ENSM_CONFIG_2
+*/
+#define FDD_EXTERNAL_CTRL_ENABLE (1 << 7) /* FDD External Control Enable */
+#define POWER_DOWN_RX_SYNTH (1 << 6) /* Power Down Rx Synth */
+#define POWER_DOWN_TX_SYNTH (1 << 5) /* Power Down Tx Synth */
+#define TXNRX_SPI_CTRL (1 << 4) /* TXNRX SPI Control */
+#define SYNTH_ENABLE_PIN_CTRL_MODE (1 << 3) /* Synth Enable Pin Control Mode */
+#define DUAL_SYNTH_MODE (1 << 2) /* Dual Synth Mode */
+#define RX_SYNTH_READY_MASK (1 << 1) /* Rx Synth Ready Mask */
+#define TX_SYNTH_READY_MASK (1 << 0) /* Tx Synth Ready Mask */
+
+/*
+* REG_CALIBRATION_CTRL
+*/
+#define RX_BB_TUNE_CAL (1 << 7) /* Rx BB Tune */
+#define TX_BB_TUNE_CAL (1 << 6) /* Tx BB Tune */
+#define RX_QUAD_CAL (1 << 5) /* Rx Quad Cal */
+#define TX_QUAD_CAL (1 << 4) /* Tx Quad Cal */
+#define RX_GAIN_STEP_CAL (1 << 3) /* Rx Gain Step Cal */
+#define TXMON_CAL (1 << 2)
+#define RFDC_CAL (1 << 1) /* DC Cal RF Start */
+#define BBDC_CAL (1 << 0) /* DC cal BB Start */
+
+
+/*
+* REG_STATE
+*/
+#define CALIBRATION_SEQUENCE_STATE(x) (((x) & 0xF) << 4) /* Calibration Sequence State<3:0> */
+#define ENSM_STATE(x) (((x) & 0xF) << 0) /* ENSM State<3:0> */
+#define ENSM_STATE_SLEEP_WAIT 0x0
+#define ENSM_STATE_ALERT 0x5
+#define ENSM_STATE_TX 0x6
+#define ENSM_STATE_TX_FLUSH 0x7
+#define ENSM_STATE_RX 0x8
+#define ENSM_STATE_RX_FLUSH 0x9
+#define ENSM_STATE_FDD 0xA
+#define ENSM_STATE_FDD_FLUSH 0xB
+#define ENSM_STATE_INVALID 0xFF
+#define ENSM_STATE_SLEEP 0x80
+
+/*
+* REG_AUXDAC_2_WORD
+*/
+#define AUXDAC_2_WORD_MSB(x) (((x) & 0x3F) << 2) /* AuxDAC 2 Word<9:2> */
+#define AUXDAC_1_WORD(x) (((x) & 0x3) << 0) /* AuxDAC 1 Word <1:0> */
+
+/*
+* REG_AUXDAC_1_CONFIG
+*/
+#define COMP_CTRL_1 (1 << 5) /* Comp Ctrl 1 */
+#define AUXDAC1_STP_FACTOR (1 << 4) /* AuxDAC1 Step Factor */
+#define AUXDAC_1_VREF(x) (((x) & 0x3) << 2) /* AuxDAC 1 Vref<1:0> */
+#define AUXDAC_1_WORD_LSB(x) (((x) & 0x3) << 0) /* AuxDAC 2 Word <1:0> */
+
+/*
+* REG_AUXDAC_2_CONFIG
+*/
+#define COMP_CTRL_2 (1 << 5) /* Comp Ctrl 2 */
+#define AUXDAC2_STP_FACTOR (1 << 4) /* AuxDAC2 Step Factor */
+#define AUXDAC_2_VREF(x) (((x) & 0xF) << 2) /* AuxDAC 2 Vref<1:0> */
+#define AUXDAC_2_WORD_LSB(x) (((x) & 0x3) << 0) /* AuxDAC 2 Word <1:0> */
+
+/*
+* REG_AUXADC_CLOCK_DIVIDER
+*/
+#define AUXADC_CLOCK_DIVIDER(x) (((x) & 0x3F) << 0) /* AuxADC Clock Divider<5:0> */
+
+/*
+* REG_AUXADC_CONFIG
+*/
+#define AUXADC_POWER_DOWN (1 << 0) /* AuxADC Power Down */
+#define AUX_ADC_DECIMATION(x) (((x) & 0x7) << 1) /* Aux ADC Decimation<2:0> */
+
+/*
+* REG_AUXADC_LSB
+*/
+#define AUXADC_WORD_LSB(x) (((x) & 0xF) << 0) /* AuxADC Word LSB<3:0> */
+
+/*
+* REG_AUTO_GPO
+*/
+#define GPO_ENABLE_AUTO_RX(x) (((x) & 0xF) << 4) /* GPO Enable Auto Rx<3:0> */
+#define GPO_ENABLE_AUTO_TX(x) (((x) & 0xF) << 0) /* GPO Enable Auto Tx<3:0> */
+
+/*
+* REG_AGC_ATTACK_DELAY
+*/
+#define INVERT_BYPASSED_LNA_POLARITY (1 << 6) /* Invert Bypassed LNA Polarity */
+#define AGC_ATTACK_DELAY(x) (((x) & 0x3F) << 0) /* AGC Attack Delay<5:0> */
+
+/*
+* REG_AUXDAC_ENABLE_CTRL
+*/
+#define AUXDAC_MANUAL_BAR(x) (((x) & 0x3) << 6) /* AuxDac Manual Bar<1:0> */
+#define AUXDAC_AUTO_TX_BAR(x) (((x) & 0x3) << 4) /* AuxDAC Auto Tx Bar<1:0> */
+#define AUXDAC_AUTO_RX_BAR(x) (((x) & 0x3) << 2) /* AuxDAC Auto Rx Bar<1:0> */
+#define AUXDAC_INIT_BAR(x) (((x) & 0x3) << 0) /* AuxDAC Init Bar<1:0> */
+
+/*
+* REG_EXTERNAL_LNA_CTRL
+*/
+#define AUXDAC_MANUAL_SELECT (1 << 7) /* AuxDAC Manual Select */
+#define EXTERNAL_LNA2_CTRL (1 << 6) /* External LNA2 control */
+#define EXTERNAL_LNA1_CTRL (1 << 5) /* External LNA1 control */
+#define GPO_MANUAL_SELECT (1 << 4) /* GPO manual select */
+#define OPEN(x) (((x) & 0xF) << 0) /* Open<3:0> */
+
+/*
+* REG_GPO_FORCE_AND_INIT
+*/
+#define GPO_MANUAL_CTRL(x) (((x) & 0xF) << 4) /* GPO Manual Control<3:0> */
+#define GPO_INIT_STATE(x) (((x) & 0xF) << 0) /* GPO Init State<3:0> */
+
+/*
+* REG_CTRL_OUTPUT_ENABLE
+*/
+#define EN_CTRL7 (1 << 7) /* En ctrl7 */
+#define EN_CTRL6 (1 << 6) /* En ctrl6 */
+#define EN_CTRL5 (1 << 5) /* En ctrl5 */
+#define EN_CTRL4 (1 << 4) /* En ctrl4 */
+#define EN_CTRL3 (1 << 3) /* En ctrl3 */
+#define EN_CTRL2 (1 << 2) /* En ctrl2 */
+#define EN_CTRL1 (1 << 1) /* En ctrl1 */
+#define EN_CTRL0 (1 << 0) /* En ctrl0 */
+
+/*
+* REG_PRODUCT_ID
+*/
+#define PRODUCT_ID_MASK 0xF8
+#define PRODUCT_ID_9361 0x08
+#define REV_MASK 0x07
+
+/*
+* REG_REFERENCE_CLOCK_CYCLES
+*/
+#define REFERENCE_CLOCK_CYCLES_PER_US(x) (((x) & 0x7F) << 0) /* Reference Clock Cycles per us<6:0> */
+
+/*
+* REG_DIGITAL_IO_CTRL
+*/
+#define CLK_OUT_DRIVE (1 << 7) /* CLK Out Drive */
+#define DATACLK_DRIVE (1 << 6) /* DATACLK drive */
+#define DATA_PORT_DRIVE (1 << 2) /* Data Port Drive */
+#define DATACLK_SLEW(x) (((x) & 0x3) << 4) /* DATACLK slew <1:0> */
+#define DATA_PORT_SLEW(x) (((x) & 0x3) << 0) /* Data Port Slew<1:0> */
+
+/*
+* REG_LVDS_BIAS_CTRL
+*/
+#define RX_ON_CHIP_TERM (1 << 5) /* Rx On Chip Term */
+#define LVDS_BYPASS_BIAS_R (1 << 4) /* Bypass Bias R */
+#define LVDS_TX_LO_VCM (1 << 3) /* LVDS Tx LO VCM */
+#define CLK_OUT_SLEW(x) (((x) & 0x3) << 6) /* CLK Out Slew<1:0> */
+#define LVDS_BIAS(x) (((x) & 0x7) << 0) /* LVDS Bias <2:0> */
+
+/*
+* REG_SDM_CTRL_1
+*/
+#define INIT_BB_FO_CAL (1 << 2) /* Init BB FO CAL */
+#define BBPLL_RESET_BAR (1 << 0) /* BBPLL Reset Bar */
+
+/*
+* REG_CLOCK_CTRL
+*/
+#define REF_FREQ_SCALER(x) (((x) & 0x3) << 0) /* Ref Frequency Scaler */
+
+/*
+* REG_CP_CURRENT
+*/
+#define CHARGE_PUMP_CURRENT(x) (((x) & 0x3F) << 0) /* Charge Pump Current<5:0> */
+
+/*
+* REG_CP_BLEED_CURRENT
+*/
+#define MCS_REFCLK_SCALE_EN (1 << 7) /* MCS refclk Scale En */
+
+/*
+* REG_LOOP_FILTER_1
+*/
+#define C1_WORD(x) (((x) & 0x7) << 5) /* C1 Word<2:0> */
+#define R1_WORD(x) (((x) & 0x1F) << 0) /* R1 Word<4:0> */
+
+/*
+* REG_LOOP_FILTER_2
+*/
+#define R2_WORD (1 << 7) /* R2 Word<0> */
+#define C2_WORD(x) (((x) & 0x1F) << 2) /* C2 Word<4:0> */
+#define C1_WORD_LSB(x) (((x) & 0x3) << 0) /* C1 Word<4:3> */
+
+/*
+* REG_LOOP_FILTER_3
+*/
+#define BYPASS_C3 (1 << 7) /* Bypass C3 */
+#define BYPASS_R2 (1 << 6) /* Bypass R2 */
+#define C3_WORD(x) (((x) & 0xF) << 2) /* C3 Word<3:0> */
+#define R2_WORD_LSB(x) (((x) & 0x3) << 0) /* R2 Word<2:1> */
+
+/*
+* REG_VCO_CTRL
+*/
+#define FREQ_CAL_ENABLE (1 << 7) /* Freq Cal Enable */
+#define FREQ_CAL_RESET (1 << 4) /* Freq Cal Reset */
+#define FREQ_CAL_COUNT_LENGTH(x) (((x) & 0x3) << 5) /* Freq Cal Count Length<1:0> */
+
+/*
+* REG_SDM_CTRL
+*/
+#define CAL_CLOCK_DIV_4 (1 << 4) /* Cal Clock div 4 */
+
+/*
+* REG_RX_SYNTH_POWER_DOWN_OVERRIDE
+*/
+#define RX_LO_POWER_DOWN (1 << 4) /* Rx LO Power Down */
+#define RX_SYNTH_VCO_ALC_POWER_DOWN (1 << 3) /* Rx Synth VCO ALC Power Down */
+#define RX_SYNTH_PTAT_POWER_DOWN (1 << 2) /* Rx Synth PTAT Power Down */
+#define RX_SYNTH_VCO_POWER_DOWN (1 << 1) /* Rx Synth VCO Power Down */
+#define RX_SYNTH_VCO_LDO_POWER_DOWN (1 << 0) /* Rx Synth VCO LDO Power Down */
+
+/*
+* REG_TX_SYNTH_POWER_DOWN_OVERRIDE
+*/
+#define TX_LO_POWER_DOWN (1 << 4) /* Tx LO Power Down */
+#define TX_SYNTH_VCO_ALC_POWER_DOWN (1 << 3) /* Tx Synth VCO ALC Power Down */
+#define TX_SYNTH_PTAT_POWER_DOWN (1 << 2) /* Tx Synth PTAT Power Down */
+#define TX_SYNTH_VCO_POWER_DOWN (1 << 1) /* Tx Synth VCO Power Down */
+#define TX_SYNTH_VCO_LDO_POWER_DOWN (1 << 0) /* Tx Synth VCO LDO Power Down */
+
+/*
+* REG_RX_ANALOG_POWER_DOWN_OVERRIDE_1
+*/
+#define RX_OFFSET_DAC_CGIN_POWER_DOWN(x) (((x) & 0x3) << 6) /* Rx Offset DAC CGin Power Down<1:0> */
+#define RX_LMT_OVERLOAD_POWER_DOWN(x) (((x) & 0x3) << 4) /* Rx LMT Overload Power Down<1:0> */
+#define RX_MIXER_GM_POWER_DOWN(x) (((x) & 0x3) << 2) /* Rx Mixer Gm Power Down<1:0> */
+#define RX_CGB_POWER_DOWN(x) (((x) & 0x3) << 0) /* Rx CGB Power Down<1:0> */
+
+/*
+* REG_RX_ANALOG_POWER_DOWN_OVERRIDE_2
+*/
+#define RX_BBF_POWER_DOWN(x) (((x) & 0x3) << 6) /* Rx BBF Power Down<1:0> */
+#define RX_TIA_POWER_DOWN(x) (((x) & 0x3) << 4) /* Rx TIA Power Down<1:0> */
+#define RX_MIXER_POWER_DOWN(x) (((x) & 0x3) << 2) /* Rx Mixer Power Down<1:0> */
+#define RX_OFFSET_DAC_CGOUT_POWER_DOWN(x) (((x) & 0x3) << 0) /* Rx Offset DAC CGOut Power Down<1:0> */
+
+/*
+* REG_TX_ANALOG_POWER_DOWN_OVERRIDE_1
+*/
+#define TX_SECONDARY_FILTER_POWER_DOWN(x) (((x) & 0x3) << 6) /* Tx Secondary Filter Power Down<1:0> */
+#define TX_BBF_POWER_DOWN(x) (((x) & 0x3) << 4) /* Tx BBF Power Down<1:0> */
+#define TX_DAC_POWER_DOWN(x) (((x) & 0x3) << 2) /* Tx DAC Power Down<1:0> */
+#define TX_DAC_BIAS_POWER_DOWN(x) (((x) & 0x3) << 0) /* Tx DAC Bias Power Down<1:0> */
+
+/*
+* REG_ANALOG_POWER_DOWN_OVERRIDE
+*/
+#define RX_EXT_VCO_BUFFER_POWER_DOWN (1 << 5) /* Rx Ext VCO Buffer Power Down */
+#define TX_EXT_VCO_BUFFER_POWER_DOWN (1 << 4) /* Tx Ext VCO Buffer Power Down */
+#define TX_MONITOR_POWER_DOWN(x) (((x) & 0x3) << 2) /* Tx Monitor Power Down<1:0> */
+#define TX_UPCONVERTER_POWER_DOWN(x) (((x) & 0x3) << 0) /* Tx Upconverter Power Down<1:0> */
+
+/*
+* REG_MISC_POWER_DOWN_OVERRIDE
+*/
+#define RX_LNA_POWER_DOWN (1 << 6) /* Rx LNA Power Down */
+#define DCXO_POWER_DOWN (1 << 1) /* DCXO Power Down */
+#define MASTER_BIAS_POWER_DOWN (1 << 0) /* Master Bias Power Down */
+#define RX_CALIBRATION_POWER_DOWN(x) (((x) & 0x3) << 2) /* Rx Calibration Power Down<1:0> */
+
+/*
+* REG_CH_1_OVERFLOW
+*/
+#define BBPLL_LOCK (1 << 7) /* BBPLL Lock */
+#define CH_1_INT3 (1 << 6) /* CH 1 INT3 */
+#define CH1_HB3 (1 << 5) /* CH1 HB3 */
+#define CH1_HB2 (1 << 4) /* CH1 HB2 */
+#define CH1_QEC (1 << 3) /* CH1 QEC */
+#define CH1_HB1 (1 << 2) /* CH1 HB1 */
+#define CH1_TFIR (1 << 1) /* CH1 TFIR */
+#define CH1_RFIR (1 << 0) /* CH1 RFIR */
+
+/*
+* REG_CH_2_OVERFLOW
+*/
+#define CH2_INT3 (1 << 6) /* CH2 INT3 */
+#define CH2_HB3 (1 << 5) /* CH2 HB3 */
+#define CH2_HB2 (1 << 4) /* CH2 HB2 */
+#define CH2_QEC (1 << 3) /* CH2 QEC */
+#define CH2_HB1 (1 << 2) /* CH2 HB1 */
+#define CH2_TFIR (1 << 1) /* CH2 TFIR */
+#define CH2_RFIR (1 << 0) /* CH2 RFIR */
+
+/*
+* REG_TX_FILTER_CONF
+*/
+#define TX_FIR_GAIN_6DB (1 << 0) /* Filter Gain */
+#define FIR_START_CLK (1 << 1) /* Start Tx/Rx Clock */
+#define FIR_WRITE (1 << 2) /* Write Tx/Rx */
+#define FIR_SELECT(x) (((x) & 0x3) << 3) /* Select Tx/Rx CH<1:0> */
+#define FIR_NUM_TAPS(x) (((x) & 0x7) << 5) /* Number of Taps<2:0> */
+
+/*
+* REG_TX_MON_LOW_GAIN
+*/
+#define TX_MON_TRACK (1 << 5) /* Tx Mon Track */
+#define TX_MON_LOW_GAIN(x) (((x) & 0x1F) << 0) /* Tx Mon Low Gain<4:0> */
+
+/*
+* REG_TX_MON_HIGH_GAIN
+*/
+#define TX_MON_HIGH_GAIN(x) (((x) & 0x1F) << 0) /* Tx Mon High Gain<4:0> */
+
+/*
+* REG_TX_LEVEL_THRESH
+*/
+#define TX_LEVEL_THRESH(x) (((x) & 0x3F) << 2) /* Tx Level Threshold<5:0> */
+#define TX_MON_DELAY_COUNTER(x) (((x) & 0x3) << 0) /* Tx Mon Delay Counter<9:8> */
+
+/*
+* REG_TX_RSSI_LSB
+*/
+#define TX_RSSI_2 (1 << 1) /* Tx RSSI 2<0> */
+#define TX_RSSI_1 (1 << 0) /* TX RSSI 1<0> */
+
+/*
+* REG_TPM_MODE_ENABLE
+*/
+#define TX2_MON_ENABLE (1 << 7) /* Tx2 Monitor Enable */
+#define TX1_MON_ENABLE (1 << 5) /* Tx1 Monitor Enable */
+#define ONE_SHOT_MODE (1 << 6) /* One Shot Mode */
+#define TX_MON_DURATION(x) (((x) & 0xF) << 0) /* Tx Mon Duration<3:0> */
+
+/*
+* REG_TX_MON_1_CONFIG
+*/
+#define TX_MON_1_LO_CM(x) (((x) & 0x3F) << 2) /* Tx Mon 1 LO CM<5:0> */
+#define TX_MON_1_GAIN(x) (((x) & 0x3) << 0) /* Tx Mon 1 Gain<1:0> */
+
+/*
+* REG_TX_MON_2_CONFIG
+*/
+#define TX_MON_2_LO_CM(x) (((x) & 0x3F) << 2) /* Tx Mon 2 LO CM<5:0> */
+#define TX_MON_2_GAIN(x) (((x) & 0x3) << 0) /* Tx Mon 2 Gain<1:0> */
+
+/*
+* REG_TX1_ATTEN_1
+*/
+#define TX_1_ATTEN (1 << 0) /* Tx 1 Atten <8> */
+
+/*
+* REG_TX2_ATTEN_1
+*/
+#define TX_2_ATTEN (1 << 0) /* Tx 2 Atten <8> */
+
+/*
+* REG_TX_ATTEN_OFFSET
+*/
+#define MASK_CLR_ATTEN_UPDATE (1 << 6) /* Mask Clr Atten Update */
+#define TX_ATTEN_OFFSET(x) (((x) & 0x3F) << 0) /* Tx Atten Offset<5:0> */
+
+/*
+* REG_TX1_DIG_ATTEN
+*/
+#define SEL_TX1_TX2 (1 << 6) /* Sel Tx1 & Ttx2 */
+
+/*
+* REG_TX2_DIG_ATTEN
+*/
+#define IMMEDIATELY_UPDATE_TPC_ATTEN (1 << 6) /* Immediately Update TPC Atten */
+
+/*
+* REG_TX1_SYMBOL_ATTEN
+*/
+#define TX_1_SYMBOL_ATTEN(x) (((x) & 0x7F) << 0) /* Tx 1 Symbol Attenuation<6:0> */
+
+/*
+* REG_TX2_SYMBOL_ATTEN
+*/
+#define TX_2_SYMBOL_ATTEN(x) (((x) & 0x7F) << 0) /* Tx 2 Symbol Attenuation<6:0> */
+
+/*
+* REG_TX_SYMBOL_ATTEN_CONFIG
+*/
+#define USE_TX1_PIN_SYMBOL_ATTEN (1 << 3) /* Use Tx1 Pin & Symbol Atten */
+#define USE_CTRL_IN_FOR_SYMBOL_ATTEN (1 << 1) /* Use CTRL IN for symbol Atten */
+#define ENABLE_SYMBOL_ATTEN (1 << 0) /* Enable Symbol Atten */
+
+/*
+* REG_TX_FORCE_BITS
+*/
+#define FORCE_OUT_2_TX2_OFFSET (1 << 7) /* Force Out 2 Tx2 Offset */
+#define FORCE_OUT_2_TX1_OFFSET (1 << 6) /* Force Out 2 Tx1 Offset */
+#define FORCE_OUT_2_TX2_PHASE_GAIN (1 << 5) /* Force Out 2 Tx2 Phase & Gain */
+#define FORCE_OUT_2_TX1_PHASE_GAIN (1 << 4) /* Force Out 2 Tx1 Phase & Gain */
+#define FORCE_OUT_1_TX2_OFFSET (1 << 3) /* Force Out 1 Tx2 Offset */
+#define FORCE_OUT_1_TX1_OFFSET (1 << 2) /* Force Out 1 Tx1 Offset */
+#define FORCE_OUT_1_TX2_PHASE_GAIN (1 << 1) /* Force Out 1 Tx2 Phase & Gain */
+#define FORCE_OUT_1_TX1_PHASE_GAIN (1 << 0) /* Force Out 1 Tx1 Phase & Gain */
+
+/*
+* REG_QUAD_CAL_NCO_FREQ_PHASE_OFFSET
+*/
+#define RX_NCO_FREQ(x) (((x) & 0x3) << 5) /* Rx NCO Frequency<1:0> */
+#define RX_NCO_PHASE_OFFSET(x) (((x) & 0x1F) << 0) /* Rx NCO Phase Offset<4:0> */
+
+/*
+* REG_QUAD_CAL_CTRL
+*/
+#define FREE_RUN_ENABLE (1 << 7) /* Free Run Enable */
+#define SETTLE_MAIN_ENABLE (1 << 6) /* Settle Main Enable */
+#define DC_OFFSET_ENABLE (1 << 5) /* DC Offset Enable */
+#define GAIN_ENABLE (1 << 4) /* Gain Enable */
+#define PHASE_ENABLE (1 << 3) /* Phase Enable */
+#define QUAD_CAL_SOFT_RESET (1 << 2) /* Quad Cal Soft Reset */
+#define M_DECIM(x) (((x) & 0x3) << 0) /* M<1:0> */
+
+/*
+* REG_KEXP_1
+*/
+#define KEXP_TX(x) (((x) & 0x3) << 6) /* Kexp Tx<1:0> */
+#define KEXP_TX_COMP(x) (((x) & 0x3) << 4) /* Kexp Tx_comp <1:0> */
+#define KEXP_DC_I(x) (((x) & 0x3) << 2) /* Kexp DC I <1:0> */
+#define KEXP_DC_Q(x) (((x) & 0x3) << 0) /* Kexp DC Q <1:0> */
+
+/*
+* REG_KEXP_2
+*/
+#define INVERT_I_DATA (1 << 5) /* Invert I data */
+#define INVERT_Q_DATA (1 << 4) /* Invert Q data */
+#define TX_NCO_FREQ(x) (((x) & 0x3) << 6) /* Tx NCO frequency<1:0> */
+#define KEXP_PHASE(x) (((x) & 0x3) << 2) /* Kexp Phase <1:0> */
+#define KEXP_AMP(x) (((x) & 0x3) << 0) /* Kexp Amp <1:0> */
+
+/*
+* REG_QUAD_CAL_STATUS_TX1
+*/
+#define TX1_LO_CONV (1 << 1) /* Tx1 LO Conv */
+#define TX1_SSB_CONV (1 << 0) /* Tx1 SSB Conv */
+#define TX1_CONVERGENCE_COUNT(x) (((x) & 0x3F) << 2) /* Tx1 Convergence Count<5:0> */
+
+/*
+* REG_QUAD_CAL_STATUS_TX2
+*/
+#define TX2_LO_CONV (1 << 1) /* Tx2 LO Conv */
+#define TX2_SSB_CONV (1 << 0) /* Tx2 SSB Conv */
+#define TX2_CONVERGENCE_COUNT(x) (((x) & 0x3F) << 2) /* Tx2 Convergence Count<5:0> */
+
+/*
+* REG_TX_QUAD_FULL_LMT_GAIN
+*/
+#define RX_FULL_TABLELMT_TABLE_GAIN(x) (((x) & 0x7F) << 0) /* RX Full table/LMT table gain<6:0> */
+
+/*
+* REG_SQUARER_CONFIG
+*/
+#define GM_STAGE_TIME_CON_OVERRIDE (1 << 5) /* Gm Stage Time Con Override */
+#define GM_STAGE_MV_HP_POLE (1 << 4) /* Gm Stage MV HP Pole */
+#define GM_STAGE_LOWER_CM (1 << 3) /* Gm Stage Lower CM */
+#define BYPASS_BIAS_R (1 << 0) /* Bypass Bias R */
+#define VBIAS_CTRL(x) (((x) & 0x3) << 1) /* Vbias Control<1:0> */
+
+/*
+* REG_THRESH_ACCUM
+*/
+#define THRESH_ACCUMULATOR(x) (((x) & 0xF) << 0) /* Threshold Accumulator<3:0> */
+
+/*
+* REG_TX_QUAD_LPF_GAIN
+*/
+#define RX_LPF_GAIN(x) (((x) & 0x1F) << 0) /* RX LPF gain<4:0> */
+
+/*
+* REG_TXDAC_VDS_I
+*/
+#define TXDAC_VDS_I(x) (((x) & 0x3F) << 0) /* TxDAC Vds I<5:0> */
+
+/*
+* REG_TXDAC_VDS_Q
+*/
+#define TXDAC_VDS_Q(x) (((x) & 0x3F) << 0) /* TxDAC Vds Q<5:0> */
+
+/*
+* REG_TXDAC_GN_I
+*/
+#define TXDAC_GN_I(x) (((x) & 0x3F) << 0) /* txDAC_gn_I<5:0> */
+
+/*
+* REG_TXDAC_GN_Q
+*/
+#define TXDAC_GN_Q(x) (((x) & 0x3F) << 0) /* txDAC_gn_Q<5:0> */
+
+/*
+* REG_TXBBF_OPAMP_A
+*/
+#define OPAMPA_OUTPUT_BIAS(x) (((x) & 0x3) << 5) /* OpAmpA Output Bias<1:0> */
+#define OPAMPA_RZ(x) (((x) & 0x3) << 3) /* OpAmpA RZ<1:0> */
+#define OPAMP_A_CC(x) (((x) & 0x7) << 0) /* OpAmp A CC<2:0> */
+
+/*
+* REG_TXBBF_OPAMP_B
+*/
+#define OPAMPB_OUTPUT_BIAS(x) (((x) & 0x3) << 5) /* OpAmpB Output Bias<1:0> */
+#define OPAMPB_RZ(x) (((x) & 0x3) << 3) /* OpAmpB RZ<1:0> */
+#define OPAMP_B_CC(x) (((x) & 0x7) << 0) /* OpAmp B CC<2:0> */
+
+/*
+* REG_TX_BBF_R1
+*/
+#define OVERRIDE_ENABLE (1 << 7) /* Override enable */
+#define R1(x) (((x) & 0x1F) << 0) /* R1<4:0> */
+
+/*
+* REG_TX_BBF_R2
+*/
+#define R2(x) (((x) & 0x1F) << 0) /* R2<4:0> */
+
+/*
+* REG_TX_BBF_R3
+*/
+#define R3(x) (((x) & 0x1F) << 0) /* R3<4:0> */
+
+/*
+* REG_TX_BBF_R4
+*/
+#define R4(x) (((x) & 0x1F) << 0) /* R4<4:0> */
+
+/*
+* REG_TX_BBF_RP
+*/
+#define RP(x) (((x) & 0x1F) << 0) /* Rp<4:0> */
+
+/*
+* REG_TX_BBF_C1
+*/
+#define C1(x) (((x) & 0x3F) << 0) /* C1<5:0> */
+
+/*
+* REG_TX_BBF_C2
+*/
+#define C2(x) (((x) & 0x3F) << 0) /* C2<5:0> */
+
+/*
+* REG_TX_BBF_CP
+*/
+#define CP(x) (((x) & 0x3F) << 0) /* Cp<5:0> */
+
+/*
+* REG_TX_TUNE_CTRL
+*/
+#define PD_TUNE (1 << 2) /* PD Tune */
+#define TUNER_RESAMPLE (1 << 1) /* Tuner Resample */
+#define TUNER_RESAMPLE_PHASE (1 << 0) /* Tuner Resample Phase */
+#define TUNE_CTRL(x) (((x) & 0x3) << 5) /* Tune Control<1:0> */
+
+/*
+* REG_TX_BBF_R2B
+*/
+#define TX_BBF_BYPASS_BIAS_R (1 << 7) /* Bypass Bias R */
+#define R2B_OVR (1 << 5) /* R2b Ovr */
+#define R2B(x) (((x) & 0x1F) << 0) /* R2b<4:0> */
+
+/*
+* REG_TX_BBF_TUNE
+*/
+#define BBF1_COMP_I (1 << 3) /* BBF1 Comp I */
+#define BBF1_COMP_Q (1 << 2) /* BBF1 Comp Q */
+#define BBF2_COMP_I (1 << 1) /* BBF2 Comp I */
+#define BBF2_COMP_Q (1 << 0) /* BBF2 Comp Q */
+
+/*
+* REG_CONFIG0
+*/
+#define BIAS(x) (((x) & 0x3) << 6) /* Bias<1:0> */
+#define RGM(x) (((x) & 0x3) << 4) /* Rgm<1:0> */
+#define CC(x) (((x) & 0x3) << 2) /* Cc<1:0> */
+#define AMPBIAS(x) (((x) & 0x3) << 0) /* AmpBias<1:0> */
+
+/*
+* REG_RESISTOR
+*/
+#define RESISTOR(x) (((x) & 0xF) << 0) /* Resistor<3:0> */
+
+/*
+* REG_CAPACITOR
+*/
+#define CAPACITOR(x) (((x) & 0x3F) << 0) /* Capacitor<5:0> */
+
+/*
+* REG_LO_CM
+*/
+#define LO_COMMON_MODE(x) (((x) & 0x3) << 5) /* LO Common Mode<1:0> */
+
+/*
+* REG_TX_BBF_TUNE_MODE
+*/
+#define EVALTIME (1 << 4) /* EvalTime */
+#define TX_BBF_TUNE_DIVIDER (1 << 0) /* TX BBF Tune Divider<8> */
+#define TUNE_COMP_MASK(x) (((x) & 0x3) << 5) /* Tune Comp Mask<1:0> */
+#define TUNER_MODE(x) (((x) & 0x7) << 1) /* Tuner Mode<2:0> */
+
+/*
+* REG_RX_FILTER_CONFIG
+*/
+#define WRITE_RX (1 << 2) /* Write Rx */
+#define START_RX_CLOCK (1 << 1) /* Start Rx Clock */
+#define NUMBER_OF_TAPS(x) (((x) & 0x7) << 5) /* Number of Taps */
+#define SELECT_RX_CH(x) (((x) & 0x3) << 3) /* Select Rx Ch<1:0> */
+
+/*
+* REG_RX_FILTER_GAIN
+*/
+#define FILTER_GAIN(x) (((x) & 0x3) << 0) /* Filter gain<1:0> */
+
+/*
+* REG_AGC_CONFIG_1
+*/
+#define DEC_PWR_FOR_LOW_PWR (1 << 7) /* Dec Pwr for Low Pwr */
+#define DEC_PWR_FOR_LOCK_LEVEL (1 << 6) /* Dec Pwr for Lock Level */
+#define DEC_PWR_FOR_GAIN_LOCK_EXIT (1 << 5) /* Dec Pwr for Gain Lock Exit */
+#define SLOW_ATTACK_HYBRID_MODE (1 << 4) /* Slow Attack Hybrid Mode */
+#define RX2_GAIN_CTRL_SETUP(x) (((x) & 0x3) << 2) /* Rx 2 Gain Control Setup<1:0> */
+#define RX1_GAIN_CTRL_SETUP(x) (((x) & 0x3) << 0) /* Rx 1 Gain Control Setup<1:0> */
+#define RX_GAIN_CTL_MASK 0x03
+#define RX2_GAIN_CTRL_SHIFT 2
+#define RX1_GAIN_CTRL_SHIFT 0
+#define RX_GAIN_CTL_MGC 0x00
+#define RX_GAIN_CTL_AGC_FAST_ATK 0x01
+#define RX_GAIN_CTL_AGC_SLOW_ATK 0x02
+#define RX_GAIN_CTL_AGC_SLOW_ATK_HYBD 0x03
+
+/*
+* REG_AGC_CONFIG_2
+*/
+#define AGC_SOFT_RESET (1 << 7) /* Soft Reset */
+#define AGC_GAIN_UNLOCK_CTRL (1 << 6) /* Gain Unlock Control */
+#define AGC_USE_FULL_GAIN_TABLE (1 << 3) /* Use Full Gain Table */
+#define DIG_GAIN_EN (1 << 2) /* Enable Digital Gain */
+#define MAN_GAIN_CTRL_RX2 (1 << 1) /* Manual Gain Control Rx 2 */
+#define MAN_GAIN_CTRL_RX1 (1 << 0) /* Manual Gain Control Rx 1 */
+
+/*
+* REG_AGC_CONFIG_3
+*/
+#define INCDEC_LMT_GAIN (1 << 4) /* Inc/Dec LMT Gain */
+#define USE_AGC_FOR_LMTLPF_GAIN (1 << 3) /* Use AGC for LMT/LPF Gain */
+#define MANUAL_INCR_STEP_SIZE(x) (((x) & 0x7) << 5) /* Manual (CTRL_IN) Incr Gain Step Size<2:0> */
+#define ADC_OVERRANGE_SAMPLE_SIZE(x) (((x) & 0x7) << 0) /* ADC Overrange Sample Size<2:0> */
+
+/*
+* REG_MAX_LMT_FULL_GAIN
+*/
+#define MAXIMUM_FULL_TABLELMT_TABLE_INDEX(x) (((x) & 0x7F) << 0) /* Maximum Full Table/LMT Table Index<6:0> */
+
+/*
+* REG_PEAK_WAIT_TIME
+*/
+#define MANUAL_CTRL_IN_DECR_GAIN_STP_SIZE(x) (((x) & 0x7) << 5) /* Manual (CTRL_IN) Decr Gain Step Size<2:0> */
+#define PEAK_OVERLOAD_WAIT_TIME(x) (((x) & 0x1F) << 0) /* Peak Overload Wait Time<4:0> */
+
+/*
+* REG_DIGITAL_GAIN
+*/
+#define DIG_GAIN_STP_SIZE(x) (((x) & 0x7) << 5) /* Dig Gain Step Size<2:0> */
+#define MAXIMUM_DIGITAL_GAIN(x) (((x) & 0x1F) << 0) /* Maximum Digital Gain<4:0> */
+
+/*
+* REG_AGC_LOCK_LEVEL
+*/
+#define ENABLE_DIG_SAT_OVRG (1 << 7) /* Enable Dig Sat Ovrg */
+#define AGC_LOCK_LEVEL_FAST_AGC_INNER_HIGH_THRESH_SLOW(x) (((x) & 0x7F) << 0) /* AGC Lock Level (Fast)/ AGC Inner High Threshold (Slow) <6:0> */
+
+/*
+* REG_GAIN_STP_CONFIG1
+*/
+#define LMT_DETECTOR_SETTLING_TIME(x) (((x) & 0x7) << 5) /* LMT Detector Settling Time<2:0> */
+#define DEC_STP_SIZE_FOR_LARGE_LMT_OVERLOAD(x) (((x) & 0x7) << 2) /* Dec Step Size for: Large LMT Overload/ Full Table Case #3 <2:0> */
+#define ADC_NOISE_CORRECTION_FACTOR(x) (((x) & 0x3) << 0) /* ADC Noise Correction Factor<1:0> */
+
+/*
+* REG_GAIN_STP_CONFIG_2
+*/
+#define DECREMENT_STP_SIZE_FOR_SMALL_LPF_GAIN_CHANGE(x) (((x) & 0x7) << 4) /* Fast Attack Only. Decrement Step Size for: Small LPF Gain Change / Full Table Case #2 <2:0> */
+#define LARGE_LPF_GAIN_STEP(x) (((x) & 0xF) << 0) /* Decrement Step Size for: Large LPF Gain Change / Full Table Case #1<3:0> */
+
+/*
+* REG_SMALL_LMT_OVERLOAD_THRESH
+*/
+#define FORCE_PD_RESET_RX2 (1 << 7) /* Force PD Reset Rx2 */
+#define FORCE_PD_RESET_RX1 (1 << 6) /* Force PD Reset Rx1 */
+#define SMALL_LMT_OVERLOAD_THRESH(x) (((x) & 0x3F) << 0) /* Small LMT Overload Threshold<5:0> */
+
+/*
+* REG_LARGE_LMT_OVERLOAD_THRESH
+*/
+#define LARGE_LMT_OVERLOAD_THRESH(x) (((x) & 0x3F) << 0) /* Large LMT Overload Threshold<5:0> */
+
+/*
+* REG_RX1_MANUAL_LMT_FULL_GAIN
+*/
+#define POWER_MEAS_IN_STATE_5_MSB (1 << 7) /* Power Meas in State 5 <3> */
+#define RX1_MANUAL_FULL_TABLE_LMT_TABLE_GAIN_INDEX(x) (((x) & 0x7F) << 0) /* Rx1 Manual Full table/LMT table Gain Index<6:0> */
+#define RX_FULL_TBL_IDX_MASK RX1_MANUAL_FULL_TABLE_LMT_TABLE_GAIN_INDEX(~0)
+
+/*
+* REG_RX1_MANUAL_LPF_GAIN
+*/
+#define POWER_MEAS_IN_STATE_5(x) (((x) & 0x7) << 5) /* Power Meas in State 5<2:0> */
+#define RX1_MANUAL_LPF_GAIN(x) (((x) & 0x1F) << 0) /* Rx1 Manual LPF Gain <4:0> */
+#define RX_LPF_IDX_MASK RX1_MANUAL_LPF_GAIN(~0)
+
+/*
+* REG_RX1_MANUAL_DIGITALFORCED_GAIN
+*/
+#define FORCE_RX1_DIGITAL_GAIN (1 << 5) /* Force Rx1 Digital Gain */
+#define RX1_MANUALFORCED_DIGITAL_GAIN(x) (((x) & 0x1F) << 0) /* Rx1 Manual/Forced Digital Gain<4:0> */
+#define RX_DIGITAL_IDX_MASK RX1_MANUALFORCED_DIGITAL_GAIN(~0)
+/*
+* REG_RX2_MANUAL_LMT_FULL_GAIN
+*/
+#define RX2_MANUAL_FULL_TABLE_LMT_TABLE_GAIN_INDEX(x) (((x) & 0x7F) << 0) /* Rx2 Manual Full table/ LMT table Gain Index<6:0> */
+
+/*
+* REG_RX2_MANUAL_LPF_GAIN
+*/
+#define RX2_MANUAL_LPF_GAIN(x) (((x) & 0x1F) << 0) /* Rx2 Manual LPF Gain<4:0> */
+
+/*
+* REG_RX2_MANUAL_DIGITALFORCED_GAIN
+*/
+#define FORCE_RX2_DIGITAL_GAIN (1 << 5) /* Force Rx2 Digital Gain */
+#define RX2_MANUALFORCED_DIGITAL_GAIN(x) (((x) & 0x1F) << 0) /* Rx2 Manual/Forced Digital Gain<4:0> */
+
+/*
+* REG_FAST_CONFIG_1
+*/
+#define ENABLE_GAIN_INC_AFTER_GAIN_LOCK (1 << 7) /* Enable Gain Inc after Gain Lock */
+#define GOTO_OPT_GAIN_IF_ENERGY_LOST_OR_EN_AGC_HIGH (1 << 6) /* Goto Opt Gain if Energy Lost or EN_AGC High */
+#define GOTO_SET_GAIN_IF_EN_AGC_HIGH (1 << 5) /* Goto Set Gain if EN_AGC High */
+#define GOTO_SET_GAIN_IF_EXIT_RX_STATE (1 << 4) /* Goto Set Gain if Exit Rx State */
+#define DONT_UNLOCK_GAIN_IF_ENERGY_LOST (1 << 3) /* Don't Unlock Gain if Energy Lost */
+#define GOTO_OPTIMIZED_GAIN_IF_EXIT_RX_STATE (1 << 2) /* Goto Optimized Gain if Exit Rx State */
+#define DONT_UNLOCK_GAIN_IF_LG_ADC_OR_LMT_OVRG (1 << 1) /* Don't Unlock Gain If Lg ADC or LMT Ovrg */
+#define ENABLE_INCR_GAIN (1 << 0) /* Enable Incr Gain */
+
+/*
+* REG_FAST_CONFIG_2_SETTLING_DELAY
+*/
+#define USE_LAST_LOCK_LEVEL_FOR_SET_GAIN (1 << 7) /* Use Last Lock Level for Set Gain */
+#define ENABLE_LMT_GAIN_INC_FOR_LOCK_LEVEL (1 << 6) /* Enable LMT Gain Inc for Lock Level */
+#define GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH (1 << 5) /* Goto Max Gain or Opt Gain if EN_AGC High */
+#define SETTLING_DELAY(x) (((x) & 0x1F) << 0) /* Settling Delay<4:0> */
+
+/*
+* REG_FAST_ENERGY_LOST_THRESH
+*/
+#define POST_LOCK_LEVEL_STP_SIZE_FOR_LPF_TABLE_FULL_TABLE(x) (((x) & 0x3) << 6) /* Post Lock Level Step Size for: LPF Table/ Full Table <1:0> */
+#define ENERGY_LOST_THRESH(x) (((x) & 0x3F) << 0) /* Energy lost threshold<5:0> */
+
+/*
+* REG_FAST_STRONGER_SIGNAL_THRESH
+*/
+#define POST_LOCK_LEVEL_STP_FOR_LMT_TABLE(x) (((x) & 0x3) << 6) /* Post Lock Level Step for LMT Table <1:0> */
+#define STRONGER_SIGNAL_THRESH(x) (((x) & 0x3F) << 0) /* Stronger Signal Threshold<5:0> */
+
+/*
+* REG_FAST_LOW_POWER_THRESH
+*/
+#define DONT_UNLOCK_GAIN_IF_ADC_OVRG (1 << 7) /* Don't unlock gain if ADC Ovrg */
+#define LOW_POWER_THRESH(x) (((x) & 0x7F) << 0) /* Low Power Threshold<6:0> */
+
+/*
+* REG_FAST_STRONG_SIGNAL_FREEZE
+*/
+#define DONT_UNLOCK_GAIN_IF_STRONGER_SIGNAL (1 << 7) /* Don't unlock gain if Stronger Signal */
+
+/*
+* REG_FAST_FINAL_OVER_RANGE_AND_OPT_GAIN
+*/
+#define FINAL_OVER_RANGE_COUNT(x) (((x) & 0x7) << 5) /* Final Over Range Count<2:0> */
+#define OPTIMIZE_GAIN_OFFSET(x) (((x) & 0xF) << 0) /* Optimize Gain Offset<3:0> */
+
+/*
+* REG_FAST_ENERGY_DETECT_COUNT
+*/
+#define INCREMENT_GAIN_STP_LPFLMT(x) (((x) & 0x7) << 5) /* Increment Gain Step (LPF/LMT)<2:0> */
+#define ENERGY_DETECT_COUNT(x) (((x) & 0x1F) << 0) /* Energy Detect count<4:0> */
+
+/*
+* REG_FAST_AGCLL_UPPER_LIMIT
+*/
+#define AGCLL_MAX_INCREASE(x) (((x) & 0x3F) << 0) /* AGCLL Max Increase<5:0> */
+
+/*
+* REG_FAST_GAIN_LOCK_EXIT_COUNT
+*/
+#define GAIN_LOCK_EXIT_COUNT(x) (((x) & 0x3F) << 0) /* Gain Lock Exit Count<5:0> */
+
+/*
+* REG_FAST_INITIAL_LMT_GAIN_LIMIT
+*/
+#define INITIAL_LMT_GAIN_LIMIT(x) (((x) & 0x7F) << 0) /* Initial LMT Gain Limit<6:0> */
+
+/*
+* REG_AGC_INNER_LOW_THRESH
+*/
+#define PREVENT_GAIN_INC (1 << 7) /* Prevent Gain Inc */
+#define AGC_INNER_LOW_THRESH(x) (((x) & 0x7F) << 0) /* AGC Inner Low Threshold<6:0> */
+
+/*
+* REG_LMT_OVERLOAD_COUNTERS
+*/
+#define LARGE_LMT_OVERLOAD_EXED_COUNTER(x) (((x) & 0xF) << 4) /* Large LMT Overload Exceeded Counter<3:0> */
+#define SMALL_LMT_OVERLOAD_EXED_COUNTER(x) (((x) & 0xF) << 0) /* Small LMT Overload Exceeded Counter<3:0> */
+
+/*
+* REG_ADC_OVERLOAD_COUNTERS
+*/
+#define LARGE_ADC_OVERLOAD_EXED_COUNTER(x) (((x) & 0xF) << 4) /* Large ADC Overload Exceeded Counter<3:0> */
+#define SMALL_ADC_OVERLOAD_EXED_COUNTER(x) (((x) & 0xF) << 0) /* Small ADC Overload Exceeded Counter<3:0> */
+
+/*
+* REG_GAIN_STP1
+*/
+#define IMMED_GAIN_CHANGE_IF_LG_LMT_OVERLOAD (1 << 7) /* Immed. Gain Change if Lg LMT Overload */
+#define IMMED_GAIN_CHANGE_IF_LG_ADC_OVERLOAD (1 << 3) /* Immed. Gain Change if Lg ADC Overload */
+#define AGC_INNER_HIGH_THRESH_EXED_STP_SIZE(x) (((x) & 0x7) << 4) /* AGC Inner High Threshold Exceeded Step Size<2:0> */
+#define AGC_INNER_LOW_THRESH_EXED_STP_SIZE(x) (((x) & 0x7) << 0) /* AGC Inner Low Threshold Exceeded Step Size<2:0> */
+
+/*
+* REG_DIGITAL_SAT_COUNTER
+*/
+#define DOUBLE_GAIN_COUNTER (1 << 5) /* Double Gain Counter */
+#define ENABLE_SYNC_FOR_GAIN_COUNTER (1 << 4) /* Enable Sync for Gain Counter */
+#define DIG_SATURATION_EXED_COUNTER(x) (((x) & 0xF) << 0) /* Dig Saturation Exceeded Counter<3:0> */
+
+/*
+* REG_OUTER_POWER_THRESHS
+*/
+#define AGC_OUTER_HIGH_THRESH(x) (((x) & 0xF) << 4) /* AGC Outer High Threshold<3:0> */
+#define AGC_OUTER_LOW_THRESH(x) (((x) & 0xF) << 0) /* AGC Outer Low Threshold<3:0> */
+
+/*
+* REG_GAIN_STP_2
+*/
+#define AGC_OUTER_HIGH_THRESH_EXED_STP_SIZE(x) (((x) & 0xF) << 4) /* AGC outer High Threshold Exceeded Step Size<3:0> */
+#define AGC_OUTER_LOW_THRESH_EXED_STP_SIZE(x) (((x) & 0xF) << 0) /* AGC Outer Low Threshold Exceeded Step Size<3:0> */
+
+/*
+* REG_EXT_LNA_HIGH_GAIN
+*/
+#define EXT_LNA_HIGH_GAIN(x) (((x) & 0x3F) << 0) /* Ext LNA High Gain<5:0> */
+
+/*
+* REG_EXT_LNA_LOW_GAIN
+*/
+#define EXT_LNA_LOW_GAIN(x) (((x) & 0x3F) << 0) /* Ext LNA Low Gain<5:0> */
+
+/*
+* REG_GAIN_TABLE_ADDRESS
+*/
+#define GAIN_TABLE_ADDRESS(x) (((x) & 0x7F) << 0) /* Gain Table Address<6:0> */
+
+/*
+* REG_GAIN_TABLE_WRITE_DATA1
+*/
+#define EXT_LNA_CTRL (1 << 7) /* Ext LNA Ctrl */
+#define LNA_GAIN(x) (((x) & 0x3) << 5) /* LNA Gain <1:0> */
+#define MIXER_GM_GAIN(x) (((x) & 0x1F) << 0) /* Mixer Gm Gain <4:0> */
+
+/*
+* REG_GAIN_TABLE_WRITE_DATA2
+*/
+#define TIA_GAIN (1 << 5) /* TIA Gain */
+#define LPF_GAIN(x) (((x) & 0x1F) << 0) /* LPF Gain <4:0> */
+
+/*
+* REG_GAIN_TABLE_WRITE_DATA_3
+*/
+#define RF_DC_CAL (1 << 5) /* RF DC Cal */
+#define DIGITAL_GAIN(x) (((x) & 0x1F) << 0) /* Digital Gain <4:0> */
+
+/*
+* REG_GAIN_TABLE_READ_DATA_1
+*/
+#define TO_LNA_GAIN(x) (((x) >> 5) & 0x3) /* LNA Gain <1:0> */
+#define TO_MIXER_GM_GAIN(x) (((x) >> 0) & 0x1F) /* Mixer Gm Gain <4:0> */
+
+/*
+* REG_GAIN_TABLE_READ_DATA_2
+*/
+#define TO_LPF_GAIN(x) (((x) >> 0) & 0x1F) /* LPF Gain <4:0> */
+
+/*
+* REG_GAIN_TABLE_READ_DATA_3
+*/
+#define TO_DIGITAL_GAIN(x) (((x) >> 0) & 0x1F) /* Digital Gain <4:0> */
+
+/*
+* REG_GAIN_TABLE_CONFIG
+*/
+#define WRITE_GAIN_TABLE (1 << 2) /* Write Gain Table */
+#define START_GAIN_TABLE_CLOCK (1 << 1) /* Start Gain Table Clock */
+#define RECEIVER_SELECT(x) (((x) & 0x3) << 3) /* Receiver Select<1:0> */
+#define GT_RX1 1
+#define GT_RX2 2
+
+
+/*
+* REG_GM_SUB_TABLE_GAIN_WRITE
+*/
+#define GM_SUB_TABLE_GAIN_WRITE(x) (((x) & 0x7F) << 0) /* Gm Sub Table Gain Word Write<6:0> */
+
+/*
+* REG_GM_SUB_TABLE_BIAS_WRITE
+*/
+#define GM_SUB_TABLE_BIAS_WRITE(x) (((x) & 0x1F) << 0) /* Gm Sub Table Bias Word Write<4:0> */
+
+/*
+* REG_GM_SUB_TABLE_CTRL_WRITE
+*/
+#define GM_SUB_TABLE_CTRL_WRITE(x) (((x) & 0x3F) << 0) /* Gm Sub Table Control Word Write<5:0> */
+
+/*
+* REG_GM_SUB_TABLE_GAIN_READ
+*/
+#define GM_SUB_TABLE_GAIN_READ(x) (((x) & 0x7F) << 0) /* Gm Sub Table Gain Word Read<6:0> */
+
+/*
+* REG_GM_SUB_TABLE_BIAS_READ
+*/
+#define GM_SUB_TABLE_BIAS_READ(x) (((x) & 0x1F) << 0) /* Gm Sub Table Bias Word Read<4:0> */
+
+/*
+* REG_GM_SUB_TABLE_CTRL_READ
+*/
+#define GM_SUB_TABLE_CTRL_READ(x) (((x) & 0x3F) << 0) /* Gm Sub Table Control Word Read<5:0> */
+
+/*
+* REG_GM_SUB_TABLE_CONFIG
+*/
+#define WRITE_GM_SUB_TABLE (1 << 2) /* Write Gm Sub Table */
+#define START_GM_SUB_TABLE_CLOCK (1 << 1) /* Start Gm Sub Table Clock */
+
+/*
+* REG_GAIN_DIFF_WORDERROR_WRITE
+*/
+#define CALIB_TABLE_GAIN_DIFFERROR_WORD(x) (((x) & 0x3F) << 0) /* Calib Table Gain Diff/Error Word<5:0> */
+
+/*
+* REG_GAIN_ERROR_READ
+*/
+#define CALIB_TABLE_GAIN_ERROR(x) (((x) & 0x1F) << 0) /* Calib Table Gain Error<4:0> */
+
+/*
+* REG_CONFIG
+*/
+#define READ_SELECT (1 << 4) /* Read Select */
+#define WRITE_MIXER_ERROR_TABLE (1 << 3) /* Write Mixer Error Table */
+#define WRITE_LNA_ERROR_TABLE (1 << 2) /* Write LNA Error Table */
+#define WRITE_LNA_GAIN_DIFF (1 << 1) /* Write LNA Gain Diff */
+#define START_CALIB_TABLE_CLOCK (1 << 0) /* Start Calib Table Clock */
+#define CALIB_TABLE_SELECT(x) (((x) & 0x3) << 5) /* Calib Table Select<1:0> */
+
+/*
+* REG_LNA_GAIN_DIFF_READ_BACK
+*/
+#define LNA_CALIB_TABLE_GAIN_DIFFERENCE_WORD(x) (((x) & 0x3F) << 0) /* LNA Calib Table Gain Difference Word<5:0> */
+
+/*
+* REG_MAX_MIXER_CALIBRATION_GAIN_INDEX
+*/
+#define MAX_MIXER_CALIBRATION_GAIN_INDEX(x) (((x) & 0x1F) << 0) /* Max Mixer Calibration Gain Index<4:0> */
+
+/*
+* REG_SETTLE_TIME
+*/
+#define ENABLE_DIG_GAIN_CORR (1 << 7) /* Enable Dig Gain Corr */
+#define FORCE_TEMP_SENSOR_FOR_CAL (1 << 6) /* Force Temp Sensor for Cal */
+#define SETTLE_TIME(x) (((x) & 0x3F) << 0) /* Settle Time<5:0> */
+
+/*
+* REG_MEASURE_DURATION
+*/
+#define GAIN_CAL_MEAS_DURATION(x) (((x) & 0xF) << 0) /* Gain Cal Meas Duration<3:0> */
+
+/*
+* REG_MEASURE_DURATION_01
+*/
+#define MEASUREMENT_DURATION_1(x) (((x) & 0xF) << 4) /* Measurement duration 1 <3:0> */
+#define MEASUREMENT_DURATION_0(x) (((x) & 0xF) << 0) /* Measurement duration 0 <3:0> */
+
+/*
+* REG_MEASURE_DURATION_23
+*/
+#define MEASUREMENT_DURATION_3(x) (((x) & 0xF) << 4) /* Measurement duration 3 <3:0> */
+#define MEASUREMENT_DURATION_2(x) (((x) & 0xF) << 0) /* Measurement duration 2 <3:0> */
+
+/*
+* REG_RSSI_CONFIG
+*/
+#define START_RSSI_MEAS (1 << 5) /* Start RSSI Meas (Mode 4) */
+#define ENABLE_ADC_POWER_MEAS (1 << 1) /* Enable ADC Power Meas. */
+#define DEFAULT_RSSI_MEAS_MODE (1 << 0) /* Default RSSI Meas Mode */
+#define RFIR_FOR_RSSI_MEASUREMENT(x) (((x) & 0x3) << 6) /* RFIR for RSSI measurement<1:0> */
+#define RSSI_MODE_SELECT(x) (((x) & 0x7) << 2) /* RSSI Mode Select<2:0> */
+
+/*
+* REG_ADC_MEASURE_DURATION_01
+*/
+#define ADC_POWER_MEASUREMENT_DURATION_1(x) (((x) & 0xF) << 4) /* ADC Power Measurement Duration 1<3:0> */
+#define ADC_POWER_MEASUREMENT_DURATION_0(x) (((x) & 0xF) << 0) /* ADC Power Measurement Duration 0 <3:0> */
+
+/*
+* REG_DEC_POWER_MEASURE_DURATION_0
+*/
+#define USE_HB3_OUT_FOR_ADC_PWR_MEAS (1 << 7) /* Use HB3 Out for ADC Pwr Meas */
+#define USE_HB1_OUT_FOR_DEC_PWR_MEAS (1 << 6) /* Use HB1 Out for Dec pwr Meas */
+#define ENABLE_DEC_PWR_MEAS (1 << 5) /* Enable Dec Pwr Meas */
+#define DEFAULT_MODE_ADC_POWER (1 << 4) /* Default Mode ADC Power */
+#define DEC_POWER_MEASUREMENT_DURATION(x) (((x) & 0xF) << 0) /* Dec Power Measurement Duration <3:0> */
+
+/*
+* REG_LNA_GAIN
+*/
+#define DB_GAIN_READBACK_CHANNEL (1 << 0) /* dB Gain Read-back Channel */
+#define MAX_LNA_GAIN(x) (((x) & 0x7F) << 1) /* Max LNA Gain<6:0> */
+
+/*
+* REG_RX_QUAD_CAL_LEVEL
+*/
+#define RX_QUAD_CAL_LEVEL(x) (((x) & 0xF) << 0) /* Rx Quad Cal Level <3 :0> */
+
+/*
+* REG_CALIBRATION_CONFIG_1
+*/
+#define ENABLE_PHASE_CORR (1 << 7) /* Enable Phase Corr */
+#define ENABLE_GAIN_CORR (1 << 6) /* Enable Gain Corr */
+#define USE_SETTLE_COUNT_FOR_DC_CAL_WAIT (1 << 5) /* Use Settle Count for DC Cal Wait */
+#define FIXED_DC_CAL_WAIT_TIME (1 << 4) /* Fixed DC Cal Wait Time */
+#define FREE_RUN_MODE (1 << 3) /* Free Run Mode */
+#define ENABLE_CORR_WORD_DECIMATION (1 << 2) /* Enable Corr Word Decimation */
+#define ENABLE_TRACKING_MODE_CH2 (1 << 1) /* Enable Tracking Mode CH2 */
+#define ENABLE_TRACKING_MODE_CH1 (1 << 0) /* Enable Tracking Mode CH1 */
+
+/*
+* REG_CALIBRATION_CONFIG_2
+*/
+#define SOFT_RESET (1 << 7) /* Soft Reset */
+#define CALIBRATION_CONFIG2_DFLT (0x3 << 5) /* Must be 2'b11 */
+#define K_EXP_PHASE(x) (((x) & 0x1F) << 0) /* K exp Phase<4:0> */
+
+/*
+* REG_CALIBRATION_CONFIG_3
+*/
+#define PREVENT_POS_LOOP_GAIN (1 << 7) /* Prevent Pos Loop Gain */
+#define K_EXP_AMPLITUDE(x) (((x) & 0x1F) << 0) /* K exp Amplitude<4:0> */
+
+/*
+* REG_RX_QUAD_GAIN1
+*/
+#define RX_FULL_TABLELMT_TABLE_GAIN(x) (((x) & 0x7F) << 0) /* Rx Full table/LMT table gain<6:0> */
+
+/*
+* REG_RX_QUAD_GAIN2
+*/
+#define CORRECTION_WORD_DECIMATION_M(x) (((x) & 0x7) << 5) /* Correction Word Decimation M<2:0> */
+#define RX_LPF_GAIN(x) (((x) & 0x1F) << 0) /* Rx LPF gain<4:0> */
+
+/*
+* REG_RX1_INPUT_A_OFFSETS
+*/
+#define RX1_INPUT_A_I_DC_OFFSET_LSB(x) (((x) & 0x3F) << 2) /* Rx1 Input A "I" DC Offset<5:0> */
+#define RX1_INPUT_A_Q_DC_OFFSET(x) (((x) & 0x3) << 0) /* Rx1 Input A "Q" DC Offset<9:8> */
+
+/*
+* REG_INPUT_A_OFFSETS_1
+*/
+#define RX2_INPUT_A_Q_DC_OFFSET_LSB(x) (((x) & 0xF) << 4) /* Rx2 Input A "Q" DC Offset<3:0> */
+#define RX1_INPUT_A_I_DC_OFFSET_MSB(x) (((x) & 0xF) << 0) /* Rx1 Input A "I" DC Offset<9:6> */
+
+/*
+* REG_RX2_INPUT_A_OFFSETS
+*/
+#define RX2_INPUT_A_I_DC_OFFSET(x) (((x) & 0x3) << 6) /* Rx2 Input A "I" DC Offset<1:0> */
+#define RX2_INPUT_A_Q_DC_OFFSET_MSB(x) (((x) & 0x3F) << 0) /* Rx2 Input A "Q" DC Offset<9:4> */
+
+/*
+* REG_RX1_INPUT_BC_OFFSETS
+*/
+#define RX1_INPUT_BC_I_DC_OFFSET_LSB(x) (((x) & 0x3F) << 2) /* Rx1 Input B&C "I" DC Offset<5:0> */
+#define RX1_INPUT_BC_Q_DC_OFFSET(x) (((x) & 0x3) << 0) /* Rx1 Input B&C "Q" DC Offset<9:8> */
+
+/*
+* REG_INPUT_BC_OFFSETS_1
+*/
+#define RX2_INPUT_BC_Q_DC_OFFSET_LSB(x) (((x) & 0xF) << 4) /* Rx2 Input B&C "Q" DC Offset<3:0> */
+#define RX1_INPUT_BC_I_DC_OFFSET_MSB(x) (((x) & 0xF) << 0) /* Rx1 Input B&C "I" DC Offset<9:6> */
+
+/*
+* REG_RX2_INPUT_BC_OFFSETS
+*/
+#define RX2_INPUT_BC_I_DC_OFFSET(x) (((x) & 0x3) << 6) /* Rx2 Input B&C "I" DC Offset<1:0> */
+#define RX2_INPUT_BC_Q_DC_OFFSET_MSB(x) (((x) & 0x3F) << 0) /* Rx2 Input B&C "Q" DC Offset<9:4> */
+
+/*
+* REG_FORCE_BITS
+*/
+#define RX2_INPUT_BC_FORCE_OFFSET (1 << 7) /* Rx2 Input B&C Force offset */
+#define RX1_INPUT_BC_FORCE_OFFSET (1 << 6) /* Rx1 Input B&C Force offset */
+#define RX2_INPUT_BC_FORCE_PHGAIN (1 << 5) /* Rx2 Input B&C Force Ph/Gain */
+#define RX1_INPUT_BC_FORCE_PHGAIN (1 << 4) /* Rx1 Input B&C Force Ph/Gain */
+#define RX2_INPUT_A_FORCE_OFFSET (1 << 3) /* Rx2 Input A Force offset */
+#define RX1_INPUT_A_FORCE_OFFSET (1 << 2) /* Rx1 Input A Force offset */
+#define RX2_INPUT_A_FORCE_PHGAIN (1 << 1) /* Rx2 Input A Force Ph/Gain */
+#define RX1_INPUT_A_FORCE_PHGAIN (1 << 0) /* Rx1 Input A Force Ph/Gain */
+
+/*
+* REG_RF_DC_OFFSET_CONFIG_1
+*/
+#define DAC_FS(x) (((x) & 0x3) << 4) /* DAC FS<1:0> */
+#define RF_DC_CALIBRATION_COUNT(x) (((x) & 0xF) << 0) /* RF DC Calibration Count<3:0> */
+
+/*
+* REG_RF_DC_OFFSET_ATTEN
+*/
+#define RF_DC_OFFSET_TABLE_UPDATE_COUNT(x) (((x) & 0x7) << 5) /* RF DC Offset Table Update Count<2:0> */
+#define RF_DC_OFFSET_ATTEN(x) (((x) & 0x1F) << 0) /* RF DC Offset Attenuation<4:0> */
+
+/*
+* REG_INVERT_BITS
+*/
+#define INVERT_RX2_RF_DC_CGIN_WORD (1 << 7) /* Invert Rx2 RF DC CGin Word */
+#define INVERT_RX1_RF_DC_CGIN_WORD (1 << 6) /* Invert Rx1 RF DC CGin Word */
+#define INVERT_RX2_RF_DC_CGOUT_WORD (1 << 5) /* Invert Rx2 RF DC CGout Word */
+#define INVERT_RX1_RF_DC_CGOUT_WORD (1 << 4) /* Invert Rx1 RF DC CGout Word */
+
+/*
+* REG_DC_OFFSET_CONFIG2
+*/
+#define USE_WAIT_COUNTER_FOR_RF_DC_INIT_CAL (1 << 7) /* Use Wait Counter for RF DC Init Cal */
+#define ENABLE_FAST_SETTLE_MODE (1 << 6) /* Enable Fast Settle Mode */
+#define ENABLE_BB_DC_OFFSET_TRACKING (1 << 5) /* Enable BB DC Offset Tracking */
+#define RESET_ACC_ON_GAIN_CHANGE (1 << 4) /* Reset Acc on Gain Change */
+#define ENABLE_RF_OFFSET_TRACKING (1 << 3) /* Enable RF Offset Tracking */
+#define DC_OFFSET_UPDATE(x) (((x) & 0x7) << 0) /* DC Offset Update<2:0> */
+
+/*
+* REG_RF_CAL_GAIN_INDEX
+*/
+#define RF_MINIMUM_CALIBRATION_GAIN_INDEX(x) (((x) & 0x7F) << 0) /* RF Minimum Calibration Gain Index<6:0> */
+
+/*
+* REG_SOI_THRESH
+*/
+#define RF_SOI_THRESH(x) (((x) & 0x7F) << 0) /* RF SOI Threshold<6:0> */
+
+/*
+* REG_BB_DC_OFFSET_SHIFT
+*/
+#define INCREASE_COUNT_DURATION (1 << 7) /* Increase Count Duration */
+#define BB_TRACKING_DECIMATE(x) (((x) & 0x3) << 5) /* BB Tracking Decimate<1:0> */
+#define BB_DC_M_SHIFT(x) (((x) & 0x1F) << 0) /* BB DC M Shift<4:0> */
+
+/*
+* REG_BB_DC_OFFSET_FAST_SETTLE_SHIFT
+*/
+#define READ_BACK_CH_SEL (1 << 7) /* Read Back CH Sel */
+#define UPDATE_TRACKING_WORD (1 << 6) /* Update Tracking Word */
+#define FORCE_RX_NULL (1 << 5) /* Force Rx Null */
+#define BB_DC_TRACKING_FAST_SETTLE_M_SHIFT(x) (((x) & 0x1F) << 0) /* BB DC Tracking Fast Settle M Shift<4:0> */
+
+/*
+* REG_BB_DC_OFFSET_ATTEN
+*/
+#define BB_DC_OFFSET_ATTEN(x) (((x) & 0xF) << 0) /* BB DC Offset Atten<3:0> */
+
+/*
+* REG_RX1_BB_DC_WORD_I_MSB
+*/
+#define RX1_BB_DC_OFFSET_CORRECTION_WORD_I(x) (((x) & 0x7F) << 0) /* RX1 BB DC Offset Correction word I<14:8> */
+
+/*
+* REG_RX1_BB_DC_WORD_Q_MSB
+*/
+#define RX1_BB_DC_OFFSET_CORRECTION_WORD_Q(x) (((x) & 0x7F) << 0) /* RX1 BB DC Offset Correction word Q<14:8> */
+
+/*
+* REG_RX2_BB_DC_WORD_I_MSB
+*/
+#define RX2_BB_DC_OFFSET_CORRECTION_WORD_I(x) (((x) & 0x7F) << 0) /* RX2 BB DC Offset Correction word I<14:8> */
+
+/*
+* REG_RX2_BB_DC_WORD_Q_MSB
+*/
+#define RX2_BB_DC_OFFSET_CORRECTION_WORD_Q(x) (((x) & 0x7F) << 0) /* RX2 BB DC Offset Correction word Q<14:8> */
+
+/*
+* REG_BB_TRACK_CORR_WORD_I_MSB
+*/
+#define RX1RX2_BB_DC_OFFSET_TRACKING_CORRECTION_WORD_I(x) (((x) & 0x7F) << 0) /* RX1/RX2 BB DC Offset Tracking correction word I<14:8> */
+
+/*
+* REG_BB_TRACK_CORR_WORD_Q_MSB
+*/
+#define RX1RX2_BB_DC_OFFSET_TRACKING_CORRECTION_WORD_Q(x) (((x) & 0x7F) << 0) /* RX1/RX2 BB DC Offset Tracking correction word Q<14:8> */
+
+/*
+* REG_SYMBOL_LSB
+*/
+#define RX2_RSSI_SYMBOL (1 << 1) /* Rx2 RSSI symbol <0> */
+#define RX1_RSSI_SYMBOL (1 << 0) /* Rx1 RSSI symbol <0> */
+
+/*
+* REG_PREAMBLE_LSB
+*/
+#define RX2_RSSI_PREAMBLE (1 << 1) /* Rx2 RSSI preamble <0> */
+#define RX1_RSSI_PREAMBLE (1 << 0) /* Rx1 RSSI preamble <0> */
+
+/*
+* REG_RX1_RSSI_SYMBOL, REG_RX1_RSSI_PREAMBLE,
+* REG_RX2_RSSI_SYMBOL, REG_RX2_RSSI_PREAMBLE
+*/
+#define RSSI_LSB_SHIFT 1
+#define RSSI_LSB_MASK1 0x01
+#define RSSI_LSB_MASK2 0x02
+
+/*
+* REG_RX_PATH_GAIN_LSB
+*/
+#define RX_PATH_GAIN (1 << 0) /* Rx Path Gain<0> */
+
+/*
+* REG_RX_DIFF_LNA_FORCE
+*/
+#define FORCE_RX2_LNA_GAIN (1 << 7) /* Force Rx2 LNA Gain */
+#define RX2_LNA_BYPASS (1 << 6) /* Rx2 LNA Bypass */
+#define FORCE_RX1_LNA_GAIN (1 << 3) /* Force Rx1 LNA Gain */
+#define RX1_LNA_BYPASS (1 << 2) /* Rx1 LNA Bypass */
+#define RX2_LNA_GAIN(x) (((x) & 0x3) << 4) /* Rx2 LNA Gain<1:0> */
+#define RX1_LNA_GAIN(x) (((x) & 0x3) << 0) /* Rx1 LNA Gain<1:0> */
+
+/*
+* REG_RX_LNA_BIAS_COARSE
+*/
+#define RX_LNA_BIAS_COARSE(x) (((x) & 0xF) << 0) /* Rx LNA Bias Coarse<3:0> */
+
+/*
+* REG_RX_LNA_BIAS_FINE_0
+*/
+#define RX_LNA_PCASCODE_BIAS(x) (((x) & 0x7) << 5) /* Rx LNA p-Cascode Bias<2:0> */
+#define RX_LNA_BIAS(x) (((x) & 0x1F) << 0) /* Rx LNA Bias<4:0> */
+
+/*
+* REG_RX_LNA_BIAS_FINE_1
+*/
+#define RX_LNA_P_CASCODE_BIAS_FINE(x) (((x) & 0x3) << 0) /* Rx LNA p- Cascode Bias Fine<4:3> */
+
+/*
+* REG_RX_MIX_GM_CONFIG
+*/
+#define RX_MIX_GM_CM_OUT(x) (((x) & 0x7) << 5) /* Rx Mix Gm CM Out<2:0> */
+#define RX_MIX_GM_PLOAD(x) (((x) & 0x3) << 0) /* Rx Mix Gm pload <1:0> */
+
+/*
+* REG_RX1_MIX_GM_FORCE
+*/
+#define FORCE_RX1_MIX_GM (1 << 6) /* Force Rx1 Mix Gm */
+#define RX1_MIX_GM_GAIN(x) (((x) & 0x3F) << 0) /* Rx1 Mix Gm Gain<5:0> */
+
+/*
+* REG_RX1_MIX_GM_BIAS_FORCE
+*/
+#define RX1_MIX_GM_BIAS(x) (((x) & 0x1F) << 0) /* Rx1 Mix Gm Bias<4:0> */
+
+/*
+* REG_RX2_MIX_GM_FORCE
+*/
+#define FORCE_RX2_MIX_GM (1 << 6) /* Force Rx2 Mix Gm */
+#define RX2_MIX_GM_GAIN(x) (((x) & 0x3F) << 0) /* Rx2 Mix Gm Gain<5:0> */
+
+/*
+* REG_RX2_MIX_GM_BIAS_FORCE
+*/
+#define RX2_MIX_GM_BIAS(x) (((x) & 0x1F) << 0) /* Rx2 Mix Gm Bias<4:0> */
+
+/*
+* REG_INPUT_A_MSBS
+*/
+#define INPUT_A_RX1_Q(x) (((x) & 0x3) << 6) /* Input A RX1 Q<9:8> */
+#define INPUT_A_RX1_I(x) (((x) & 0x3) << 4) /* Input A RX1 I<9:8> */
+#define INPUT_A_RX2_I(x) (((x) & 0x3) << 2) /* Input A RX2 I<9:8> */
+#define INPUT_A_RX2_Q(x) (((x) & 0x3) << 0) /* Input A RX2 Q<9:8> */
+
+/*
+* REG_INPUTS_BC_MSBS
+*/
+#define INPUTS_BC_RX1_Q(x) (((x) & 0x3) << 6) /* Inputs B&C RX1 Q<9:8> */
+#define INPUTS_BC_RX1_I(x) (((x) & 0x3) << 4) /* Inputs B&C RX1 I<9:8> */
+#define INPUTS_BC_RX2_I(x) (((x) & 0x3) << 2) /* Inputs B&C RX2 I<9:8> */
+#define INPUTS_BC_RX2_Q(x) (((x) & 0x3) << 0) /* Inputs B&C RX2 Q<9:8> */
+
+/*
+* REG_FORCE_OS_DAC
+*/
+#define FORCE_CGIN_DAC (1 << 2) /* Force CGin DAC */
+
+/*
+* REG_RX_MIX_LO_CM
+*/
+#define RX_MIX_LO_CM(x) (((x) & 0x3F) << 0) /* Rx Mix LO CM<5:0> */
+
+/*
+* REG_RX_CGB_SEG_ENABLE
+*/
+#define RX_CGB_SEG_ENABLE(x) (((x) & 0x3F) << 0) /* Rx CGB Seg Enable<5:0> */
+
+/*
+* REG_RX_MIX_INPUTBIAS
+*/
+#define RX_CGB_INPUT_CM_SEL(x) (((x) & 0x3) << 4) /* Rx CGB Input CM Sel<1:0> */
+#define RX_CGB_BIAS(x) (((x) & 0xF) << 0) /* Rx CGB Bias<3:0> */
+
+/*
+* REG_RX_TIA_CONFIG
+*/
+#define TIA2_OVERRIDE_C (1 << 3) /* TIA2 Override C */
+#define TIA2_OVERRIDE_R (1 << 2) /* TIA2 Override R */
+#define TIA1_OVERRIDE_C (1 << 1) /* TIA1 Override C */
+#define TIA1_OVERRIDE_R (1 << 0) /* TIA1 Override R */
+#define TIA_SEL_CC(x) (((x) & 0x7) << 5) /* TIA Sel CC<2:0> */
+
+/*
+* REG_TIA1_C_LSB
+*/
+#define TIA1_RF(x) (((x) & 0x3) << 6) /* TIA1 RF<1:0> */
+#define TIA1_C_LSB(x) (((x) & 0x3F) << 0) /* TIA1 C LSB<5:0> */
+
+/*
+* REG_TIA1_C_MSB
+*/
+#define TIA1_C_MSB(x) (((x) & 0x7F) << 0) /* TIA1 C MSB<6:0> */
+
+/*
+* REG_TIA2_C_LSB
+*/
+#define TIA2_RF(x) (((x) & 0x3) << 6) /* TIA2 RF<1:0> */
+#define TIA2_C_LSB(x) (((x) & 0x3F) << 0) /* TIA2 C LSB<5:0> */
+
+/*
+* REG_TIA2_C_MSB
+*/
+#define TIA2_C_MSB(x) (((x) & 0x7F) << 0) /* TIA2 C MSB<6:0> */
+
+/*
+* REG_RX1_BBF_R1A
+*/
+#define FORCE_RX1_RESISTORS (1 << 7) /* Force Rx1 Resistors */
+#define RX1_BBF_R1A(x) (((x) & 0x3F) << 0) /* Rx1 BBF R1A<5:0> */
+
+/*
+* REG_RX2_BBF_R1A
+*/
+#define FORCE_RX2_RESISTORS (1 << 7) /* Force Rx2 Resistors */
+#define RX2_BBF_R1A(x) (((x) & 0x3F) << 0) /* Rx2 BBF R1A<5:0> */
+
+/*
+* REG_RX1_TUNE_CTRL
+*/
+#define RX1_TUNE_RESAMPLE_PHASE (1 << 2) /* Rx1 Tune Resample Phase */
+#define RX1_TUNE_RESAMPLE (1 << 1) /* Rx1 Tune Resample */
+#define RX1_PD_TUNE (1 << 0) /* Rx1 PD Tune */
+
+/*
+* REG_RX2_TUNE_CTRL
+*/
+#define RX2_TUNE_RESAMPLE_PHASE (1 << 2) /* Rx2 Tune Resam ple Phase */
+#define RX2_TUNE_RESAMPLE (1 << 1) /* Rx2 Tune Resample */
+#define RX2_PD_TUNE (1 << 0) /* Rx2 PD Tune */
+
+/*
+* REG_RX_BBF_R2346
+*/
+#define TUNE_OVERRIDE (1 << 7) /* Tune Override */
+#define RX_BBF_R2346(x) (((x) & 0x7) << 0) /* Rx BBF R2346<2:0> */
+
+/*
+* REG_RX_BBF_C1_MSB
+*/
+#define RX_BBF_C1_MSB(x) (((x) & 0x3F) << 0) /* Rx BBF C1 MSB<5:0> */
+
+/*
+* REG_RX_BBF_C1_LSB
+*/
+#define RX_BBF_C1_LSB(x) (((x) & 0x7F) << 0) /* Rx BBF C1 LSB<6:0> */
+
+/*
+* REG_RX_BBF_C2_MSB
+*/
+#define RX_BBF_C2_MSB(x) (((x) & 0x3F) << 0) /* Rx BBF C2 MSB<5:0> */
+
+/*
+* REG_RX_BBF_C2_LSB
+*/
+#define RX_BBF_C2_LSB(x) (((x) & 0x7F) << 0) /* Rx BBF C2 LSB<6:0> */
+
+/*
+* REG_RX_BBF_C3_MSB
+*/
+#define RX_BBF_C3_MSB(x) (((x) & 0x3F) << 0) /* Rx BBF C3 MSB<5:0> */
+
+/*
+* REG_RX_BBF_C3_LSB
+*/
+#define RX_BBF_C3_LSB(x) (((x) & 0x7F) << 0) /* Rx BBF C3 LSB<6:0> */
+
+/*
+* REG_RX_BBF_CC1_CTR
+*/
+#define RX_BBF_CC1_CTR(x) (((x) & 0x7F) << 0) /* Rx BBF CC1 Ctr<6:0> */
+
+/*
+* REG_RX_BBF_POW_RZ_BYTE0
+*/
+#define MUST_BE_ZERO (1 << 7) /* Must be zero */
+#define RX1_BBF_POW_CTR(x) (((x) & 0x3) << 5) /* Rx1 BBF Pow Ctr<1:0> */
+#define RX_BBF_RZ1_CTR(x) (((x) & 0x3) << 3) /* Rx BBF Rz1 Ctr<1:0> */
+
+/*
+* REG_RX_BBF_CC2_CTR
+*/
+#define RX_BBF_CC2_CTR(x) (((x) & 0x7F) << 0) /* Rx BBF CC2 Ctr<6:0> */
+
+/*
+* REG_RX_BBF_POW_RZ_BYTE1
+*/
+#define RX_BBF_POW3_CTR(x) (((x) & 0x3) << 6) /* Rx BBF Pow3 Ctr<1:0> */
+#define RX_BBF_RZ3_CTR(x) (((x) & 0x3) << 4) /* Rx BBF RZ3 Ctr<1:0> */
+#define RX_BBF_POW2_CTR(x) (((x) & 0x3) << 2) /* Rx BBF Pow2 Ctr<1:0> */
+#define RX_BBF_RZ2_CTR(x) (((x) & 0x3) << 0) /* Rx BBF Rz2 Ctr<1:0> */
+
+/*
+* REG_RX_BBF_CC3_CTR
+*/
+#define RX_BBF_CC3_CTR(x) (((x) & 0x7F) << 0) /* Rx BBF CC3 Ctr<6:0> */
+
+/*
+* REG_RX_BBF_TUNE
+*/
+#define RXBBF_BYPASS_BIAS_R (1 << 7) /* RxBBF Bypass Bias R */
+#define RX_BBF_R5_TUNE (1 << 4) /* Rx BBF R5 Tune */
+#define RX1_BBF_TUNE_COMP_I (1 << 3) /* Rx1 BBF Tune Comp I */
+#define RX1_BBF_TUNE_COMP_Q (1 << 2) /* Rx1 BBF Tune Comp Q */
+#define RX2_BBF_TUNE_COMP_I (1 << 1) /* Rx2 BBF Tune Comp I */
+#define RX2_BBF_TUNE_COMP_Q (1 << 0) /* Rx2 BBF Tune Comp Q */
+#define RX_BBF_TUNE_CTR(x) (((x) & 0x3) << 5) /* Rx BBF Tune Ctr<1:0> */
+
+/*
+* REG_RX1_BBF_MAN_GAIN
+*/
+#define RX1_BBF_FORCE_GAIN (1 << 5) /* Rx1 BBF Force Gain */
+#define RX1_BBF_BQ_GAIN(x) (((x) & 0x3) << 3) /* Rx1 BBF BQ Gain<1:0> */
+#define RX1_BBF_POLE_GAIN(x) (((x) & 0x7) << 0) /* Rx1 BBF Pole Gain<2:0> */
+
+/*
+* REG_RX2_BBF_MAN_GAIN
+*/
+#define RX2_BBF_FORCE_GAIN (1 << 5) /* Rx2 BBF Force Gain */
+#define RX2_BBF_BQ_GAIN(x) (((x) & 0x3) << 3) /* Rx2 BBF BQ Gain<1:0> */
+#define RX2_BBF_POLE_GAIN(x) (((x) & 0x7) << 0) /* Rx2 BBF Pole Gain<2:0> */
+
+/*
+* REG_RX_BBF_TUNE_CONFIG
+*/
+#define RX_TUNE_EVALTIME (1 << 4) /* Rx Tune Evaltime */
+#define RX_BBF_TUNE_DIVIDE (1 << 0) /* RX BBF Tune Divide<8> */
+#define TUNE_COMP_MASK(x) (((x) & 0x3) << 5) /* Tune Comp Mask <1:0> */
+#define RX_TUNE_MODE(x) (((x) & 0x7) << 1) /* Rx Tune Mode<2:0> */
+
+/*
+* REG_POLE_GAIN
+*/
+#define POLE_GAIN_TUNE(x) (((x) & 0x3) << 0) /* Pole Gain Tune<1:0> */
+
+/*
+* REG_RX_BBBW_MHZ
+*/
+#define RX_TUNE_BBBW_MHZ(x) (((x) & 0x1F) << 0) /* Rx Tune BBBW MHz<4::0> */
+
+/*
+* REG_RX_BBBW_KHZ
+*/
+#define RX_TUNE_BBBW_KHZ(x) (((x) & 0x7F) << 0) /* Rx Tune BBBW kHz<6:0> */
+
+/*
+* REG_RX_PFD_CONFIG
+*/
+#define BYPASS_LD_SYNTH (1 << 0) /* Bypass Ld Synth */
+
+/*
+* REG_RX_INTEGER_BYTE_1
+*/
+#define SYNTH_INTEGER_WORD(x) (((x) & 0x7) << 0) /* Synthesizer Integer Word<10:8> */
+
+/*
+* REG_RX_FRACT_BYTE_2
+*/
+#define SYNTH_FRACT_WORD(x) (((x) & 0x7F) << 0) /* Synthesizer Fractional Word <22:16> */
+
+/*
+* REG_RX_FORCE_VCO_TUNE_1
+*/
+#define VCO_CAL_OFFSET(x) (((x) & 0xF) << 3) /* VCO Cal Offset<3:0> */
+
+/*
+* REG_RX_ALC_VARACTOR
+*/
+#define INIT_ALC_VALUE(x) (((x) & 0xF) << 4) /* Init ALC Value<3:0> */
+#define VCO_VARACTOR(x) (((x) & 0xF) << 0) /* VCO Varactor<3:0> */
+
+/*
+* REG_RX_VCO_OUTPUT
+*/
+#define PORB_VCO_LOGIC (1 << 6) /* PORb VCO Logic */
+#define VCO_OUTPUT_LEVEL(x) (((x) & 0xF) << 0) /* VCO Output Level<3:0> */
+
+/*
+* REG_RX_CP_CURRENT
+*/
+#define CHARGE_PUMP_CURRENT(x) (((x) & 0x3F) << 0) /* Charge Pump Current<5:0> */
+
+/*
+* REG_RX_CP_OFFSET
+*/
+#define SYNTH_RECAL (1 << 7) /* Synth Re-Cal */
+
+/*
+* REG_RX_CP_CONFIG
+*/
+#define HALF_VCO_CAL_CLK (1 << 7) /* Half Vco Cal Clk */
+#define F_CPCAL (1 << 3) /* F Cpcal */
+#define CP_CAL_ENABLE (1 << 2) /* Cp Cal Enable */
+
+/*
+* REG_RX_LOOP_FILTER_1
+*/
+#define LOOP_FILTER_C2(x) (((x) & 0xF) << 4) /* Loop Filter C2<3:0> */
+#define LOOP_FILTER_C1(x) (((x) & 0xF) << 0) /* Loop Filter C1<3:0> */
+
+/*
+* REG_RX_LOOP_FILTER_2
+*/
+#define LOOP_FILTER_R1(x) (((x) & 0xF) << 4) /* Loop Filter R1<3:0> */
+#define LOOP_FILTER_C3(x) (((x) & 0xF) << 0) /* Loop Filter C3<3:0> */
+
+/*
+* REG_RX_LOOP_FILTER_3
+*/
+#define LOOP_FILTER_BYPASS_R3 (1 << 7) /* Loop Filter Bypass R3 */
+#define LOOP_FILTER_BYPASS_R1 (1 << 6) /* Loop Filter Bypass R1 */
+#define LOOP_FILTER_BYPASS_C2 (1 << 5) /* Loop Filter Bypass C2 */
+#define LOOP_FILTER_BYPASS_C1 (1 << 4) /* Loop Filter Bypass C1 */
+#define LOOP_FILTER_R3(x) (((x) & 0xF) << 0) /* Loop Filter R3<3:0> */
+
+/*
+* REG_RX_DITHERCP_CAL
+*/
+#define FORCED_CP_CAL_WORD(x) (((x) & 0xF) << 0) /* Forced CP Cal Word<3:0> */
+
+/*
+* REG_RX_VCO_BIAS_1
+*/
+#define VCO_BIAS_TCF(x) (((x) & 0x3) << 3) /* VCO Bias Tcf<1:0> */
+#define VCO_BIAS_REF(x) (((x) & 0x7) << 0) /* VCO Bias Ref<2:0> */
+
+/*
+* REG_RX_CAL_STATUS
+*/
+#define CP_CAL_VALID (1 << 7) /* CP Cal Valid */
+#define CP_CAL_DONE (1 << 5) /* CP Cal Done */
+#define VCO_CAL_BUSY (1 << 4) /* VCO Cal Busy */
+#define CP_CAL_WORD(x) (((x) & 0xF) << 0) /* CP Cal Word<3:0> */
+
+/*
+* REG_RX_VCO_CAL_REF
+*/
+#define VCO_CAL_REF_TCF(x) (((x) & 0x7) << 0) /* VCO Cal Ref Tcf<2:0> */
+
+/*
+* REG_RX_VCO_PD_OVERRIDES
+*/
+#define POWER_DOWN_VARACTOR_REF (1 << 3) /* Power Down Varactor Ref */
+#define PWR_DOWN_VARACT_REF_TCF (1 << 2) /* Pwr Down Varact Ref Tcf */
+#define POWER_DOWN_CAL_TCF (1 << 1) /* Power Down Cal Tcf */
+#define POWER_DOWN_VCO_BUFFFER (1 << 0) /* Power Down VCO Bufffer */
+
+/*
+* REG_RX_CP_OVERRANGE_VCO_LOCK
+*/
+#define CP_OVRG_HIGH (1 << 7) /* CP Ovrg High */
+#define CP_OVRG_LOW (1 << 6) /* CP Ovrg Low */
+#define VCO_LOCK (1 << 1) /* Lock */
+
+/*
+* REG_RX_VCO_LDO
+*/
+#define VCO_LDO_BYPASS (1 << 7) /* VCO LDO Bypass */
+#define VCO_LDO_INRUSH(x) (((x) & 0x3) << 5) /* VCO LDO Inrush<1:0> */
+#define VCO_LDO_SEL(x) (((x) & 0x7) << 2) /* VCO LDO Sel<2:0> */
+#define VCO_LDO_VDROP_SEL(x) (((x) & 0x3) << 0) /* VCO LDO Vdrop Sel<1:0> */
+
+/*
+* REG_RX_VCO_CAL
+*/
+#define VCO_CAL_EN (1 << 7) /* VCO Cal En */
+#define VCO_CAL_ALC_WAIT(x) (((x) & 0x7) << 4) /* VCO Cal ALC Wait <2:0> */
+#define VCO_CAL_COUNT(x) (((x) & 0x3) << 2) /* VCO Cal Count <1:0> */
+
+/*
+* REG_RX_LOCK_DETECT_CONFIG
+*/
+#define LOCK_DETECT_COUNT(x) (((x) & 0x3) << 2) /* Lock Detect Count<1:0> */
+#define LOCK_DETECT_MODE(x) (((x) & 0x3) << 0) /* Lock Detect Mode<1:0> */
+
+/*
+* REG_RX_CP_LEVEL_DETECT
+*/
+#define CP_LEVEL_DETECT_POWER_DOWN (1 << 6) /* CP Level Detect Power Down */
+#define CP_LEVEL_THRESH_LOW(x) (((x) & 0x7) << 3) /* CP Level Threshold Low<2:0> */
+#define CP_LEVEL_THRESH_HIGH(x) (((x) & 0x7) << 0) /* CP Level Threshold High<2:0> */
+
+/*
+* REG_RX_DSM_SETUP_0
+*/
+#define DSM_PROG(x) (((x) & 0xF) << 0) /* DSM Prog<3:0> */
+
+/*
+* REG_RX_DSM_SETUP_1
+*/
+#define SIF_CLOCK (1 << 6) /* SIF clock */
+#define SIF_RESET_BAR (1 << 5) /* SIF Reset Bar */
+#define SIF_ADDR(x) (((x) & 0x1F) << 0) /* SIF Addr<4:0> */
+
+/*
+* REG_RX_CORRECTION_WORD0
+*/
+#define UPDATE_FREQ_WORD (1 << 7) /* Update Freq Word */
+#define READ_EFFECTIVE_TUNING_WORD (1 << 5) /* Read Effective Tuning Word */
+#define FREQ_CORRECTION_WORD_MSB(x) (((x) & 0x1F) << 0) /* Frequency Correction Word<11:7> */
+
+/*
+* REG_RX_CORRECTION_WORD1
+*/
+#define UPDATE_FREQ_WORD (1 << 7) /* Update Freq Word */
+#define FREQ_CORRECTION_WORD_LSB(x) (((x) & 0x7F) << 0) /* Frequency Correction Word<6:0> */
+
+/*
+* REG_RX_VCO_VARACTOR_CTRL_0
+*/
+#define VCO_VARACTOR_REFERENCE_TCF(x) (((x) & 0x7) << 4) /* VCO Varactor Reference Tcf<2:0> */
+#define VCO_VARACTOR_OFFSET(x) (((x) & 0xF) << 0) /* VCO Varactor Offset<3:0> */
+
+/*
+* REG_RX_VCO_VARACTOR_CTRL_1
+*/
+#define VCO_VARACTOR_REFERENCE(x) (((x) & 0xF) << 0) /* VCO Varactor Reference<3:0> */
+
+/*
+* REG_RX_FAST_LOCK_SETUP
+*/
+#define RX_FAST_LOCK_LOAD_SYNTH (1 << 3) /* Rx Fast Lock Load Synth */
+#define RX_FAST_LOCK_PROFILE_INIT (1 << 2) /* Rx Fast Lock Profile Init */
+#define RX_FAST_LOCK_PROFILE_PIN_SELECT (1 << 1) /* Rx Fast Lock Profile Pin Select */
+#define RX_FAST_LOCK_MODE_ENABLE (1 << 0) /* Rx Fast Lock Mode Enable */
+#define RX_FAST_LOCK_PROFILE(x) (((x) & 0x7) << 5) /* Rx Fast Lock Profile<2:0> */
+
+/*
+* REG_RX_FAST_LOCK_PROGRAM_ADDR
+*/
+#define RX_FAST_LOCK_PROFILE_ADDR(x) (((x) & 0x7) << 4) /* Rx Fast Lock Profile<2:0> */
+#define RX_FAST_LOCK_PROFILE_WORD(x) (((x) & 0xF) << 0) /* Configuration Word <3:0> */
+
+
+/*
+* REG_RX_FAST_LOCK_PROGRAM_CTRL
+*/
+#define RX_FAST_LOCK_PROGRAM_WRITE (1 << 1) /* Rx Fast Lock Program Write */
+#define RX_FAST_LOCK_PROGRAM_CLOCK_ENABLE (1 << 0) /* Rx Fast Lock Program Clock Enable */
+
+#define RX_FAST_LOCK_CONFIG_WORD_NUM 16
+
+/*
+* REG_RX_LO_GEN_POWER_MODE
+*/
+#define RX_LO_GEN_POWER_MODE(x) (((x) & 0x3) << 4) /* Power Mode<3:0> */
+
+/*
+* REG_TX_PFD_CONFIG
+*/
+#define DIV_TEST_EN (1 << 5) /* Div Test En */
+#define PFD_CLK_EDGE (1 << 1) /* PFD Clk Edge */
+#define BYPASS_LD_SYNTH (1 << 0) /* Bypass Ld Synth */
+#define PFD_WIDTH(x) (((x) & 0x3) << 2) /* PFD Width <1:0> */
+
+/*
+* REG_TX_INTEGER_BYTE_1
+*/
+#define SDM_BYPASS (1 << 7) /* SDM Bypass */
+#define SDM_POWER_DOWN (1 << 6) /* SDM Power Down */
+#define SYNTH_INTEGER_WORD(x) (((x) & 0x7) << 0) /* Synthesizer Integer Word<10:8> */
+
+/*
+* REG_TX_FRACT_BYTE_2
+*/
+#define SYNTH_FRACT_WORD(x) (((x) & 0x7F) << 0) /* Synthesizer Fractional Word <22:16> */
+
+/*
+* REG_TX_FORCE_ALC
+*/
+#define FORCE_ALC_ENABLE (1 << 7) /* Force ALC Enable */
+#define FORCE_ALC_WORD(x) (((x) & 0x7F) << 0) /* Force ALC Word<6:0> */
+
+/*
+* REG_TX_FORCE_VCO_TUNE_1
+*/
+#define BYPASS_LOAD_DELAY (1 << 7) /* Bypass Load Delay */
+#define FORCE_VCO_TUNE_ENABLE (1 << 1) /* Force VCO Tune Enable */
+#define FORCE_VCO_TUNE (1 << 0) /* Force VCO Tune */
+#define VCO_CAL_OFFSET(x) (((x) & 0xF) << 3) /* VCO Cal Offset<3:0> */
+
+/*
+* REG_TX_ALCVARACT_OR
+*/
+#define INIT_ALC_VALUE(x) (((x) & 0xF) << 4) /* Init ALC Value<3:0> */
+#define VCO_VARACTOR(x) (((x) & 0xF) << 0) /* VCO Varactor<3:0> */
+
+/*
+* REG_TX_VCO_OUTPUT
+*/
+#define PORB_VCO_LOGIC (1 << 6) /* PORb VCO Logic */
+#define VCO_OUTPUT_LEVEL(x) (((x) & 0xF) << 0) /* VCO Output Level<3:0> */
+
+/*
+* REG_TX_CP_CURRENT
+*/
+#define TX_CP_CURRENT_DFLT (1 << 7) /* Set to 1 */
+#define VTUNE_FORCE (1 << 6) /* Vtune Force */
+#define CHARGE_PUMP_CURRENT(x) (((x) & 0x3F) << 0) /* Charge Pump Current<5:0> */
+
+/*
+* REG_TX_CP_OFFSET
+*/
+#define SYNTH_RECAL (1 << 7) /* Synth Re-Cal */
+#define CHARGE_PUMP_OFFSET(x) (((x) & 0x3F) << 0) /* Charge Pump Offset<5:0> */
+
+/*
+* REG_TX_CP_CONFIG
+*/
+#define HALF_VCO_CAL_CLK (1 << 7) /* Half Vco Cal Clk */
+#define DITHER_MODE (1 << 6) /* Dither Mode */
+#define CP_OFFSET_OFF (1 << 4) /* Cp Offset Off */
+#define F_CPCAL (1 << 3) /* F Cpcal */
+#define CP_CAL_ENABLE (1 << 2) /* Cp Cal Enable */
+#define CP_TEST(x) (((x) & 0x3) << 0) /* Cp Test <1:0> */
+
+/*
+* REG_TX_LOOP_FILTER_1
+*/
+#define LOOP_FILTER_C2(x) (((x) & 0xF) << 4) /* Loop Filter C2<3:0> */
+#define LOOP_FILTER_C1(x) (((x) & 0xF) << 0) /* Loop Filter C1<3:0> */
+
+/*
+* REG_TX_LOOP_FILTER_2
+*/
+#define LOOP_FILTER_R1(x) (((x) & 0xF) << 4) /* Loop Filter R1<3:0> */
+#define LOOP_FILTER_C3(x) (((x) & 0xF) << 0) /* Loop Filter C3<3:0> */
+
+/*
+* REG_TX_LOOP_FILTER_3
+*/
+#define LOOP_FILTER_BYPASS_R3 (1 << 7) /* Loop Filter Bypass R3 */
+#define LOOP_FILTER_BYPASS_R1 (1 << 6) /* Loop Filter Bypass R1 */
+#define LOOP_FILTER_BYPASS_C2 (1 << 5) /* Loop Filter Bypass C2 */
+#define LOOP_FILTER_BYPASS_C1 (1 << 4) /* Loop Filter Bypass C1 */
+#define LOOP_FILTER_R3(x) (((x) & 0xF) << 0) /* Loop Filter R3<3:0> */
+
+/*
+* REG_TX_DITHERCP_CAL
+*/
+#define NUMBER_SDM_DITHER_BITS(x) (((x) & 0xF) << 4) /* Number SDM Dither Bits<3:0> */
+#define FORCED_CP_CAL_WORD(x) (((x) & 0xF) << 0) /* Forced CP Cal Word<3:0> */
+
+/*
+* REG_TX_VCO_BIAS_1
+*/
+#define MUST_BE_ZEROS(x) (((x) & 0x3) << 5) /* Must be zeros */
+#define VCO_BIAS_TCF(x) (((x) & 0x3) << 3) /* VCO Bias Tcf<1:0> */
+#define VCO_BIAS_REF(x) (((x) & 0x7) << 0) /* VCO Bias Ref<2:0> */
+
+/*
+* REG_TX_VCO_BIAS_2
+*/
+#define VCO_BYPASS_BIAS_DAC_R (1 << 7) /* VCO Bypass Bias DAC R */
+#define VCO_COMP_BYPASS_BIAS_R (1 << 4) /* VCO Comp Bypass Bias R */
+#define BYPASS_PRESCALE_R (1 << 3) /* Bypass Prescale R */
+#define LAST_ALC_ENABLE (1 << 2) /* Last ALC Enable */
+#define PRESCALE_BIAS(x) (((x) & 0x3) << 0) /* Prescale Bias <1:0> */
+
+/*
+* REG_TX_CAL_STATUS
+*/
+#define CP_CAL_VALID (1 << 7) /* CP Cal Valid */
+#define COMP_OUT (1 << 6) /* Comp Out */
+#define CP_CAL_DONE (1 << 5) /* CP Cal Done */
+#define VCO_CAL_BUSY (1 << 4) /* VCO Cal Busy */
+#define CP_CAL_WORD(x) (((x) & 0xF) << 0) /* CP Cal Word<3:0> */
+
+/*
+* REG_TX_VCO_CAL_REF
+*/
+#define VCO_CAL_REF_MONITOR (1 << 3) /* VCO Cal Ref Monitor */
+#define VCO_CAL_REF_TCF(x) (((x) & 0x7) << 0) /* VCO Cal Ref Tcf<2:0> */
+
+/*
+* REG_TX_VCO_PD_OVERRIDES
+*/
+#define POWER_DOWN_VARACTOR_REF (1 << 3) /* Power Down Varactor Ref */
+#define POWER_DOWN_VARACT_REF_TCF (1 << 2) /* Power Down Varact Ref Tcf */
+#define POWER_DOWN_CAL_TCF (1 << 1) /* Power Down Cal Tcf */
+#define POWER_DOWN_VCO_BUFFFER (1 << 0) /* Power Down VCO Bufffer */
+
+/*
+* REG_TX_CP_OVERRANGE_VCO_LOCK
+*/
+#define CP_OVRG_HIGH (1 << 7) /* CP Ovrg High */
+#define CP_OVRG_LOW (1 << 6) /* CP Ovrg Low */
+#define VCO_LOCK (1 << 1) /* Lock */
+
+/*
+* REG_TX_VCO_LDO
+*/
+#define VCO_LDO_BYPASS (1 << 7) /* VCO LDO Bypass */
+#define VCO_LDO_INRUSH(x) (((x) & 0x3) << 5) /* VCO LDO Inrush<1:0> */
+#define VCO_LDO_VOUT_SEL(x) (((x) & 0x7) << 2) /* VCO LDO Vout Sel<2:0> */
+#define VCO_LDO_VDROP_SEL(x) (((x) & 0x3) << 0) /* VCO LDO Vdrop Sel<1:0> */
+
+/*
+* REG_TX_VCO_CAL
+*/
+#define VCO_CAL_EN (1 << 7) /* VCO Cal En */
+#define VCO_CAL_ALC_WAIT(x) (((x) & 0x7) << 4) /* VCO Cal ALC Wait<2:0) */
+#define VCO_CAL_COUNT(x) (((x) & 0x3) << 2) /* VCO Cal Count<1:0> */
+#define FB_CLOCK_ADV(x) (((x) & 0x3) << 0) /* FB Clock Adv<1:0> */
+
+/*
+* REG_TX_LOCK_DETECT_CONFIG
+*/
+#define LOCK_DETECT_COUNT(x) (((x) & 0x3) << 2) /* Lock Detect Count<1:0> */
+#define LOCK_DETECT_MODE(x) (((x) & 0x3) << 0) /* Lock Detect Mode<1:0> */
+
+/*
+* REG_TX_CP_LEVEL_DETECT
+*/
+#define CP_LEVEL_DETECT_POWER_DOWN (1 << 6) /* CP Level Detect Power Down */
+#define CP_LEVEL_DETECT_THRESH_LOW(x) (((x) & 0x7) << 3) /* CP Level Detect Threshold Low<2:0> */
+#define CP_LEVEL_DETECT_THRESH_HIGH(x) (((x) & 0x7) << 0) /* CP Level Detect Threshold High<2:0> */
+
+/*
+* REG_TX_DSM_SETUP_0
+*/
+#define DSM_PROG(x) (((x) & 0xF) << 0) /* DSM Prog<3:0> */
+
+/*
+* REG_TX_DSM_SETUP_1
+*/
+#define SIF_CLOCK (1 << 6) /* SIF clock */
+#define SIF_RESET_BAR (1 << 5) /* SIF Reset Bar */
+#define SIF_ADDR(x) (((x) & 0x1F) << 0) /* SIF Addr<4:0> */
+
+/*
+* REG_TX_CORRECTION_WORD0
+*/
+#define UPDATE_FREQ_WORD (1 << 7) /* Update Freq Word */
+#define READ_EFFECTIVE_TUNING_WORD (1 << 5) /* Read Effective Tuning Word */
+#define FREQ_CORRECTION_WORD_MSB(x) (((x) & 0x1F) << 0) /* Frequency Correction Word<11:7> */
+
+/*
+* REG_TX_CORRECTION_WORD1
+*/
+#define UPDATE_FREQ_WORD (1 << 7) /* Update Freq Word */
+#define FREQ_CORRECTION_WORD_LSB(x) (((x) & 0x7F) << 0) /* Frequency Correction Word<6:0> */
+
+/*
+* REG_TX_VCO_VARACTOR_CTRL_0
+*/
+#define VCO_VARACTOR_REFERENCE_TCF(x) (((x) & 0x7) << 4) /* VCO Varactor Reference Tcf<2:0> */
+#define VCO_VARACTOR_OFFSET(x) (((x) & 0xF) << 0) /* VCO Varactor Offset<3:0> */
+
+/*
+* REG_TX_VCO_VARACTOR_CTRL_1
+*/
+#define VCO_VARACTOR_REFERENCE(x) (((x) & 0xF) << 0) /* VCO Varactor Reference<3:0> */
+
+/*
+* REG_DCXO_COARSE_TUNE
+*/
+#define DCXO_TUNE_COARSE(x) (((x) & 0x3F) << 0) /* DCXO Tune Coarse<5:0> */
+
+/*
+* REG_DCXO_FINE_TUNE_LOW
+*/
+#define DCXO_TUNE_FINE_LOW(x) (((x) & 0x1F) << 3) /* DCXO Tune Fine<4:0> */
+
+/*
+* REG_DCXO_FINE_TUNE_HIGH
+*/
+#define DCXO_TUNE_FINE_HIGH(x) ((x) >> 5) /* DCXO Tune Fine<12:5> */
+
+/*
+* REG_DCXO_CONFIG
+*/
+#define MUST_BE_ZERO (1 << 7) /* Must be zero */
+#define DCXO_RTAIL(x) (((x) & 0x7) << 4) /* DCXO Rtail<2:0> */
+#define DCXO_RD(x) (((x) & 0x3) << 2) /* DCXO Rd<1:0> */
+
+/*
+* REG_DCXO_TEMPCO_ADDR
+*/
+#define DCXO_TEMPCO_EN (1 << 7) /* DCXO Tempco En */
+#define DCXO_TEMPCO_CLK (1 << 6) /* DCXO Tempco Clk */
+#define DCXO_TEMPERATURE_COEF_ADDRESS(x) (((x) & 0x3F) << 0) /* DCXO Temperature Coefficient Address<5:0> */
+
+/*
+* REG_TX_FAST_LOCK_SETUP
+*/
+#define TX_FAST_LOCK_LOAD_SYNTH (1 << 3) /* Tx Fast Lock Load Synth */
+#define TX_FAST_LOCK_PROFILE_INIT (1 << 2) /* Tx Fast Lock Profile Init */
+#define TX_FAST_LOCK_PROFILE_PIN_SELECT (1 << 1) /* Tx Fast Lock Profile Pin Select */
+#define TX_FAST_LOCK_MODE_ENABLE (1 << 0) /* Tx Fast Lock Mode Enable */
+#define TX_FAST_LOCK_PROFILE(x) (((x) & 0x7) << 5) /* Tx Fast Lock Profile<2:0> */
+
+/*
+* REG_TX_FAST_LOCK_PROGRAM_CTRL
+*/
+#define TX_FAST_LOCK_PROGRAM_WRITE (1 << 1) /* Tx Fast Lock Program Write */
+#define TX_FAST_LOCK_PROGRAM_CLOCK_ENABLE (1 << 0) /* Tx Fast Lock Program Clock Enable */
+
+/*
+* REG_TX_LO_GEN_POWER_MODE
+*/
+#define TX_LO_GEN_POWER_MODE(x) (((x) & 0xF) << 4) /* Power Mode<3:0> */
+
+/*
+* REG_BANDGAP_CONFIG0
+*/
+#define POWER_DOWN_BANDGAP_REF (1 << 7) /* Power Down Bandgap Ref */
+#define MASTER_BIAS_FILTER_BYPASS (1 << 6) /* Master Bias Filter Bypass */
+#define MASTER_BIAS_REF_SEL (1 << 5) /* Master Bias Ref Sel */
+#define MASTER_BIAS_TRIM(x) (((x) & 0x1F) << 0) /* Master Bias Trim<4:0> */
+
+/*
+* REG_BANDGAP_CONFIG1
+*/
+#define VCO_LDO_FILTER_BYPASS (1 << 7) /* VCO LDO Filter Bypass */
+#define VCO_LDO_REF_SEL (1 << 6) /* VCO LDO Ref Sel */
+#define BANDGAP_REF_RESET (1 << 5) /* Bandgap Ref Reset */
+#define BANDGAP_TEMP_TRIM(x) (((x) & 0x1F) << 0) /* Bandgap Temp Trim<4:0> */
+
+/*
+* REG_REF_DIVIDE_CONFIG_1
+*/
+#define REF_DIVIDE_CONFIG_1_DFLT (1 << 2) /* Set to 1 */
+#define RX_REF_RESET_BAR (1 << 1) /* Rx Ref Reset Bar */
+#define RX_REF_DIVIDER_MSB (1 << 0) /* Rx Ref Divider<1> */
+
+/*
+* REG_REF_DIVIDE_CONFIG_2
+*/
+#define RX_REF_DIVIDER_LSB (1 << 7) /* Rx Ref Divider< 0> */
+#define TX_REF_RESET_BAR (1 << 4) /* Tx Ref Reset Bar */
+#define RX_REF_DOUBLER_FB_DELAY(x) (((x) & 0x3) << 5) /* Rx Ref Doubler FB Delay<1:0> */
+#define TX_REF_DIVIDER(x) (((x) & 0x3) << 2) /* Tx Ref Divider<1:0> */
+#define TX_REF_DOUBLER_FB_DELAY(x) (((x) & 0x3) << 0) /* Tx Ref Doubler FB Delay<1:0> */
+
+/*
+* REG_GAIN_RX1,2
+*/
+#define FULL_TABLE_GAIN_INDEX(x) (((x) & 0x7F) << 0) /* Full Table Gain Index Rx1/LMT Gain Rx1<6:0> */
+
+/*
+* REG_LPF_GAIN_RX1,2
+*/
+#define LPF_GAIN_RX(x) (((x) & 0x1F) << 0) /* LPF gain Rx1<4:0> */
+
+/*
+* REG_DIG_GAIN_RX1,2
+*/
+#define DIGITAL_GAIN_RX(x) (((x) & 0x1F) << 0) /* Digital gain Rx1<4:0> */
+
+/*
+* REG_FAST_ATTACK_STATE
+*/
+#define FAST_ATTACK_STATE_RX2(x) (((x) & 0x7) << 4) /* Fast Attack State Rx2<2:0> */
+#define FAST_ATTACK_STATE_RX1(x) (((x) & 0x7) << 0) /* Fast Attack State Rx1<2:0> */
+#define FAST_ATK_MASK 0x7
+#define RX1_FAST_ATK_SHIFT 0
+#define RX2_FAST_ATK_SHIFT 4
+#define FAST_ATK_RESET 0
+#define FAST_ATK_PEAK_DETECT 1
+#define FAST_ATK_PWR_MEASURE 2
+#define FAST_ATK_FINAL_SETTELING 3
+#define FAST_ATK_FINAL_OVER 4
+#define FAST_ATK_GAIN_LOCKED 5
+
+/*
+* REG_SLOW_LOOP_STATE
+*/
+#define SLOW_LOOP_STATE_RX2(x) (((x) & 0x7) << 4) /* Slow Loop State Rx2<2:0> */
+#define SLOW_LOOP_STATE_RX1(x) (((x) & 0x7) << 0) /* Slow Loop State Rx1<2:0> */
+
+
+/*
+* REG_OVRG_SIGS_RX1,2
+*/
+#define GAIN_LOCK_1 (1 << 6) /* Gain Lock 1 */
+#define LOW_POWER_1 (1 << 5) /* Low Power 1 */
+#define LARGE_LMT_OL (1 << 4) /* Large LMT OL */
+#define SMALL_LMT_OL (1 << 3) /* Small LMT OL */
+#define LARGE_ADC_OL (1 << 2) /* Large ADC OL */
+#define SMALL_ADC_OL (1 << 1) /* Small ADC OL */
+#define DIG_SAT (1 << 0) /* Dig Sat */
+/*
+* REG_CTRL
+*/
+#define CTRL_ENABLE (1 << 0) /* Set to 1 */
+
+/*
+* REG_BIST_CONFIG
+*/
+#define TONE_PRBS (1 << 1) /* Tone/ PRBS */
+#define BIST_ENABLE (1 << 0) /* BIST Enable */
+#define TONE_FREQ(x) (((x) & 0x3) << 6) /* Tone Frequency<1:0> */
+#define TONE_LEVEL(x) (((x) & 0x3) << 4) /* Tone Level<1:0> */
+#define BIST_CTRL_POINT(x) (((x) & 0x3) << 2) /* BIST Control Point <1:0> */
+
+/*
+* REG_OBSERVE_CONFIG
+*/
+#define DATA_PORT_SP_HD_LOOP_TEST_OE (1 << 7) /* Data Port SP, HD Loop Test OE */
+#define RX_MASK (1 << 6) /* Rx Mask */
+#define CHANNEL (1 << 5) /* Channel */
+#define DATA_PORT_LOOP_TEST_ENABLE (1 << 0) /* Data Port Loop Test Enable */
+#define OBSERVATION_POINT(x) (((x) & 0xF) << 1) /* Observation Point<2:0> */
+
+/*
+* REG_BIST_AND_DATA_PORT_TEST_CONFIG
+*/
+#define BIST_MASK_CHANNEL_2_Q_DATA (1 << 5) /* BIST Mask Channel 2 Q data */
+#define BIST_MASK_CHANNEL_2_I_DATA (1 << 4) /* BIST Mask Channel 2 I data */
+#define BIST_MASK_CHANNEL_1_Q_DATA (1 << 3) /* BIST Mask Channel 1 Q data */
+#define BIST_MASK_CHANNEL_1_I_DATA (1 << 2) /* BIST Mask Channel 1 I data */
+#define DATA_PORT_HILOW (1 << 1) /* Data Port Hi/Low */
+#define USE_DATA_PORT (1 << 0) /* Use Data Port */
+#define TEMP_SENSE_VBE_TEST(x) (((x) & 0x3) << 6) /* Temp Sense Vbe Test<1:0> */
+
+/*
+* REG_DAC_TEST_2
+*/
+#define DAC_TEST_ENABLE (1 << 7) /* DAC Test Enable */
+#define DAC_TEST_WORD(x) (((x) & 0x7F) << 0) /* DAC test Word <22:16> */
+
+/*
+* SPI Comm Helpers
+*/
+#define AD_READ (0 << 15)
+#define AD_WRITE (1 << 15)
+#define AD_CNT(x) ((((x) - 1) & 0x7) << 12)
+#define AD_ADDR(x) ((x) & 0x3FF)
+
+
+/*
+* AD9361 Limits
+*/
+
+#define RSSI_MULTIPLIER 100
+#define RSSI_RESOLUTION ((int) (0.25 * RSSI_MULTIPLIER))
+#define RSSI_MAX_WEIGHT 255
+
+#define MAX_LMT_INDEX 40
+#define MAX_LPF_GAIN 24
+#define MAX_DIG_GAIN 31
+
+#define MAX_BBPLL_FREF 70000000UL /* 70 MHz */
+#define MIN_BBPLL_FREQ 715000000UL /* 715 MHz */
+#define MAX_BBPLL_FREQ 1430000000UL /* 1430 MHz */
+#define MAX_BBPLL_DIV 64
+#define MIN_BBPLL_DIV 2
+
+/*
+ * The ADC minimum and maximum operating output data rates
+ * are 25MHz and 640MHz respectively.
+ * For more information see here: https://ez.analog.com/docs/DOC-12763
+ */
+
+#define MIN_ADC_CLK 25000000UL /* 25 MHz */
+//#define MIN_ADC_CLK (MIN_BBPLL_FREQ / MAX_BBPLL_DIV) /* 11.17MHz */
+#define MAX_ADC_CLK 640000000UL /* 640 MHz */
+#define MAX_DAC_CLK (MAX_ADC_CLK / 2)
+
+#define MAX_MBYTE_SPI 8
+
+#define RFPLL_MODULUS 8388593UL
+#define BBPLL_MODULUS 2088960UL
+
+#define MAX_SYNTH_FREF 80000000UL /* 80 MHz */
+#define MIN_SYNTH_FREF 10000000UL /* 10 MHz */
+#define MIN_VCO_FREQ_HZ 6000000000ULL
+#define MAX_CARRIER_FREQ_HZ 6000000000ULL
+#define MIN_CARRIER_FREQ_HZ 47000000ULL /* Nuand modification */
+
+#define AD9363A_MAX_CARRIER_FREQ_HZ 3800000000ULL
+#define AD9363A_MIN_CARRIER_FREQ_HZ 325000000ULL
+
+/*
+* Driver
+*/
+
+enum rx_gain_table_type {
+ RXGAIN_FULL_TBL,
+ RXGAIN_SPLIT_TBL,
+};
+
+enum rx_gain_table_name {
+ TBL_200_1300_MHZ,
+ TBL_1300_4000_MHZ,
+ TBL_4000_6000_MHZ,
+ RXGAIN_TBLS_END,
+};
+
+enum fir_dest {
+ FIR_TX1 = 0x01,
+ FIR_TX2 = 0x02,
+ FIR_TX1_TX2 = 0x03,
+ FIR_RX1 = 0x81,
+ FIR_RX2 = 0x82,
+ FIR_RX1_RX2 = 0x83,
+ FIR_IS_RX = 0x80,
+};
+
+struct rf_gain_ctrl {
+ uint32_t ant;
+ uint8_t mode;
+};
+
+enum rf_gain_ctrl_mode {
+ RF_GAIN_MGC,
+ RF_GAIN_FASTATTACK_AGC,
+ RF_GAIN_SLOWATTACK_AGC,
+ RF_GAIN_HYBRID_AGC
+};
+
+enum f_agc_target_gain_index_type {
+ MAX_GAIN,
+ SET_GAIN,
+ OPTIMIZED_GAIN,
+ NO_GAIN_CHANGE,
+};
+
+struct gain_control {
+ enum rf_gain_ctrl_mode rx1_mode;
+ enum rf_gain_ctrl_mode rx2_mode;
+
+ /* Common */
+ uint8_t adc_ovr_sample_size; /* 1..8 Sum x samples, AGC_CONFIG_3 */
+ uint8_t adc_small_overload_thresh; /* 0..255, 0x105 */
+ uint8_t adc_large_overload_thresh; /* 0..255, 0x104 */
+
+ uint16_t lmt_overload_high_thresh; /* 16..800 mV, 0x107 */
+ uint16_t lmt_overload_low_thresh; /* 16..800 mV, 0x108 */
+ uint16_t dec_pow_measuremnt_duration; /* Samples, 0x15C */
+ uint8_t low_power_thresh; /* -64..0 dBFS, 0x114 */
+
+ bool dig_gain_en; /* should be turned off, since ADI GT doesn't use dig gain */
+ uint8_t max_dig_gain; /* 0..31 */
+
+ /* MGC */
+ bool mgc_rx1_ctrl_inp_en; /* Enables Pin control on RX1 default SPI ctrl */
+ bool mgc_rx2_ctrl_inp_en; /* Enables Pin control on RX2 default SPI ctrl */
+
+ uint8_t mgc_inc_gain_step; /* 1..8 */
+ uint8_t mgc_dec_gain_step; /* 1..8 */
+ uint8_t mgc_split_table_ctrl_inp_gain_mode; /* 0=AGC determine this, 1=only in LPF, 2=only in LMT */
+
+ /* AGC */
+ uint8_t agc_attack_delay_extra_margin_us; /* 0..31 us */
+
+ uint8_t agc_outer_thresh_high;
+ uint8_t agc_outer_thresh_high_dec_steps;
+ uint8_t agc_inner_thresh_high;
+ uint8_t agc_inner_thresh_high_dec_steps;
+ uint8_t agc_inner_thresh_low;
+ uint8_t agc_inner_thresh_low_inc_steps;
+ uint8_t agc_outer_thresh_low;
+ uint8_t agc_outer_thresh_low_inc_steps;
+
+ uint8_t adc_small_overload_exceed_counter; /* 0..15, 0x122 */
+ uint8_t adc_large_overload_exceed_counter; /* 0..15, 0x122 */
+ uint8_t adc_large_overload_inc_steps; /* 0..15, 0x106 */
+
+ bool adc_lmt_small_overload_prevent_gain_inc; /* 0x120 */
+
+ uint8_t lmt_overload_large_exceed_counter; /* 0..15, 0x121 */
+ uint8_t lmt_overload_small_exceed_counter; /* 0..15, 0x121 */
+ uint8_t lmt_overload_large_inc_steps; /* 0..7, 0x121 */
+
+ uint8_t dig_saturation_exceed_counter; /* 0..15, 0x128 */
+ uint8_t dig_gain_step_size; /* 1..8, 0x100 */
+ bool sync_for_gain_counter_en; /* 0x128:4 !Hybrid */
+
+ uint32_t gain_update_interval_us; /* in us */
+ bool immed_gain_change_if_large_adc_overload; /* 0x123:3 */
+ bool immed_gain_change_if_large_lmt_overload; /* 0x123:7 */
+
+ /*
+ * Fast AGC
+ */
+ uint32_t f_agc_dec_pow_measuremnt_duration; /* Samples, 0x15C */
+ uint32_t f_agc_state_wait_time_ns; /* 0x117 0..31 RX samples -> time_ns */
+ /* Fast AGC - Low Power */
+ bool f_agc_allow_agc_gain_increase; /* 0x110:1 */
+ uint8_t f_agc_lp_thresh_increment_time; /* 0x11B RX samples */
+ uint8_t f_agc_lp_thresh_increment_steps; /* 0x117 1..8 */
+
+ /* Fast AGC - Lock Level */
+ uint8_t f_agc_lock_level; /* 0x101 0..-127 dBFS */
+ bool f_agc_lock_level_lmt_gain_increase_en; /* 0x111:6 */
+ uint8_t f_agc_lock_level_gain_increase_upper_limit; /* 0x118 0..63 */
+ /* Fast AGC - Peak Detectors and Final Settling */
+ uint8_t f_agc_lpf_final_settling_steps; /* 0x112:6 0..3 (Post Lock Level Step)*/
+ uint8_t f_agc_lmt_final_settling_steps; /* 0x113:6 0..3 (Post Lock Level Step)*/
+ uint8_t f_agc_final_overrange_count; /* 0x116:5 0..7 */
+ /* Fast AGC - Final Power Test */
+ bool f_agc_gain_increase_after_gain_lock_en; /* 0x110:7 */
+ /* Fast AGC - Unlocking the Gain */
+ /* 0 = MAX Gain, 1 = Set Gain, 2 = Optimized Gain */
+ enum f_agc_target_gain_index_type f_agc_gain_index_type_after_exit_rx_mode; /* 0x110:[4,2] */
+ bool f_agc_use_last_lock_level_for_set_gain_en; /* 0x111:7 */
+ uint8_t f_agc_optimized_gain_offset; /*0x116 0..15 steps */
+ bool f_agc_rst_gla_stronger_sig_thresh_exceeded_en; /* 0x110:~6 */
+ uint8_t f_agc_rst_gla_stronger_sig_thresh_above_ll; /*0x113 0..63 dbFS */
+ bool f_agc_rst_gla_engergy_lost_sig_thresh_exceeded_en; /* 0x110:6 */
+ bool f_agc_rst_gla_engergy_lost_goto_optim_gain_en; /* 0x110:6 */
+ uint8_t f_agc_rst_gla_engergy_lost_sig_thresh_below_ll; /* 0x112:6 */
+ uint8_t f_agc_energy_lost_stronger_sig_gain_lock_exit_cnt; /* 0x119 0..63 RX samples */
+ bool f_agc_rst_gla_large_adc_overload_en; /*0x110:~1 and 0x114:~7 */
+ bool f_agc_rst_gla_large_lmt_overload_en; /*0x110:~1 */
+ bool f_agc_rst_gla_en_agc_pulled_high_en;
+ /* 0 = Max Gain, 1 = Set Gain, 2 = Optimized Gain, 3 = No Gain Change */
+
+ enum f_agc_target_gain_index_type f_agc_rst_gla_if_en_agc_pulled_high_mode; /* 0x0FB, 0x111 */
+ uint8_t f_agc_power_measurement_duration_in_state5; /* 0x109, 0x10a RX samples 0..524288*/
+
+};
+
+struct auxdac_control {
+ uint16_t dac1_default_value;
+ uint16_t dac2_default_value;
+
+ bool auxdac_manual_mode_en;
+
+ bool dac1_in_rx_en;
+ bool dac1_in_tx_en;
+ bool dac1_in_alert_en;
+
+ bool dac2_in_rx_en;
+ bool dac2_in_tx_en;
+ bool dac2_in_alert_en;
+
+ uint8_t dac1_rx_delay_us;
+ uint8_t dac1_tx_delay_us;
+ uint8_t dac2_rx_delay_us;
+ uint8_t dac2_tx_delay_us;
+};
+
+enum rssi_restart_mode {
+ AGC_IN_FAST_ATTACK_MODE_LOCKS_THE_GAIN,
+ EN_AGC_PIN_IS_PULLED_HIGH,
+ ENTERS_RX_MODE,
+ GAIN_CHANGE_OCCURS,
+ SPI_WRITE_TO_REGISTER,
+ GAIN_CHANGE_OCCURS_OR_EN_AGC_PIN_PULLED_HIGH,
+};
+
+struct rssi_control {
+ enum rssi_restart_mode restart_mode;
+ bool rssi_unit_is_rx_samples; /* default unit is time */
+ uint32_t rssi_delay;
+ uint32_t rssi_wait;
+ uint32_t rssi_duration;
+};
+
+struct rx_gain_info {
+ enum rx_gain_table_type tbl_type;
+ int32_t starting_gain_db;
+ int32_t max_gain_db;
+ int32_t gain_step_db;
+ int32_t max_idx;
+ int32_t idx_step_offset;
+};
+
+struct port_control {
+ uint8_t pp_conf[3];
+ uint8_t rx_clk_data_delay;
+ uint8_t tx_clk_data_delay;
+ uint8_t digital_io_ctrl;
+ uint8_t lvds_bias_ctrl;
+ uint8_t lvds_invert[2];
+#ifdef NUAND_MODIFICATIONS
+ // add digital interface drive and clock skew
+ uint8_t clk_out_drive;
+ uint8_t dataclk_drive;
+ uint8_t data_port_drive;
+ uint8_t clk_out_slew;
+ uint8_t dataclk_slew;
+ uint8_t data_port_slew;
+#endif // NUAND_MODIFICATIONS
+};
+
+struct ctrl_outs_control {
+ uint8_t index;
+ uint8_t en_mask;
+};
+
+struct elna_control {
+ uint16_t gain_mdB;
+ uint16_t bypass_loss_mdB;
+ uint32_t settling_delay_ns;
+ bool elna_1_control_en; /* GPO0 */
+ bool elna_2_control_en; /* GPO1 */
+ bool elna_in_gaintable_all_index_en;
+};
+
+struct auxadc_control {
+ int8_t offset;
+ uint32_t temp_time_inteval_ms;
+ uint32_t temp_sensor_decimation;
+ bool periodic_temp_measuremnt;
+ uint32_t auxadc_clock_rate;
+ uint32_t auxadc_decimation;
+};
+
+struct gpo_control {
+ bool gpo0_inactive_state_high_en;
+ bool gpo1_inactive_state_high_en;
+ bool gpo2_inactive_state_high_en;
+ bool gpo3_inactive_state_high_en;
+ bool gpo0_slave_rx_en;
+ bool gpo0_slave_tx_en;
+ bool gpo1_slave_rx_en;
+ bool gpo1_slave_tx_en;
+ bool gpo2_slave_rx_en;
+ bool gpo2_slave_tx_en;
+ bool gpo3_slave_rx_en;
+ bool gpo3_slave_tx_en;
+ uint8_t gpo0_rx_delay_us;
+ uint8_t gpo0_tx_delay_us;
+ uint8_t gpo1_rx_delay_us;
+ uint8_t gpo1_tx_delay_us;
+ uint8_t gpo2_rx_delay_us;
+ uint8_t gpo2_tx_delay_us;
+ uint8_t gpo3_rx_delay_us;
+ uint8_t gpo3_tx_delay_us;
+};
+
+struct tx_monitor_control {
+ bool tx_mon_track_en;
+ bool one_shot_mode_en;
+ uint32_t low_high_gain_threshold_mdB;
+ uint8_t low_gain_dB;
+ uint8_t high_gain_dB;
+ uint16_t tx_mon_delay;
+ uint16_t tx_mon_duration;
+ uint8_t tx1_mon_front_end_gain;
+ uint8_t tx2_mon_front_end_gain;
+ uint8_t tx1_mon_lo_cm;
+ uint8_t tx2_mon_lo_cm;
+};
+
+enum ad9361_pdata_rx_freq {
+ BBPLL_FREQ,
+ ADC_FREQ,
+ R2_FREQ,
+ R1_FREQ,
+ CLKRF_FREQ,
+ RX_SAMPL_FREQ,
+ NUM_RX_CLOCKS,
+};
+
+enum ad9361_pdata_tx_freq {
+#ifdef NUAND_MODIFICATIONS
+ // this is a name conflict on some platforms
+ IGNORE_FREQ,
+#else
+ IGNORE,
+#endif // NUAND_MODIFICATIONS
+ DAC_FREQ,
+ T2_FREQ,
+ T1_FREQ,
+ CLKTF_FREQ,
+ TX_SAMPL_FREQ,
+ NUM_TX_CLOCKS,
+};
+
+enum ad9361_clkout {
+ CLKOUT_DISABLE,
+ BUFFERED_XTALN_DCXO,
+ ADC_CLK_DIV_2,
+ ADC_CLK_DIV_3,
+ ADC_CLK_DIV_4,
+ ADC_CLK_DIV_8,
+ ADC_CLK_DIV_16,
+};
+
+struct ad9361_phy_platform_data {
+ bool rx2tx2;
+ bool fdd;
+ bool fdd_independent_mode;
+ bool split_gt;
+ bool use_extclk;
+ bool ensm_pin_pulse_mode;
+ bool ensm_pin_ctrl;
+ bool debug_mode;
+ bool tdd_use_dual_synth;
+ bool tdd_skip_vco_cal;
+ bool use_ext_rx_lo;
+ bool use_ext_tx_lo;
+ bool rx1rx2_phase_inversion_en;
+ bool qec_tracking_slow_mode_en;
+ uint8_t dc_offset_update_events;
+ uint8_t dc_offset_attenuation_high;
+ uint8_t dc_offset_attenuation_low;
+ uint8_t rf_dc_offset_count_high;
+ uint8_t rf_dc_offset_count_low;
+ uint8_t dig_interface_tune_skipmode;
+ uint8_t dig_interface_tune_fir_disable;
+ uint32_t dcxo_coarse;
+ uint32_t dcxo_fine;
+ uint32_t rf_rx_input_sel;
+ uint32_t rf_tx_output_sel;
+ uint32_t rx1tx1_mode_use_rx_num;
+ uint32_t rx1tx1_mode_use_tx_num;
+ uint32_t rx_path_clks[NUM_RX_CLOCKS];
+ uint32_t tx_path_clks[NUM_TX_CLOCKS];
+ uint32_t trx_synth_max_fref;
+ uint64_t rx_synth_freq;
+ uint64_t tx_synth_freq;
+ uint32_t rf_rx_bandwidth_Hz;
+ uint32_t rf_tx_bandwidth_Hz;
+ int32_t tx_atten;
+ bool update_tx_gain_via_alert;
+ uint32_t rx_fastlock_delay_ns;
+ uint32_t tx_fastlock_delay_ns;
+ bool trx_fastlock_pinctrl_en[2];
+
+ enum ad9361_clkout ad9361_clkout_mode;
+
+ struct gain_control gain_ctrl;
+ struct rssi_control rssi_ctrl;
+ struct port_control port_ctrl;
+ struct ctrl_outs_control ctrl_outs_ctrl;
+ struct elna_control elna_ctrl;
+ struct auxadc_control auxadc_ctrl;
+ struct auxdac_control auxdac_ctrl;
+ struct gpo_control gpo_ctrl;
+ struct tx_monitor_control txmon_ctrl;
+
+ int32_t gpio_resetb;
+ /* MCS SYNC */
+ int32_t gpio_sync;
+ int32_t gpio_cal_sw1;
+ int32_t gpio_cal_sw2;
+};
+
+struct rf_rx_gain {
+ uint32_t ant; /* Antenna number to read gain */
+ int32_t gain_db; /* gain value in dB */
+ uint32_t fgt_lmt_index; /* Full Gain Table / LNA-MIXER-TIA gain index */
+ uint32_t lmt_gain; /* LNA-MIXER-TIA gain in dB (Split GT mode only)*/
+ uint32_t lpf_gain; /* Low pass filter gain in dB / index (Split GT mode only)*/
+ uint32_t digital_gain; /* Digital gain in dB / index */
+ /* Debug only */
+ uint32_t lna_index; /* LNA Index (Split GT mode only) */
+ uint32_t tia_index; /* TIA Index (Split GT mode only) */
+ uint32_t mixer_index; /* MIXER Index (Split GT mode only) */
+
+};
+struct rf_rssi {
+ uint32_t ant; /* Antenna number for which RSSI is reported */
+ uint32_t symbol; /* Runtime RSSI */
+ uint32_t preamble; /* Initial RSSI */
+ int32_t multiplier; /* Multiplier to convert reported RSSI */
+ uint8_t duration; /* Duration to be considered for measuring */
+};
+
+struct SynthLUT {
+ uint16_t VCO_MHz;
+ uint8_t VCO_Output_Level;
+ uint8_t VCO_Varactor;
+ uint8_t VCO_Bias_Ref;
+ uint8_t VCO_Bias_Tcf;
+ uint8_t VCO_Cal_Offset;
+ uint8_t VCO_Varactor_Reference;
+ uint8_t Charge_Pump_Current;
+ uint8_t LF_C2;
+ uint8_t LF_C1;
+ uint8_t LF_R1;
+ uint8_t LF_C3;
+ uint8_t LF_R3;
+};
+
+enum {
+ LUT_FTDD_40,
+ LUT_FTDD_60,
+ LUT_FTDD_80,
+ LUT_FTDD_ENT,
+};
+
+enum ad9361_clocks {
+ BB_REFCLK,
+ RX_REFCLK,
+ TX_REFCLK,
+ BBPLL_CLK,
+ ADC_CLK,
+ R2_CLK,
+ R1_CLK,
+ CLKRF_CLK,
+ RX_SAMPL_CLK,
+ DAC_CLK,
+ T2_CLK,
+ T1_CLK,
+ CLKTF_CLK,
+ TX_SAMPL_CLK,
+ RX_RFPLL_INT,
+ TX_RFPLL_INT,
+ RX_RFPLL_DUMMY,
+ TX_RFPLL_DUMMY,
+ RX_RFPLL,
+ TX_RFPLL,
+ NUM_AD9361_CLKS,
+ EXT_REF_CLK,
+};
+
+struct ad9361_debugfs_entry {
+ struct ad9361_rf_phy *phy;
+ const char *propname;
+ void *out_value;
+ uint32_t val;
+ uint8_t size;
+ uint8_t cmd;
+};
+
+struct ad9361_fastlock_entry {
+#define FASTLOOK_INIT 1
+ uint8_t flags;
+ uint8_t alc_orig;
+ uint8_t alc_written;
+};
+
+struct ad9361_fastlock {
+ uint8_t save_profile;
+ uint8_t current_profile[2];
+ struct ad9361_fastlock_entry entry[2][8];
+};
+
+enum dig_tune_flags {
+ BE_VERBOSE = 1,
+ BE_MOREVERBOSE = 2,
+ DO_IDELAY = 4,
+ DO_ODELAY = 8,
+ SKIP_STORE_RESULT = 16,
+ RESTORE_DEFAULT = 32,
+};
+
+enum ad9361_bist_mode {
+ BIST_DISABLE,
+ BIST_INJ_TX,
+ BIST_INJ_RX,
+};
+
+enum dev_id {
+ ID_AD9361,
+ ID_AD9364,
+ ID_AD9363A
+};
+
+struct ad9361_rf_phy {
+ enum dev_id dev_sel;
+ uint8_t id_no;
+ struct spi_device *spi;
+#ifdef NUAND_MODIFICATIONS
+ // add gpio device struct
+ struct gpio_device *gpio;
+#endif // NUAND_MODIFICATIONS
+ struct clk *clk_refin;
+ struct clk *clks[NUM_AD9361_CLKS];
+ struct refclk_scale *ref_clk_scale[NUM_AD9361_CLKS];
+ struct clk_onecell_data clk_data;
+ uint32_t (*ad9361_rfpll_ext_recalc_rate)(struct refclk_scale *clk_priv);
+ int32_t (*ad9361_rfpll_ext_round_rate)(struct refclk_scale *clk_priv, uint32_t rate);
+ int32_t (*ad9361_rfpll_ext_set_rate)(struct refclk_scale *clk_priv, uint32_t rate);
+ struct ad9361_phy_platform_data *pdata;
+ uint8_t prev_ensm_state;
+ uint8_t curr_ensm_state;
+ uint8_t cached_rx_rfpll_div;
+ uint8_t cached_tx_rfpll_div;
+ struct rx_gain_info rx_gain[RXGAIN_TBLS_END];
+ enum rx_gain_table_name current_table;
+ bool ensm_pin_ctl_en;
+
+ bool auto_cal_en;
+ uint64_t last_tx_quad_cal_freq;
+ uint32_t last_tx_quad_cal_phase;
+ uint64_t current_tx_lo_freq;
+ uint64_t current_rx_lo_freq;
+ bool current_tx_use_tdd_table;
+ bool current_rx_use_tdd_table;
+ uint32_t flags;
+ uint32_t cal_threshold_freq;
+ uint32_t current_rx_bw_Hz;
+ uint32_t current_tx_bw_Hz;
+ uint32_t rxbbf_div;
+ uint32_t rate_governor;
+ bool bypass_rx_fir;
+ bool bypass_tx_fir;
+ bool rx_eq_2tx;
+ bool filt_valid;
+ uint32_t filt_rx_path_clks[NUM_RX_CLOCKS];
+ uint32_t filt_tx_path_clks[NUM_TX_CLOCKS];
+ uint32_t filt_rx_bw_Hz;
+ uint32_t filt_tx_bw_Hz;
+ uint8_t tx_fir_int;
+ uint8_t tx_fir_ntaps;
+ uint8_t rx_fir_dec;
+ uint8_t rx_fir_ntaps;
+ uint8_t agc_mode[2];
+ bool rfdc_track_en;
+ bool bbdc_track_en;
+ bool quad_track_en;
+ bool txmon_tdd_en;
+ uint16_t auxdac1_value;
+ uint16_t auxdac2_value;
+ uint32_t tx1_atten_cached;
+ uint32_t tx2_atten_cached;
+ struct ad9361_fastlock fastlock;
+ struct axiadc_converter *adc_conv;
+ struct axiadc_state *adc_state;
+ int32_t bist_loopback_mode;
+ enum ad9361_bist_mode bist_prbs_mode;
+ enum ad9361_bist_mode bist_tone_mode;
+ uint32_t bist_tone_freq_Hz;
+ uint32_t bist_tone_level_dB;
+ uint32_t bist_tone_mask;
+ bool bbpll_initialized;
+};
+
+struct refclk_scale {
+ struct spi_device *spi;
+ struct ad9361_rf_phy *phy;
+ uint32_t mult;
+ uint32_t div;
+ enum ad9361_clocks source;
+ enum ad9361_clocks parent_source;
+};
+
+enum debugfs_cmd {
+ DBGFS_NONE,
+ DBGFS_INIT,
+ DBGFS_LOOPBACK,
+ DBGFS_BIST_PRBS,
+ DBGFS_BIST_TONE,
+ DBGFS_BIST_DT_ANALYSIS,
+ DBGFS_RXGAIN_1,
+ DBGFS_RXGAIN_2,
+};
+
+/******************************************************************************/
+/************************ Functions Declarations ******************************/
+/******************************************************************************/
+int32_t ad9361_spi_readm(struct spi_device *spi, uint32_t reg,
+ uint8_t *rbuf, uint32_t num);
+int32_t ad9361_spi_read(struct spi_device *spi, uint32_t reg);
+int32_t ad9361_spi_write(struct spi_device *spi,
+ uint32_t reg, uint32_t val);
+int32_t ad9361_reset(struct ad9361_rf_phy *phy);
+int32_t register_clocks(struct ad9361_rf_phy *phy);
+int32_t ad9361_init_gain_tables(struct ad9361_rf_phy *phy);
+int32_t ad9361_setup(struct ad9361_rf_phy *phy);
+int32_t ad9361_post_setup(struct ad9361_rf_phy *phy);
+int32_t ad9361_set_ensm_mode(struct ad9361_rf_phy *phy, bool fdd, bool pinctrl);
+int32_t ad9361_ensm_set_state(struct ad9361_rf_phy *phy, uint8_t ensm_state,
+ bool pinctrl);
+int32_t ad9361_set_rx_gain(struct ad9361_rf_phy *phy,
+ uint32_t rx_id, struct rf_rx_gain *rx_gain);
+int32_t ad9361_get_rx_gain(struct ad9361_rf_phy *phy,
+ uint32_t rx_id, struct rf_rx_gain *rx_gain);
+int32_t ad9361_update_rf_bandwidth(struct ad9361_rf_phy *phy,
+ uint32_t rf_rx_bw, uint32_t rf_tx_bw);
+int32_t ad9361_calculate_rf_clock_chain(struct ad9361_rf_phy *phy,
+ uint32_t tx_sample_rate,
+ uint32_t rate_gov,
+ uint32_t *rx_path_clks,
+ uint32_t *tx_path_clks);
+int32_t ad9361_set_trx_clock_chain(struct ad9361_rf_phy *phy,
+ uint32_t *rx_path_clks,
+ uint32_t *tx_path_clks);
+int32_t ad9361_get_trx_clock_chain(struct ad9361_rf_phy *phy, uint32_t *rx_path_clks,
+ uint32_t *tx_path_clks);
+uint32_t ad9361_to_clk(uint64_t freq);
+uint64_t ad9361_from_clk(uint32_t freq);
+int32_t ad9361_read_rssi(struct ad9361_rf_phy *phy, struct rf_rssi *rssi);
+int32_t ad9361_set_gain_ctrl_mode(struct ad9361_rf_phy *phy,
+ struct rf_gain_ctrl *gain_ctrl);
+int32_t ad9361_load_fir_filter_coef(struct ad9361_rf_phy *phy,
+ enum fir_dest dest, int32_t gain_dB,
+ uint32_t ntaps, short *coef);
+int32_t ad9361_validate_enable_fir(struct ad9361_rf_phy *phy);
+int32_t ad9361_set_tx_atten(struct ad9361_rf_phy *phy, uint32_t atten_mdb,
+ bool tx1, bool tx2, bool immed);
+int32_t ad9361_get_tx_atten(struct ad9361_rf_phy *phy, uint32_t tx_num);
+uint32_t ad9361_clk_factor_recalc_rate(struct refclk_scale *clk_priv,
+ uint32_t parent_rate);
+int32_t ad9361_clk_factor_round_rate(struct refclk_scale *clk_priv, uint32_t rate,
+ uint32_t *prate);
+int32_t ad9361_clk_factor_set_rate(struct refclk_scale *clk_priv, uint32_t rate,
+ uint32_t parent_rate);
+uint32_t ad9361_bbpll_recalc_rate(struct refclk_scale *clk_priv,
+ uint32_t parent_rate);
+int32_t ad9361_bbpll_round_rate(struct refclk_scale *clk_priv, uint32_t rate,
+ uint32_t *prate);
+int32_t ad9361_bbpll_set_rate(struct refclk_scale *clk_priv, uint32_t rate,
+ uint32_t parent_rate);
+uint32_t ad9361_rfpll_int_recalc_rate(struct refclk_scale *clk_priv,
+ uint32_t parent_rate);
+int32_t ad9361_rfpll_int_round_rate(struct refclk_scale *clk_priv, uint32_t rate,
+ uint32_t *prate);
+int32_t ad9361_rfpll_int_set_rate(struct refclk_scale *clk_priv, uint32_t rate,
+ uint32_t parent_rate);
+uint32_t ad9361_rfpll_dummy_recalc_rate(struct refclk_scale *clk_priv);
+int32_t ad9361_rfpll_dummy_set_rate(struct refclk_scale *clk_priv, uint32_t rate);
+uint32_t ad9361_rfpll_recalc_rate(struct refclk_scale *clk_priv);
+int32_t ad9361_rfpll_round_rate(struct refclk_scale *clk_priv, uint32_t rate);
+int32_t ad9361_rfpll_set_rate(struct refclk_scale *clk_priv, uint32_t rate);
+int32_t ad9361_clk_mux_set_parent(struct refclk_scale *clk_priv, uint8_t index);
+int32_t ad9361_tracking_control(struct ad9361_rf_phy *phy, bool bbdc_track,
+ bool rfdc_track, bool rxquad_track);
+int32_t ad9361_bist_loopback(struct ad9361_rf_phy *phy, int32_t mode);
+void ad9361_get_bist_loopback(struct ad9361_rf_phy *phy, int32_t *mode);
+int32_t ad9361_bist_prbs(struct ad9361_rf_phy *phy, enum ad9361_bist_mode mode);
+void ad9361_get_bist_prbs(struct ad9361_rf_phy *phy, enum ad9361_bist_mode *mode);
+int32_t ad9361_bist_tone(struct ad9361_rf_phy *phy,
+ enum ad9361_bist_mode mode, uint32_t freq_Hz,
+ uint32_t level_dB, uint32_t mask);
+void ad9361_get_bist_tone(struct ad9361_rf_phy *phy,
+ enum ad9361_bist_mode *mode, uint32_t *freq_Hz,
+ uint32_t *level_dB, uint32_t *mask);
+int32_t ad9361_rf_port_setup(struct ad9361_rf_phy *phy, bool is_out,
+ uint32_t rx_inputs, uint32_t txb);
+int32_t ad9361_mcs(struct ad9361_rf_phy *phy, int32_t step);
+int32_t ad9361_do_calib_run(struct ad9361_rf_phy *phy, uint32_t cal, int32_t arg);
+int32_t ad9361_fastlock_store(struct ad9361_rf_phy *phy, bool tx, uint32_t profile);
+int32_t ad9361_fastlock_recall(struct ad9361_rf_phy *phy, bool tx, uint32_t profile);
+int32_t ad9361_fastlock_load(struct ad9361_rf_phy *phy, bool tx,
+ uint32_t profile, uint8_t *values);
+int32_t ad9361_fastlock_save(struct ad9361_rf_phy *phy, bool tx,
+ uint32_t profile, uint8_t *values);
+void ad9361_ensm_force_state(struct ad9361_rf_phy *phy, uint8_t ensm_state);
+void ad9361_ensm_restore_prev_state(struct ad9361_rf_phy *phy);
+int32_t ad9361_set_trx_clock_chain_freq(struct ad9361_rf_phy *phy,
+ uint32_t freq);
+int32_t ad9361_find_opt(uint8_t *field, uint32_t size, uint32_t *ret_start);
+int32_t ad9361_hdl_loopback(struct ad9361_rf_phy *phy, bool enable);
+int32_t ad9361_dig_interface_timing_analysis(struct ad9361_rf_phy *phy,
+ char *buf, int32_t buflen);
+int32_t ad9361_dig_tune(struct ad9361_rf_phy *phy, uint32_t max_freq,
+ enum dig_tune_flags flags);
+int32_t ad9361_en_dis_tx(struct ad9361_rf_phy *phy, uint32_t tx_if, uint32_t enable);
+int32_t ad9361_en_dis_rx(struct ad9361_rf_phy *phy, uint32_t rx_if, uint32_t enable);
+int32_t ad9361_1rx1tx_channel_map(struct ad9361_rf_phy *phy, bool tx, int32_t channel);
+int32_t ad9361_rssi_gain_step_calib(struct ad9361_rf_phy *phy);
+int32_t ad9361_set_dcxo_tune(struct ad9361_rf_phy *phy,
+ uint32_t coarse, uint32_t fine);
+int32_t ad9361_tx_mute(struct ad9361_rf_phy *phy, uint32_t state);
+uint32_t ad9361_validate_rf_bw(struct ad9361_rf_phy *phy, uint32_t bw);
+#endif
diff --git a/Radio/HW/BladeRF/common/thirdparty/ad936x/ad9361_api.c b/Radio/HW/BladeRF/common/thirdparty/ad936x/ad9361_api.c
new file mode 100644
index 0000000..180315d
--- /dev/null
+++ b/Radio/HW/BladeRF/common/thirdparty/ad936x/ad9361_api.c
@@ -0,0 +1,2126 @@
+/***************************************************************************//**
+ * @file ad9361_api.c
+ * @brief Implementation of AD9361 API Driver.
+ * @author DBogdan (dragos.bogdan@analog.com)
+********************************************************************************
+ * Copyright 2013(c) Analog Devices, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * - Neither the name of Analog Devices, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * - The use of this software may or may not infringe the patent rights
+ * of one or more patent holders. This license does not release you
+ * from the requirement that you obtain separate licenses from these
+ * patent holders to use this software.
+ * - Use of the software either in source or binary form, must be run
+ * on or directly connected to an Analog Devices Inc. component.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*******************************************************************************/
+
+/******************************************************************************/
+/***************************** Include Files **********************************/
+/******************************************************************************/
+#include "ad9361.h"
+#include "ad9361_api.h"
+#include "platform.h"
+#include "util.h"
+#include "config.h"
+#include <string.h>
+
+#ifndef AXI_ADC_NOT_PRESENT
+/******************************************************************************/
+/************************ Constants Definitions *******************************/
+/******************************************************************************/
+static struct axiadc_chip_info axiadc_chip_info_tbl[] =
+{
+ {
+ "4_CH_DEV",
+ 4
+ },
+ {
+ "2_CH_DEV",
+ 2
+ },
+};
+#endif
+
+/**
+ * Initialize the AD9361 part.
+ * @param init_param The structure that contains the AD9361 initial parameters.
+ * @return A structure that contains the AD9361 current state in case of
+ * success, negative error code otherwise.
+ *
+ * Note: This function will/may affect the data path.
+ */
+#ifdef NUAND_MODIFICATIONS
+// add *userdata parameter
+int32_t ad9361_init (struct ad9361_rf_phy **ad9361_phy, AD9361_InitParam *init_param, void *userdata)
+#else
+int32_t ad9361_init (struct ad9361_rf_phy **ad9361_phy, AD9361_InitParam *init_param)
+#endif // NUAND_MODIFICATIONS
+{
+ struct ad9361_rf_phy *phy;
+ int32_t ret = 0;
+ int32_t rev = 0;
+ int32_t i = 0;
+
+ phy = (struct ad9361_rf_phy *)zmalloc(sizeof(*phy));
+ if (!phy) {
+ return -ENOMEM;
+ }
+
+ phy->spi = (struct spi_device *)zmalloc(sizeof(*phy->spi));
+ if (!phy->spi) {
+ return -ENOMEM;
+ }
+
+#ifdef NUAND_MODIFICATIONS
+ // allocate gpio struct
+ phy->gpio = (struct gpio_device *)zmalloc(sizeof(*phy->gpio));
+ if (!phy->gpio) {
+ return -ENOMEM;
+ }
+#endif // NUAND_MODIFICATIONS
+
+ phy->clk_refin = (struct clk *)zmalloc(sizeof(*phy->clk_refin));
+ if (!phy->clk_refin) {
+ return -ENOMEM;
+ }
+
+ phy->pdata = (struct ad9361_phy_platform_data *)zmalloc(sizeof(*phy->pdata));
+ if (!phy->pdata) {
+ return -ENOMEM;
+ }
+#ifndef AXI_ADC_NOT_PRESENT
+ phy->adc_conv = (struct axiadc_converter *)zmalloc(sizeof(*phy->adc_conv));
+ if (!phy->adc_conv) {
+ return -ENOMEM;
+ }
+
+ phy->adc_state = (struct axiadc_state *)zmalloc(sizeof(*phy->adc_state));
+ if (!phy->adc_state) {
+ return -ENOMEM;
+ }
+ phy->adc_state->phy = phy;
+#endif
+
+#ifdef NUAND_MODIFICATIONS
+ /* Ensure phy->clks and phy->ref_clk_scale are adequately nulled */
+ for (size_t i = 0; i < NUM_AD9361_CLKS; ++i) {
+ phy->clks[i] = NULL;
+ phy->ref_clk_scale[i] = NULL;
+ }
+ phy->clk_data.clks = NULL;
+#endif // NUAND_MODIFICATIONS
+
+ /* Device selection */
+ phy->dev_sel = init_param->dev_sel;
+
+ /* Identification number */
+ phy->spi->id_no = init_param->id_no;
+ phy->id_no = init_param->id_no;
+
+ /* Reference Clock */
+ phy->clk_refin->rate = init_param->reference_clk_rate;
+
+ /* Base Configuration */
+ phy->pdata->fdd = init_param->frequency_division_duplex_mode_enable;
+ phy->pdata->fdd_independent_mode = init_param->frequency_division_duplex_independent_mode_enable;
+ phy->pdata->rx2tx2 = init_param->two_rx_two_tx_mode_enable;
+ phy->pdata->rx1tx1_mode_use_rx_num = init_param->one_rx_one_tx_mode_use_rx_num;
+ phy->pdata->rx1tx1_mode_use_tx_num = init_param->one_rx_one_tx_mode_use_tx_num;
+ phy->pdata->tdd_use_dual_synth = init_param->tdd_use_dual_synth_mode_enable;
+ phy->pdata->tdd_skip_vco_cal = init_param->tdd_skip_vco_cal_enable;
+ phy->pdata->rx_fastlock_delay_ns = init_param->rx_fastlock_delay_ns;
+ phy->pdata->tx_fastlock_delay_ns = init_param->tx_fastlock_delay_ns;
+ phy->pdata->trx_fastlock_pinctrl_en[0] = init_param->rx_fastlock_pincontrol_enable;
+ phy->pdata->trx_fastlock_pinctrl_en[1] = init_param->tx_fastlock_pincontrol_enable;
+ if (phy->dev_sel == ID_AD9363A) {
+ phy->pdata->use_ext_rx_lo = false;
+ phy->pdata->use_ext_tx_lo = false;
+ } else {
+ phy->pdata->use_ext_rx_lo = init_param->external_rx_lo_enable;
+ phy->pdata->use_ext_tx_lo = init_param->external_tx_lo_enable;
+ }
+ phy->pdata->dc_offset_update_events = init_param->dc_offset_tracking_update_event_mask;
+ phy->pdata->dc_offset_attenuation_high = init_param->dc_offset_attenuation_high_range;
+ phy->pdata->dc_offset_attenuation_low = init_param->dc_offset_attenuation_low_range;
+ phy->pdata->rf_dc_offset_count_high = init_param->dc_offset_count_high_range;
+ phy->pdata->rf_dc_offset_count_low = init_param->dc_offset_count_low_range;
+ phy->pdata->split_gt = init_param->split_gain_table_mode_enable;
+ phy->pdata->trx_synth_max_fref = init_param->trx_synthesizer_target_fref_overwrite_hz;
+ phy->pdata->qec_tracking_slow_mode_en = init_param->qec_tracking_slow_mode_enable;
+
+ /* ENSM Control */
+ phy->pdata->ensm_pin_pulse_mode = init_param->ensm_enable_pin_pulse_mode_enable;
+ phy->pdata->ensm_pin_ctrl = init_param->ensm_enable_txnrx_control_enable;
+
+ /* LO Control */
+ phy->pdata->rx_synth_freq = init_param->rx_synthesizer_frequency_hz;
+ phy->pdata->tx_synth_freq = init_param->tx_synthesizer_frequency_hz;
+
+ /* Rate & BW Control */
+ for(i = 0; i < 6; i++) {
+ phy->pdata->rx_path_clks[i] = init_param->rx_path_clock_frequencies[i];
+ }
+ for(i = 0; i < 6; i++) {
+ phy->pdata->tx_path_clks[i] = init_param->tx_path_clock_frequencies[i];
+ }
+ phy->pdata->rf_rx_bandwidth_Hz = init_param->rf_rx_bandwidth_hz;
+ phy->pdata->rf_tx_bandwidth_Hz = init_param->rf_tx_bandwidth_hz;
+
+ /* RF Port Control */
+ phy->pdata->rf_rx_input_sel = init_param->rx_rf_port_input_select;
+ phy->pdata->rf_tx_output_sel = init_param->tx_rf_port_input_select;
+
+ /* TX Attenuation Control */
+ phy->pdata->tx_atten = init_param->tx_attenuation_mdB;
+ phy->pdata->update_tx_gain_via_alert = init_param->update_tx_gain_in_alert_enable;
+
+ /* Reference Clock Control */
+ switch (phy->dev_sel) {
+ case ID_AD9363A:
+ phy->pdata->use_extclk = true;
+ break;
+ default:
+ phy->pdata->use_extclk = init_param->xo_disable_use_ext_refclk_enable;
+ }
+ phy->pdata->dcxo_coarse = init_param->dcxo_coarse_and_fine_tune[0];
+ phy->pdata->dcxo_fine = init_param->dcxo_coarse_and_fine_tune[1];
+ phy->pdata->ad9361_clkout_mode = (enum ad9361_clkout)init_param->clk_output_mode_select;
+
+ /* Gain Control */
+ phy->pdata->gain_ctrl.rx1_mode = (enum rf_gain_ctrl_mode)init_param->gc_rx1_mode;
+ phy->pdata->gain_ctrl.rx2_mode = (enum rf_gain_ctrl_mode)init_param->gc_rx2_mode;
+ phy->pdata->gain_ctrl.adc_large_overload_thresh = init_param->gc_adc_large_overload_thresh;
+ phy->pdata->gain_ctrl.adc_ovr_sample_size = init_param->gc_adc_ovr_sample_size;
+ phy->pdata->gain_ctrl.adc_small_overload_thresh = init_param->gc_adc_small_overload_thresh;
+ phy->pdata->gain_ctrl.dec_pow_measuremnt_duration = init_param->gc_dec_pow_measurement_duration;
+ phy->pdata->gain_ctrl.dig_gain_en = init_param->gc_dig_gain_enable;
+ phy->pdata->gain_ctrl.lmt_overload_high_thresh = init_param->gc_lmt_overload_high_thresh;
+ phy->pdata->gain_ctrl.lmt_overload_low_thresh = init_param->gc_lmt_overload_low_thresh;
+ phy->pdata->gain_ctrl.low_power_thresh = init_param->gc_low_power_thresh;
+ phy->pdata->gain_ctrl.max_dig_gain = init_param->gc_max_dig_gain;
+
+ /* Gain MGC Control */
+ phy->pdata->gain_ctrl.mgc_dec_gain_step = init_param->mgc_dec_gain_step;
+ phy->pdata->gain_ctrl.mgc_inc_gain_step = init_param->mgc_inc_gain_step;
+ phy->pdata->gain_ctrl.mgc_rx1_ctrl_inp_en = init_param->mgc_rx1_ctrl_inp_enable;
+ phy->pdata->gain_ctrl.mgc_rx2_ctrl_inp_en = init_param->mgc_rx2_ctrl_inp_enable;
+ phy->pdata->gain_ctrl.mgc_split_table_ctrl_inp_gain_mode = init_param->mgc_split_table_ctrl_inp_gain_mode;
+
+ /* Gain AGC Control */
+ phy->pdata->gain_ctrl.adc_large_overload_exceed_counter = init_param->agc_adc_large_overload_exceed_counter;
+ phy->pdata->gain_ctrl.adc_large_overload_inc_steps = init_param->agc_adc_large_overload_inc_steps;
+ phy->pdata->gain_ctrl.adc_lmt_small_overload_prevent_gain_inc = init_param->agc_adc_lmt_small_overload_prevent_gain_inc_enable;
+ phy->pdata->gain_ctrl.adc_small_overload_exceed_counter = init_param->agc_adc_small_overload_exceed_counter;
+ phy->pdata->gain_ctrl.dig_gain_step_size = init_param->agc_dig_gain_step_size;
+ phy->pdata->gain_ctrl.dig_saturation_exceed_counter = init_param->agc_dig_saturation_exceed_counter;
+ phy->pdata->gain_ctrl.gain_update_interval_us = init_param->agc_gain_update_interval_us;
+ phy->pdata->gain_ctrl.immed_gain_change_if_large_adc_overload = init_param->agc_immed_gain_change_if_large_adc_overload_enable;
+ phy->pdata->gain_ctrl.immed_gain_change_if_large_lmt_overload = init_param->agc_immed_gain_change_if_large_lmt_overload_enable;
+ phy->pdata->gain_ctrl.agc_inner_thresh_high = init_param->agc_inner_thresh_high;
+ phy->pdata->gain_ctrl.agc_inner_thresh_high_dec_steps = init_param->agc_inner_thresh_high_dec_steps;
+ phy->pdata->gain_ctrl.agc_inner_thresh_low = init_param->agc_inner_thresh_low;
+ phy->pdata->gain_ctrl.agc_inner_thresh_low_inc_steps = init_param->agc_inner_thresh_low_inc_steps;
+ phy->pdata->gain_ctrl.lmt_overload_large_exceed_counter = init_param->agc_lmt_overload_large_exceed_counter;
+ phy->pdata->gain_ctrl.lmt_overload_large_inc_steps = init_param->agc_lmt_overload_large_inc_steps;
+ phy->pdata->gain_ctrl.lmt_overload_small_exceed_counter = init_param->agc_lmt_overload_small_exceed_counter;
+ phy->pdata->gain_ctrl.agc_outer_thresh_high = init_param->agc_outer_thresh_high;
+ phy->pdata->gain_ctrl.agc_outer_thresh_high_dec_steps = init_param->agc_outer_thresh_high_dec_steps;
+ phy->pdata->gain_ctrl.agc_outer_thresh_low = init_param->agc_outer_thresh_low;
+ phy->pdata->gain_ctrl.agc_outer_thresh_low_inc_steps = init_param->agc_outer_thresh_low_inc_steps;
+ phy->pdata->gain_ctrl.agc_attack_delay_extra_margin_us = init_param->agc_attack_delay_extra_margin_us;
+ phy->pdata->gain_ctrl.sync_for_gain_counter_en = init_param->agc_sync_for_gain_counter_enable;
+
+ /* Fast AGC */
+ phy->pdata->gain_ctrl.f_agc_dec_pow_measuremnt_duration = init_param->fagc_dec_pow_measuremnt_duration;
+ phy->pdata->gain_ctrl.f_agc_state_wait_time_ns = init_param->fagc_state_wait_time_ns;
+ /* Fast AGC - Low Power */
+ phy->pdata->gain_ctrl.f_agc_allow_agc_gain_increase = init_param->fagc_allow_agc_gain_increase;
+ phy->pdata->gain_ctrl.f_agc_lp_thresh_increment_time = init_param->fagc_lp_thresh_increment_time;
+ phy->pdata->gain_ctrl.f_agc_lp_thresh_increment_steps = init_param->fagc_lp_thresh_increment_steps;
+ /* Fast AGC - Lock Level */
+ phy->pdata->gain_ctrl.f_agc_lock_level = init_param->fagc_lock_level;
+ phy->pdata->gain_ctrl.f_agc_lock_level_lmt_gain_increase_en = init_param->fagc_lock_level_lmt_gain_increase_en;
+ phy->pdata->gain_ctrl.f_agc_lock_level_gain_increase_upper_limit = init_param->fagc_lock_level_gain_increase_upper_limit;
+ /* Fast AGC - Peak Detectors and Final Settling */
+ phy->pdata->gain_ctrl.f_agc_lpf_final_settling_steps = init_param->fagc_lpf_final_settling_steps;
+ phy->pdata->gain_ctrl.f_agc_lmt_final_settling_steps = init_param->fagc_lmt_final_settling_steps;
+ phy->pdata->gain_ctrl.f_agc_final_overrange_count = init_param->fagc_final_overrange_count;
+ /* Fast AGC - Final Power Test */
+ phy->pdata->gain_ctrl.f_agc_gain_increase_after_gain_lock_en = init_param->fagc_gain_increase_after_gain_lock_en;
+ /* Fast AGC - Unlocking the Gain */
+ phy->pdata->gain_ctrl.f_agc_gain_index_type_after_exit_rx_mode = (enum f_agc_target_gain_index_type)init_param->fagc_gain_index_type_after_exit_rx_mode;
+ phy->pdata->gain_ctrl.f_agc_use_last_lock_level_for_set_gain_en = init_param->fagc_use_last_lock_level_for_set_gain_en;
+ phy->pdata->gain_ctrl.f_agc_rst_gla_stronger_sig_thresh_exceeded_en = init_param->fagc_rst_gla_stronger_sig_thresh_exceeded_en;
+ phy->pdata->gain_ctrl.f_agc_optimized_gain_offset = init_param->fagc_optimized_gain_offset;
+ phy->pdata->gain_ctrl.f_agc_rst_gla_stronger_sig_thresh_above_ll = init_param->fagc_rst_gla_stronger_sig_thresh_above_ll;
+ phy->pdata->gain_ctrl.f_agc_rst_gla_engergy_lost_sig_thresh_exceeded_en = init_param->fagc_rst_gla_engergy_lost_sig_thresh_exceeded_en;
+ phy->pdata->gain_ctrl.f_agc_rst_gla_engergy_lost_goto_optim_gain_en = init_param->fagc_rst_gla_engergy_lost_goto_optim_gain_en;
+ phy->pdata->gain_ctrl.f_agc_rst_gla_engergy_lost_sig_thresh_below_ll = init_param->fagc_rst_gla_engergy_lost_sig_thresh_below_ll;
+ phy->pdata->gain_ctrl.f_agc_energy_lost_stronger_sig_gain_lock_exit_cnt = init_param->fagc_energy_lost_stronger_sig_gain_lock_exit_cnt;
+ phy->pdata->gain_ctrl.f_agc_rst_gla_large_adc_overload_en = init_param->fagc_rst_gla_large_adc_overload_en;
+ phy->pdata->gain_ctrl.f_agc_rst_gla_large_lmt_overload_en = init_param->fagc_rst_gla_large_lmt_overload_en;
+ phy->pdata->gain_ctrl.f_agc_rst_gla_en_agc_pulled_high_en = init_param->fagc_rst_gla_en_agc_pulled_high_en;
+ phy->pdata->gain_ctrl.f_agc_rst_gla_if_en_agc_pulled_high_mode = (enum f_agc_target_gain_index_type)init_param->fagc_rst_gla_if_en_agc_pulled_high_mode;
+ phy->pdata->gain_ctrl.f_agc_power_measurement_duration_in_state5 = init_param->fagc_power_measurement_duration_in_state5;
+
+ /* RSSI Control */
+ phy->pdata->rssi_ctrl.rssi_delay = init_param->rssi_delay;
+ phy->pdata->rssi_ctrl.rssi_duration = init_param->rssi_duration;
+ phy->pdata->rssi_ctrl.restart_mode = (enum rssi_restart_mode)init_param->rssi_restart_mode;
+ phy->pdata->rssi_ctrl.rssi_unit_is_rx_samples = init_param->rssi_unit_is_rx_samples_enable;
+ phy->pdata->rssi_ctrl.rssi_wait = init_param->rssi_wait;
+
+ /* Aux ADC Control */
+ phy->pdata->auxadc_ctrl.auxadc_decimation = init_param->aux_adc_decimation;
+ phy->pdata->auxadc_ctrl.auxadc_clock_rate = init_param->aux_adc_rate;
+
+ /* AuxDAC Control */
+ phy->pdata->auxdac_ctrl.auxdac_manual_mode_en = init_param->aux_dac_manual_mode_enable;
+ phy->pdata->auxdac_ctrl.dac1_default_value = init_param->aux_dac1_default_value_mV;
+ phy->pdata->auxdac_ctrl.dac1_in_rx_en = init_param->aux_dac1_active_in_rx_enable;
+ phy->pdata->auxdac_ctrl.dac1_in_tx_en = init_param->aux_dac1_active_in_tx_enable;
+ phy->pdata->auxdac_ctrl.dac1_in_alert_en = init_param->aux_dac1_active_in_alert_enable;
+ phy->pdata->auxdac_ctrl.dac1_rx_delay_us = init_param->aux_dac1_rx_delay_us;
+ phy->pdata->auxdac_ctrl.dac1_tx_delay_us = init_param->aux_dac1_tx_delay_us;
+ phy->pdata->auxdac_ctrl.dac2_default_value = init_param->aux_dac2_default_value_mV;
+ phy->pdata->auxdac_ctrl.dac2_in_rx_en = init_param->aux_dac2_active_in_rx_enable;
+ phy->pdata->auxdac_ctrl.dac2_in_tx_en = init_param->aux_dac2_active_in_tx_enable;
+ phy->pdata->auxdac_ctrl.dac2_in_alert_en = init_param->aux_dac2_active_in_alert_enable;
+ phy->pdata->auxdac_ctrl.dac2_rx_delay_us = init_param->aux_dac2_rx_delay_us;
+ phy->pdata->auxdac_ctrl.dac2_tx_delay_us = init_param->aux_dac2_tx_delay_us;
+
+ /* Temperature Sensor Control */
+ phy->pdata->auxadc_ctrl.temp_sensor_decimation = init_param->temp_sense_decimation;
+ phy->pdata->auxadc_ctrl.temp_time_inteval_ms = init_param->temp_sense_measurement_interval_ms;
+ phy->pdata->auxadc_ctrl.offset = init_param->temp_sense_offset_signed;
+ phy->pdata->auxadc_ctrl.periodic_temp_measuremnt = init_param->temp_sense_periodic_measurement_enable;
+
+ /* Control Out Setup */
+ phy->pdata->ctrl_outs_ctrl.en_mask = init_param->ctrl_outs_enable_mask;
+ phy->pdata->ctrl_outs_ctrl.index = init_param->ctrl_outs_index;
+
+ /* External LNA Control */
+ phy->pdata->elna_ctrl.settling_delay_ns = init_param->elna_settling_delay_ns;
+ phy->pdata->elna_ctrl.gain_mdB = init_param->elna_gain_mdB;
+ phy->pdata->elna_ctrl.bypass_loss_mdB = init_param->elna_bypass_loss_mdB;
+ phy->pdata->elna_ctrl.elna_1_control_en = init_param->elna_rx1_gpo0_control_enable;
+ phy->pdata->elna_ctrl.elna_2_control_en = init_param->elna_rx2_gpo1_control_enable;
+ phy->pdata->elna_ctrl.elna_in_gaintable_all_index_en = init_param->elna_gaintable_all_index_enable;
+
+ /* Digital Interface Control */
+ phy->pdata->dig_interface_tune_skipmode = (init_param->digital_interface_tune_skip_mode);
+ phy->pdata->dig_interface_tune_fir_disable = (init_param->digital_interface_tune_fir_disable);
+ phy->pdata->port_ctrl.pp_conf[0] = (init_param->pp_tx_swap_enable << 7);
+ phy->pdata->port_ctrl.pp_conf[0] |= (init_param->pp_rx_swap_enable << 6);
+ phy->pdata->port_ctrl.pp_conf[0] |= (init_param->tx_channel_swap_enable << 5);
+ phy->pdata->port_ctrl.pp_conf[0] |= (init_param->rx_channel_swap_enable << 4);
+ phy->pdata->port_ctrl.pp_conf[0] |= (init_param->rx_frame_pulse_mode_enable << 3);
+ phy->pdata->port_ctrl.pp_conf[0] |= (init_param->two_t_two_r_timing_enable << 2);
+ phy->pdata->port_ctrl.pp_conf[0] |= (init_param->invert_data_bus_enable << 1);
+ phy->pdata->port_ctrl.pp_conf[0] |= (init_param->invert_data_clk_enable << 0);
+ phy->pdata->port_ctrl.pp_conf[1] = (init_param->fdd_alt_word_order_enable << 7);
+ phy->pdata->port_ctrl.pp_conf[1] |= (init_param->invert_rx_frame_enable << 2);
+ phy->pdata->port_ctrl.pp_conf[2] = (init_param->fdd_rx_rate_2tx_enable << 7);
+ phy->pdata->port_ctrl.pp_conf[2] |= (init_param->swap_ports_enable << 6);
+ phy->pdata->port_ctrl.pp_conf[2] |= (init_param->single_data_rate_enable << 5);
+ phy->pdata->port_ctrl.pp_conf[2] |= (init_param->lvds_mode_enable << 4);
+ phy->pdata->port_ctrl.pp_conf[2] |= (init_param->half_duplex_mode_enable << 3);
+ phy->pdata->port_ctrl.pp_conf[2] |= (init_param->single_port_mode_enable << 2);
+ phy->pdata->port_ctrl.pp_conf[2] |= (init_param->full_port_enable << 1);
+ phy->pdata->port_ctrl.pp_conf[2] |= (init_param->full_duplex_swap_bits_enable << 0);
+ phy->pdata->port_ctrl.pp_conf[1] |= (init_param->delay_rx_data & 0x3);
+ phy->pdata->port_ctrl.rx_clk_data_delay = DATA_CLK_DELAY(init_param->rx_data_clock_delay);
+ phy->pdata->port_ctrl.rx_clk_data_delay |= RX_DATA_DELAY(init_param->rx_data_delay);
+ phy->pdata->port_ctrl.tx_clk_data_delay = FB_CLK_DELAY(init_param->tx_fb_clock_delay);
+ phy->pdata->port_ctrl.tx_clk_data_delay |= TX_DATA_DELAY(init_param->tx_data_delay);
+ phy->pdata->port_ctrl.lvds_bias_ctrl = ((init_param->lvds_bias_mV - 75) / 75) & 0x7;
+ phy->pdata->port_ctrl.lvds_bias_ctrl |= (init_param->lvds_rx_onchip_termination_enable << 5);
+ phy->pdata->rx1rx2_phase_inversion_en = init_param->rx1rx2_phase_inversion_en;
+
+#ifdef NUAND_MODIFICATIONS
+ // settings for digital interface drive and slew
+ phy->pdata->port_ctrl.clk_out_drive = init_param->clk_out_drive & 0x1;
+ phy->pdata->port_ctrl.dataclk_drive = init_param->dataclk_drive & 0x1;
+ phy->pdata->port_ctrl.data_port_drive = init_param->data_port_drive & 0x1;
+ phy->pdata->port_ctrl.clk_out_slew = init_param->clk_out_slew & 0x3;
+ phy->pdata->port_ctrl.dataclk_slew = init_param->dataclk_slew & 0x3;
+ phy->pdata->port_ctrl.data_port_slew = init_param->data_port_slew & 0x3;
+#endif // NUAND_MODIFICATIONS
+
+ /* GPO Control */
+ phy->pdata->gpo_ctrl.gpo0_inactive_state_high_en = init_param->gpo0_inactive_state_high_enable;
+ phy->pdata->gpo_ctrl.gpo1_inactive_state_high_en = init_param->gpo1_inactive_state_high_enable;
+ phy->pdata->gpo_ctrl.gpo2_inactive_state_high_en = init_param->gpo2_inactive_state_high_enable;
+ phy->pdata->gpo_ctrl.gpo3_inactive_state_high_en = init_param->gpo3_inactive_state_high_enable;
+
+ phy->pdata->gpo_ctrl.gpo0_slave_rx_en = init_param->gpo0_slave_rx_enable;
+ phy->pdata->gpo_ctrl.gpo0_slave_tx_en = init_param->gpo0_slave_tx_enable;
+ phy->pdata->gpo_ctrl.gpo1_slave_rx_en = init_param->gpo1_slave_rx_enable;
+ phy->pdata->gpo_ctrl.gpo1_slave_tx_en = init_param->gpo1_slave_tx_enable;
+ phy->pdata->gpo_ctrl.gpo2_slave_rx_en = init_param->gpo2_slave_rx_enable;
+ phy->pdata->gpo_ctrl.gpo2_slave_tx_en = init_param->gpo2_slave_tx_enable;
+ phy->pdata->gpo_ctrl.gpo3_slave_rx_en = init_param->gpo3_slave_rx_enable;
+ phy->pdata->gpo_ctrl.gpo3_slave_tx_en = init_param->gpo3_slave_tx_enable;
+
+ phy->pdata->gpo_ctrl.gpo0_rx_delay_us = init_param->gpo0_rx_delay_us;
+ phy->pdata->gpo_ctrl.gpo0_tx_delay_us = init_param->gpo0_tx_delay_us;
+ phy->pdata->gpo_ctrl.gpo1_rx_delay_us = init_param->gpo1_rx_delay_us;
+ phy->pdata->gpo_ctrl.gpo1_tx_delay_us = init_param->gpo1_tx_delay_us;
+ phy->pdata->gpo_ctrl.gpo2_rx_delay_us = init_param->gpo2_rx_delay_us;
+ phy->pdata->gpo_ctrl.gpo2_tx_delay_us = init_param->gpo2_tx_delay_us;
+ phy->pdata->gpo_ctrl.gpo3_rx_delay_us = init_param->gpo3_rx_delay_us;
+ phy->pdata->gpo_ctrl.gpo3_tx_delay_us = init_param->gpo3_tx_delay_us;
+
+ /* Tx Monitor Control */
+ phy->pdata->txmon_ctrl.low_high_gain_threshold_mdB = init_param->low_high_gain_threshold_mdB;
+ phy->pdata->txmon_ctrl.low_gain_dB = init_param->low_gain_dB;
+ phy->pdata->txmon_ctrl.high_gain_dB = init_param->high_gain_dB;
+ phy->pdata->txmon_ctrl.tx_mon_track_en = init_param->tx_mon_track_en;
+ phy->pdata->txmon_ctrl.one_shot_mode_en = init_param->one_shot_mode_en;
+ phy->pdata->txmon_ctrl.tx_mon_delay = init_param->tx_mon_delay;
+ phy->pdata->txmon_ctrl.tx_mon_duration = init_param->tx_mon_duration;
+ phy->pdata->txmon_ctrl.tx1_mon_front_end_gain = init_param->tx1_mon_front_end_gain;
+ phy->pdata->txmon_ctrl.tx2_mon_front_end_gain = init_param->tx2_mon_front_end_gain;
+ phy->pdata->txmon_ctrl.tx1_mon_lo_cm = init_param->tx1_mon_lo_cm;
+ phy->pdata->txmon_ctrl.tx2_mon_lo_cm = init_param->tx2_mon_lo_cm;
+
+ phy->pdata->debug_mode = true;
+ phy->pdata->gpio_resetb = init_param->gpio_resetb;
+ /* Optional: next three GPIOs are used for MCS synchronization */
+ phy->pdata->gpio_sync = init_param->gpio_sync;
+ phy->pdata->gpio_cal_sw1 = init_param->gpio_cal_sw1;
+ phy->pdata->gpio_cal_sw2 = init_param->gpio_cal_sw2;
+
+ phy->pdata->port_ctrl.digital_io_ctrl = 0;
+ phy->pdata->port_ctrl.lvds_invert[0] = init_param->lvds_invert1_control;
+ phy->pdata->port_ctrl.lvds_invert[1] = init_param->lvds_invert2_control;
+
+#ifndef AXI_ADC_NOT_PRESENT
+ phy->adc_conv->chip_info = &axiadc_chip_info_tbl[phy->pdata->rx2tx2 ? ID_AD9361 : ID_AD9364];
+#endif
+
+ phy->rx_eq_2tx = false;
+
+ phy->current_table = RXGAIN_TBLS_END;
+ phy->bypass_tx_fir = true;
+ phy->bypass_rx_fir = true;
+ phy->rate_governor = 1;
+ phy->rfdc_track_en = true;
+ phy->bbdc_track_en = true;
+ phy->quad_track_en = true;
+
+ phy->bist_loopback_mode = 0;
+ phy->bist_prbs_mode = BIST_DISABLE;
+ phy->bist_tone_mode = BIST_DISABLE;
+ phy->bist_tone_freq_Hz = 0;
+ phy->bist_tone_level_dB = 0;
+ phy->bist_tone_mask = 0;
+
+#ifdef NUAND_MODIFICATIONS
+ // initialize SPI and GPIO
+ ret = spi_init(phy, userdata);
+ if (ret < 0) {
+ goto out;
+ }
+
+ ret = gpio_init(phy, userdata);
+ if (ret < 0) {
+ goto out;
+ }
+#endif // NUAND_MODIFICATIONS
+
+ ad9361_reset(phy);
+
+ ret = ad9361_spi_read(phy->spi, REG_PRODUCT_ID);
+ if ((ret & PRODUCT_ID_MASK) != PRODUCT_ID_9361) {
+ printf("%s : Unsupported PRODUCT_ID 0x%X", __func__, (unsigned int)ret);
+#ifdef NUAND_MODIFICATIONS
+ // this missing newline, so help me god...
+ printf("\n");
+#endif // NUAND_MODIFICATIONS
+ ret = -ENODEV;
+ goto out;
+ }
+ rev = ret & REV_MASK;
+
+ if (AD9364_DEVICE) {
+ phy->pdata->rx2tx2 = false;
+ phy->pdata->rx1tx1_mode_use_rx_num = 1;
+ phy->pdata->rx1tx1_mode_use_tx_num = 1;
+ }
+
+ phy->ad9361_rfpll_ext_recalc_rate = init_param->ad9361_rfpll_ext_recalc_rate;
+ phy->ad9361_rfpll_ext_round_rate = init_param->ad9361_rfpll_ext_round_rate;
+ phy->ad9361_rfpll_ext_set_rate = init_param->ad9361_rfpll_ext_set_rate;
+
+ ret = register_clocks(phy);
+ if (ret < 0)
+ goto out;
+
+#ifndef AXI_ADC_NOT_PRESENT
+#ifdef NUAND_MODIFICATIONS
+ // use alternate axiadc accessors
+ axiadc_init(phy, userdata);
+ ret = axiadc_read(phy->adc_state, ADI_REG_VERSION, &(phy->adc_state->pcore_version));
+ if (ret < 0)
+ goto out;
+#else
+ axiadc_init(phy);
+ phy->adc_state->pcore_version = axiadc_read(phy->adc_state, ADI_REG_VERSION);
+#endif // NUAND_MODIFICATIONS
+#endif
+
+ ad9361_init_gain_tables(phy);
+
+ ret = ad9361_setup(phy);
+ if (ret < 0)
+ goto out;
+
+#ifndef AXI_ADC_NOT_PRESENT
+ /* platform specific wrapper to call ad9361_post_setup() */
+ ret = axiadc_post_setup(phy);
+ if (ret < 0)
+ goto out;
+#endif
+
+#ifndef NUAND_MODIFICATIONS
+ // reduce console noise
+ printf("%s : AD936x Rev %d successfully initialized\n", __func__, (int)rev);
+#endif // !NUAND_MODIFICATIONS
+
+ *ad9361_phy = phy;
+
+ return 0;
+
+out:
+ free(phy->spi);
+#ifndef AXI_ADC_NOT_PRESENT
+ free(phy->adc_conv);
+ free(phy->adc_state);
+#endif
+ free(phy->clk_refin);
+ free(phy->pdata);
+
+#ifdef NUAND_MODIFICATIONS
+ // free gpio struct, and also the clock stuff
+ free(phy->gpio);
+ free(phy->clk_data.clks);
+ for (size_t i = 0; i < NUM_AD9361_CLKS; ++i) {
+ free(phy->clks[i]);
+ free(phy->ref_clk_scale[i]);
+ }
+#endif // NUAND_MODIFICATIONS
+
+ free(phy);
+ printf("%s : AD936x initialization error\n", __func__);
+
+ return -ENODEV;
+}
+
+#ifdef NUAND_MODIFICATIONS
+/**
+ * Deinitialize the AD9361 part.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_deinit (struct ad9361_rf_phy *phy)
+{
+ if (phy) {
+ /* Put AD9361 part into reset */
+ if (gpio_is_valid(phy->gpio, phy->pdata->gpio_resetb)) {
+ gpio_set_value(phy->gpio, phy->pdata->gpio_resetb, 0);
+ }
+ }
+
+ if (phy) {
+ free(phy->spi);
+ free(phy->gpio);
+#ifndef AXI_ADC_NOT_PRESENT
+ free(phy->adc_conv);
+ free(phy->adc_state);
+#endif
+ free(phy->clk_refin);
+ free(phy->clk_data.clks);
+ free(phy->pdata);
+
+ for (size_t i = 0; i < NUM_AD9361_CLKS; ++i) {
+ free(phy->clks[i]);
+ free(phy->ref_clk_scale[i]);
+ }
+
+ free(phy);
+ }
+
+ return 0;
+}
+#endif // NUAND_MODIFICATIONS
+
+/**
+ * Set the Enable State Machine (ENSM) mode.
+ * @param phy The AD9361 current state structure.
+ * @param mode The ENSM mode.
+ * Accepted values:
+ * ENSM_MODE_TX
+ * ENSM_MODE_RX
+ * ENSM_MODE_ALERT
+ * ENSM_MODE_FDD
+ * ENSM_MODE_WAIT
+ * ENSM_MODE_SLEEP
+ * ENSM_MODE_PINCTRL
+ * ENSM_MODE_PINCTRL_FDD_INDEP
+ * @return 0 in case of success, negative error code otherwise.
+ *
+ * Note: This function will/may affect the data path.
+ */
+int32_t ad9361_set_en_state_machine_mode (struct ad9361_rf_phy *phy,
+ uint32_t mode)
+{
+ int32_t ret;
+ uint8_t ensm_state;
+ bool pinctrl = false;
+
+ phy->pdata->fdd_independent_mode = false;
+
+ switch (mode) {
+ case ENSM_MODE_TX:
+ ensm_state = ENSM_STATE_TX;
+ break;
+ case ENSM_MODE_RX:
+ ensm_state = ENSM_STATE_RX;
+ break;
+ case ENSM_MODE_ALERT:
+ ensm_state = ENSM_STATE_ALERT;
+ break;
+ case ENSM_MODE_FDD:
+ ensm_state = ENSM_STATE_FDD;
+ break;
+ case ENSM_MODE_WAIT:
+ ensm_state = ENSM_STATE_SLEEP_WAIT;
+ break;
+ case ENSM_MODE_SLEEP:
+ ensm_state = ENSM_STATE_SLEEP;
+ break;
+ case ENSM_MODE_PINCTRL:
+ ensm_state = ENSM_STATE_SLEEP_WAIT;
+ pinctrl = true;
+ break;
+ case ENSM_MODE_PINCTRL_FDD_INDEP:
+ ensm_state = ENSM_STATE_FDD;
+ phy->pdata->fdd_independent_mode = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ad9361_set_ensm_mode(phy, phy->pdata->fdd, pinctrl);
+ ret = ad9361_ensm_set_state(phy, ensm_state, pinctrl);
+
+ return ret;
+}
+
+/**
+ * Get the Enable State Machine (ENSM) mode.
+ * @param phy The AD9361 current state structure.
+ * @param mode A variable to store the selected ENSM mode.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_en_state_machine_mode (struct ad9361_rf_phy *phy,
+ uint32_t *mode)
+{
+ uint8_t ensm_state;
+ bool pinctrl = false;
+ int32_t ret;
+
+ ensm_state = ad9361_spi_read(phy->spi, REG_STATE);
+ ensm_state &= ENSM_STATE(~0);
+ ret = ad9361_spi_read(phy->spi, REG_ENSM_CONFIG_1);
+ if ((ret & ENABLE_ENSM_PIN_CTRL) == ENABLE_ENSM_PIN_CTRL)
+ pinctrl = true;
+
+ switch (ensm_state) {
+ case ENSM_STATE_TX:
+ *mode = ENSM_MODE_TX;
+ break;
+ case ENSM_STATE_RX:
+ *mode = ENSM_MODE_RX;
+ break;
+ case ENSM_STATE_ALERT:
+ *mode = ENSM_MODE_ALERT;
+ break;
+ case ENSM_STATE_FDD:
+ if (phy->pdata->fdd_independent_mode)
+ *mode = ENSM_MODE_PINCTRL_FDD_INDEP;
+ else
+ *mode = ENSM_MODE_FDD;
+ break;
+ case ENSM_STATE_SLEEP_WAIT:
+ if (pinctrl)
+ *mode = ENSM_MODE_PINCTRL;
+ else
+ *mode = ENSM_MODE_WAIT;
+ break;
+ case ENSM_STATE_SLEEP:
+ *mode = ENSM_MODE_SLEEP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * Set the receive RF gain for the selected channel.
+ * @param phy The AD9361 current state structure.
+ * @param ch The desired channel number (RX1, RX2).
+ * Accepted values in 2x2 mode:
+ * RX1 (0)
+ * RX2 (1)
+ * Accepted values in 1x1 mode:
+ * RX1 (0)
+ * @param gain_db The RF gain (dB).
+ * Example:
+ * 10 (10 dB)
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_set_rx_rf_gain (struct ad9361_rf_phy *phy,
+ uint8_t ch, int32_t gain_db)
+{
+ struct rf_rx_gain rx_gain = {0};
+ int32_t ret = 0;
+
+ if ((phy->pdata->rx2tx2 == 0) && (ch == RX2)) {
+ printf("%s : RX2 is an invalid option in 1x1 mode!\n", __func__);
+ return -1;
+ }
+
+ rx_gain.gain_db = gain_db;
+ ret = ad9361_set_rx_gain(phy,
+ ad9361_1rx1tx_channel_map(phy, false,
+ ch + 1), &rx_gain);
+
+ return ret;
+}
+
+/**
+ * Get current receive RF gain for the selected channel.
+ * @param phy The AD9361 current state structure.
+ * @param ch The desired channel number (RX1, RX2).
+ * Accepted values in 2x2 mode:
+ * RX1 (0)
+ * RX2 (1)
+ * Accepted values in 1x1 mode:
+ * RX1 (0)
+ * @param gain_db A variable to store the RF gain (dB).
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_rx_rf_gain (struct ad9361_rf_phy *phy,
+ uint8_t ch, int32_t *gain_db)
+{
+ struct rf_rx_gain rx_gain = {0};
+ int32_t ret = 0;
+
+ if ((phy->pdata->rx2tx2 == 0) && (ch == RX2)) {
+ printf("%s : RX2 is an invalid option in 1x1 mode!\n", __func__);
+ return -1;
+ }
+
+ ret = ad9361_get_rx_gain(phy, ad9361_1rx1tx_channel_map(phy,
+ false, ch + 1), &rx_gain);
+
+ *gain_db = rx_gain.gain_db;
+
+ return ret;
+}
+
+/**
+ * Set the RX RF bandwidth.
+ * @param phy The AD9361 current state structure.
+ * @param bandwidth_hz The desired bandwidth (Hz).
+ * Example:
+ * 18000000 (18 MHz)
+ * @return 0 in case of success, negative error code otherwise.
+ *
+ * Note: This function will/may affect the data path.
+ */
+int32_t ad9361_set_rx_rf_bandwidth (struct ad9361_rf_phy *phy,
+ uint32_t bandwidth_hz)
+{
+ int32_t ret = 0;
+
+ bandwidth_hz = ad9361_validate_rf_bw(phy, bandwidth_hz);
+
+ if (phy->current_rx_bw_Hz != bandwidth_hz)
+ ret = ad9361_update_rf_bandwidth(phy, bandwidth_hz,
+ phy->current_tx_bw_Hz);
+ else
+ ret = 0;
+
+ return ret;
+}
+
+/**
+ * Get the RX RF bandwidth.
+ * @param phy The AD9361 current state structure.
+ * @param bandwidth_hz A variable to store the bandwidth value (Hz).
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_rx_rf_bandwidth (struct ad9361_rf_phy *phy,
+ uint32_t *bandwidth_hz)
+{
+ *bandwidth_hz = phy->current_rx_bw_Hz;
+
+ return 0;
+}
+
+/**
+ * Set the RX sampling frequency.
+ * @param phy The AD9361 current state structure.
+ * @param sampling_freq_hz The desired frequency (Hz).
+ * Example:
+ * 30720000 (30.72 MHz)
+ * @return 0 in case of success, negative error code otherwise.
+ *
+ * Note: This function will/may affect the data path.
+ */
+int32_t ad9361_set_rx_sampling_freq (struct ad9361_rf_phy *phy,
+ uint32_t sampling_freq_hz)
+{
+ int32_t ret;
+ uint32_t rx[6], tx[6];
+
+ ret = ad9361_calculate_rf_clock_chain(phy, sampling_freq_hz,
+ phy->rate_governor, rx, tx);
+ if (ret < 0)
+ return ret;
+
+ ad9361_set_trx_clock_chain(phy, rx, tx);
+
+ ret = ad9361_update_rf_bandwidth(phy, phy->current_rx_bw_Hz,
+ phy->current_tx_bw_Hz);
+
+ return ret;
+}
+
+/**
+ * Get current RX sampling frequency.
+ * @param phy The AD9361 current state structure.
+ * @param sampling_freq_hz A variable to store the frequency value (Hz).
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_rx_sampling_freq (struct ad9361_rf_phy *phy,
+ uint32_t *sampling_freq_hz)
+{
+ *sampling_freq_hz = (uint32_t)clk_get_rate(phy,
+ phy->ref_clk_scale[RX_SAMPL_CLK]);
+
+ return 0;
+}
+
+/**
+ * Set the RX LO frequency.
+ * @param phy The AD9361 current state structure.
+ * @param lo_freq_hz The desired frequency (Hz).
+ * Example:
+ * 2400000000 (2.4 GHz)
+ * @return 0 in case of success, negative error code otherwise.
+ *
+ * Note: This function will/may affect the data path.
+ */
+int32_t ad9361_set_rx_lo_freq (struct ad9361_rf_phy *phy,
+ uint64_t lo_freq_hz)
+{
+ int32_t ret;
+
+ ret = clk_set_rate(phy, phy->ref_clk_scale[RX_RFPLL],
+ ad9361_to_clk(lo_freq_hz));
+
+ return ret;
+}
+
+/**
+ * Get current RX LO frequency.
+ * @param phy The AD9361 current state structure.
+ * @param lo_freq_hz A variable to store the frequency value (Hz).
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_rx_lo_freq (struct ad9361_rf_phy *phy,
+ uint64_t *lo_freq_hz)
+{
+ *lo_freq_hz = ad9361_from_clk(clk_get_rate(phy,
+ phy->ref_clk_scale[RX_RFPLL]));
+
+ return 0;
+}
+
+/**
+ * Switch between internal and external LO.
+ * @param phy The AD9361 state structure.
+ * @param int_ext The selected lo (INT_LO, EXT_LO).
+ * Accepted values:
+ * INT_LO
+ * EXT_LO
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_set_rx_lo_int_ext(struct ad9361_rf_phy *phy, uint8_t int_ext)
+{
+ if ((phy->dev_sel == ID_AD9363A) && (int_ext = EXT_LO)) {
+ printf("%s : EXT_LO is not supported by AD9363!\n", __func__);
+ return -1;
+ }
+
+ phy->pdata->use_ext_rx_lo = int_ext;
+
+ return ad9361_clk_mux_set_parent(phy->ref_clk_scale[RX_RFPLL], int_ext);
+}
+
+/**
+ * Get the RSSI for the selected channel.
+ * @param phy The AD9361 current state structure.
+ * @param ch The desired channel (RX1, RX2).
+ * Accepted values in 2x2 mode:
+ * RX1 (0)
+ * RX2 (1)
+ * Accepted values in 1x1 mode:
+ * RX1 (0)
+ * @param rssi A variable to store the RSSI.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_rx_rssi (struct ad9361_rf_phy *phy,
+ uint8_t ch, struct rf_rssi *rssi)
+{
+ int32_t ret;
+
+ if ((phy->pdata->rx2tx2 == 0) && (ch == RX2)) {
+ printf("%s : RX2 is an invalid option in 1x1 mode!\n", __func__);
+ return -1;
+ }
+
+ rssi->ant = ad9361_1rx1tx_channel_map(phy, false, ch + 1);
+ rssi->duration = 1;
+ ret = ad9361_read_rssi(phy, rssi);
+
+ return ret;
+}
+
+/**
+ * Set the gain control mode for the selected channel.
+ * @param phy The AD9361 current state structure.
+ * @param ch The desired channel (RX1, RX2).
+ * Accepted values in 2x2 mode:
+ * RX1 (0)
+ * RX2 (1)
+ * Accepted values in 1x1 mode:
+ * RX1 (0)
+ * @param gc_mode The gain control mode (manual, fast_attack, slow_attack,
+ * hybrid).
+ * Accepted values:
+ * RF_GAIN_MGC (manual)
+ * RF_GAIN_FASTATTACK_AGC (fast_attack)
+ * RF_GAIN_SLOWATTACK_AGC (slow_attack)
+ * RF_GAIN_HYBRID_AGC (hybrid)
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_set_rx_gain_control_mode (struct ad9361_rf_phy *phy,
+ uint8_t ch, uint8_t gc_mode)
+{
+ struct rf_gain_ctrl gc = {0};
+
+ if ((phy->pdata->rx2tx2 == 0) && (ch == RX2)) {
+ printf("%s : RX2 is an invalid option in 1x1 mode!\n", __func__);
+ return -1;
+ }
+
+ gc.ant = ad9361_1rx1tx_channel_map(phy, false, ch + 1);
+ gc.mode = phy->agc_mode[ch] = gc_mode;
+
+ ad9361_set_gain_ctrl_mode(phy, &gc);
+
+ return 0;
+}
+
+/**
+ * Get the gain control mode for the selected channel.
+ * @param phy The AD9361 current state structure.
+ * @param ch The desired channel (RX1, RX2).
+ * Accepted values:
+ * RX1 (0)
+ * RX2 (1)
+ * @param gc_mode A variable to store the gain control mode.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_rx_gain_control_mode (struct ad9361_rf_phy *phy,
+ uint8_t ch, uint8_t *gc_mode)
+{
+ *gc_mode = phy->agc_mode[ch];
+
+ return 0;
+}
+
+/**
+ * Set the RX FIR filter configuration.
+ * @param phy The AD9361 current state structure.
+ * @param fir_cfg FIR filter configuration.
+ * @return 0 in case of success, negative error code otherwise.
+ *
+ * Note: This function will/may affect the data path.
+ */
+int32_t ad9361_set_rx_fir_config (struct ad9361_rf_phy *phy,
+ AD9361_RXFIRConfig fir_cfg)
+{
+ int32_t ret;
+
+ phy->rx_fir_dec = fir_cfg.rx_dec;
+ ret = ad9361_load_fir_filter_coef(phy, (enum fir_dest)(fir_cfg.rx | FIR_IS_RX),
+ fir_cfg.rx_gain, fir_cfg.rx_coef_size, fir_cfg.rx_coef);
+
+ return ret;
+}
+
+/**
+ * Get the RX FIR filter configuration.
+ * @param phy The AD9361 current state structure.
+ * @param tx_ch The selected RX channel (RX1, RX2).
+ * Accepted values:
+ * RX1 (0)
+ * RX2 (1)
+ * @param fir_cfg FIR filter configuration output file.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_rx_fir_config(struct ad9361_rf_phy *phy, uint8_t rx_ch, AD9361_RXFIRConfig *fir_cfg)
+{
+ int32_t ret;
+ uint32_t fir_conf;
+ uint8_t index;
+
+ rx_ch += 1;
+
+ ret = ad9361_spi_read(phy->spi, REG_RX_FILTER_CONFIG);
+ if(ret < 0)
+ return ret;
+ fir_conf = ret;
+
+ fir_cfg->rx_coef_size = (((fir_conf & FIR_NUM_TAPS(7)) >> 5) + 1) * 16;
+
+ ret = ad9361_spi_read(phy->spi, REG_RX_FILTER_GAIN);
+ if(ret < 0)
+ return ret;
+ fir_cfg->rx_gain = -6 * (ret & FILTER_GAIN(3)) + 6;
+ fir_cfg->rx = rx_ch;
+
+ fir_conf &= ~FIR_SELECT(3);
+ fir_conf |= FIR_SELECT(rx_ch) | FIR_START_CLK;
+ ad9361_spi_write(phy->spi, REG_RX_FILTER_CONFIG, fir_conf);
+
+ for(index = 0; index < 128; index++)
+ {
+ ad9361_spi_write(phy->spi, REG_RX_FILTER_COEF_ADDR, index);
+ ret = ad9361_spi_read(phy->spi, REG_RX_FILTER_COEF_READ_DATA_1);
+ if(ret < 0)
+ return ret;
+ fir_cfg->rx_coef[index] = ret;
+ ret = ad9361_spi_read(phy->spi, REG_RX_FILTER_COEF_READ_DATA_2);
+ if(ret < 0)
+ return ret;
+ fir_cfg->rx_coef[index] |= (ret << 8);
+ }
+
+ fir_conf &= ~FIR_START_CLK;
+ ad9361_spi_write(phy->spi, REG_RX_FILTER_CONFIG, fir_conf);
+
+ fir_cfg->rx_dec = phy->rx_fir_dec;
+
+ return 0;
+}
+
+/**
+ * Enable/disable the RX FIR filter.
+ * @param phy The AD9361 current state structure.
+ * @param en_dis The option (ENABLE, DISABLE).
+ * Accepted values:
+ * ENABLE (1)
+ * DISABLE (0)
+ * @return 0 in case of success, negative error code otherwise.
+ *
+ * Note: This function will/may affect the data path.
+ */
+int32_t ad9361_set_rx_fir_en_dis (struct ad9361_rf_phy *phy,
+ uint8_t en_dis)
+{
+ int32_t ret = 0;
+
+ if(phy->bypass_rx_fir == !en_dis)
+ return ret;
+
+ phy->bypass_rx_fir = !en_dis;
+ ret = ad9361_validate_enable_fir(phy);
+ if (ret < 0) {
+ phy->bypass_rx_fir = true;
+ }
+
+ return ret;
+}
+
+/**
+ * Get the status of the RX FIR filter.
+ * @param phy The AD9361 current state structure.
+ * @param en_dis The enable/disable status buffer.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_rx_fir_en_dis (struct ad9361_rf_phy *phy,
+ uint8_t *en_dis)
+{
+ *en_dis = !phy->bypass_rx_fir;
+
+ return 0;
+}
+
+/**
+ * Enable/disable the RX RFDC Tracking.
+ * @param phy The AD9361 current state structure.
+ * @param en_dis The option (ENABLE, DISABLE).
+ * Accepted values:
+ * ENABLE (1)
+ * DISABLE (0)
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_set_rx_rfdc_track_en_dis (struct ad9361_rf_phy *phy,
+ uint8_t en_dis)
+{
+ int32_t ret = 0;
+
+ if(phy->rfdc_track_en == en_dis)
+ return ret;
+
+ phy->rfdc_track_en = en_dis;
+ ret = ad9361_tracking_control(phy, phy->bbdc_track_en,
+ phy->rfdc_track_en, phy->quad_track_en);
+
+ return ret;
+}
+
+/**
+ * Get the status of the RX RFDC Tracking.
+ * @param phy The AD9361 current state structure.
+ * @param en_dis The enable/disable status buffer.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_rx_rfdc_track_en_dis (struct ad9361_rf_phy *phy,
+ uint8_t *en_dis)
+{
+ *en_dis = phy->rfdc_track_en;
+
+ return 0;
+}
+
+/**
+ * Enable/disable the RX BasebandDC Tracking.
+ * @param phy The AD9361 current state structure.
+ * @param en_dis The option (ENABLE, DISABLE).
+ * Accepted values:
+ * ENABLE (1)
+ * DISABLE (0)
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_set_rx_bbdc_track_en_dis (struct ad9361_rf_phy *phy,
+ uint8_t en_dis)
+{
+ int32_t ret = 0;
+
+ if(phy->bbdc_track_en == en_dis)
+ return ret;
+
+ phy->bbdc_track_en = en_dis;
+ ret = ad9361_tracking_control(phy, phy->bbdc_track_en,
+ phy->rfdc_track_en, phy->quad_track_en);
+
+ return ret;
+}
+
+/**
+ * Get the status of the RX BasebandDC Tracking.
+ * @param phy The AD9361 current state structure.
+ * @param en_dis The enable/disable status buffer.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_rx_bbdc_track_en_dis (struct ad9361_rf_phy *phy,
+ uint8_t *en_dis)
+{
+ *en_dis = phy->bbdc_track_en;
+
+ return 0;
+}
+
+/**
+ * Enable/disable the RX Quadrature Tracking.
+ * @param phy The AD9361 current state structure.
+ * @param en_dis The option (ENABLE, DISABLE).
+ * Accepted values:
+ * ENABLE (1)
+ * DISABLE (0)
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_set_rx_quad_track_en_dis (struct ad9361_rf_phy *phy,
+ uint8_t en_dis)
+{
+ int32_t ret = 0;
+
+ if(phy->quad_track_en == en_dis)
+ return ret;
+
+ phy->quad_track_en = en_dis;
+ ret = ad9361_tracking_control(phy, phy->bbdc_track_en,
+ phy->rfdc_track_en, phy->quad_track_en);
+
+ return ret;
+}
+
+/**
+ * Get the status of the RX Quadrature Tracking.
+ * @param phy The AD9361 current state structure.
+ * @param en_dis The enable/disable status buffer.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_rx_quad_track_en_dis (struct ad9361_rf_phy *phy,
+ uint8_t *en_dis)
+{
+ *en_dis = phy->quad_track_en;
+
+ return 0;
+}
+
+/**
+ * Set the RX RF input port.
+ * @param phy The AD9361 current state structure.
+ * @param mode The RF port.
+ * Accepted values:
+ * A_BALANCED (0 - (RX1A_N & RX1A_P) and (RX2A_N & RX2A_P) enabled; balanced)
+ * B_BALANCED (1 - (RX1B_N & RX1B_P) and (RX2B_N & RX2B_P) enabled; balanced)
+ * C_BALANCED (2 - (RX1C_N & RX1C_P) and (RX2C_N & RX2C_P) enabled; balanced)
+ * A_N (3 - RX1A_N and RX2A_N enabled; unbalanced)
+ * A_P (4 - RX1A_P and RX2A_P enabled; unbalanced)
+ * B_N (5 - RX1B_N and RX2B_N enabled; unbalanced)
+ * B_P (6 - RX1B_P and RX2B_P enabled; unbalanced)
+ * C_N (7 - RX1C_N and RX2C_N enabled; unbalanced)
+ * C_P (8 - RX1C_P and RX2C_P enabled; unbalanced)
+ * TX_MON1 (9 - TX_MONITOR1)
+ * TX_MON2 (10 - TX_MONITOR2)
+ * TX_MON1_2 (11 - TX_MONITOR1 & TX_MONITOR2)
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_set_rx_rf_port_input (struct ad9361_rf_phy *phy,
+ uint32_t mode)
+{
+ int32_t ret;
+
+ phy->pdata->rf_rx_input_sel = mode;
+
+ ret = ad9361_rf_port_setup(phy, false,
+ phy->pdata->rf_rx_input_sel,
+ phy->pdata->rf_tx_output_sel);
+
+ return ret;
+}
+
+/**
+ * Get the selected RX RF input port.
+ * @param phy The AD9361 current state structure.
+ * @param mode The RF port.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_rx_rf_port_input (struct ad9361_rf_phy *phy,
+ uint32_t *mode)
+{
+ *mode = phy->pdata->rf_rx_input_sel;
+
+ return 0;
+}
+
+/**
+ * Store RX fastlock profile.
+ * To create a profile tune the synthesizer (ad9361_set_rx_lo_freq()) and then
+ * call this function specifying the target profile number.
+ * @param phy The AD9361 state structure.
+ * @param profile The profile number (0 - 7).
+ * Accepted values:
+ * 0 - 7
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_rx_fastlock_store(struct ad9361_rf_phy *phy, uint32_t profile)
+{
+ return ad9361_fastlock_store(phy, 0, profile);
+}
+
+/**
+ * Recall specified RX fastlock profile.
+ * When in fastlock pin select mode (init_param->rx_fastlock_pincontrol_enable),
+ * the function needs to be called before then the pin-control can be used.
+ * @param phy The AD9361 state structure.
+ * @param profile The profile number (0 - 7).
+ * Accepted values:
+ * 0 - 7
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_rx_fastlock_recall(struct ad9361_rf_phy *phy, uint32_t profile)
+{
+ return ad9361_fastlock_recall(phy, 0, profile);
+}
+
+/**
+ * Load RX fastlock profile. A previously saved profile can be loaded in any
+ * of the 8 available slots.
+ * @param phy The AD9361 state structure.
+ * @param profile The profile number (0 - 7).
+ * Accepted values:
+ * 0 - 7
+ * @param values Fastlock profile program data.
+ * Example:
+ * val0,val1,val2,Â…,val15
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_rx_fastlock_load(struct ad9361_rf_phy *phy, uint32_t profile, uint8_t *values)
+{
+ return ad9361_fastlock_load(phy, 0, profile, values);
+}
+
+/**
+ * Save RX fastlock profile. In order to use more than 8 Profiles, an existing
+ * profile can be read back and stored by the user application.
+ * @param phy The AD9361 state structure.
+ * @param profile The profile number (0 - 7).
+ * Accepted values:
+ * 0 - 7
+ * @param values Fastlock profile program data.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_rx_fastlock_save(struct ad9361_rf_phy *phy, uint32_t profile, uint8_t *values)
+{
+ return ad9361_fastlock_save(phy, 0, profile, values);
+}
+
+/**
+ * Set the transmit attenuation for the selected channel.
+ * @param phy The AD9361 current state structure.
+ * @param ch The desired channel number (TX1, TX2).
+ * Accepted values in 2x2 mode:
+ * TX1 (0)
+ * TX2 (1)
+ * Accepted values in 1x1 mode:
+ * TX1 (0)
+ * @param attenuation_mdb The attenuation (mdB).
+ * Example:
+ * 10000 (10 dB)
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_set_tx_attenuation (struct ad9361_rf_phy *phy,
+ uint8_t ch, uint32_t attenuation_mdb)
+{
+ int32_t ret;
+ int32_t channel;
+
+ if ((phy->pdata->rx2tx2 == 0) && (ch == TX2)) {
+ printf("%s : TX2 is an invalid option in 1x1 mode!\n", __func__);
+ return -1;
+ }
+
+ channel = ad9361_1rx1tx_channel_map(phy, true, ch);
+ ret = ad9361_set_tx_atten(phy, attenuation_mdb,
+ channel == 0, channel == 1,
+ !phy->pdata->update_tx_gain_via_alert);
+
+ return ret;
+}
+
+/**
+ * Get current transmit attenuation for the selected channel.
+ * @param phy The AD9361 current state structure.
+ * @param ch The desired channel number (TX1, TX2).
+ * Accepted values in 2x2 mode:
+ * TX1 (0)
+ * TX2 (1)
+ * Accepted values in 1x1 mode:
+ * TX1 (0)
+ * @param attenuation_mdb A variable to store the attenuation value (mdB).
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_tx_attenuation (struct ad9361_rf_phy *phy,
+ uint8_t ch, uint32_t *attenuation_db)
+{
+ int32_t ret;
+
+ if ((phy->pdata->rx2tx2 == 0) && (ch == TX2)) {
+ printf("%s : TX2 is an invalid option in 1x1 mode!\n", __func__);
+ return -1;
+ }
+
+ ret = ad9361_get_tx_atten(phy,
+ ad9361_1rx1tx_channel_map(phy, true,
+ ch + 1));
+
+ if(ret < 0)
+ return ret;
+ *attenuation_db = ret;
+
+ return 0;
+}
+
+/**
+ * Set the TX RF bandwidth.
+ * @param phy The AD9361 current state structure.
+ * @param bandwidth_hz The desired bandwidth (Hz).
+ * Example:
+ * 18000000 (18 MHz)
+ * @return 0 in case of success, negative error code otherwise.
+ *
+ * Note: This function will/may affect the data path.
+ */
+int32_t ad9361_set_tx_rf_bandwidth (struct ad9361_rf_phy *phy,
+ uint32_t bandwidth_hz)
+{
+ int32_t ret = 0;
+
+ bandwidth_hz = ad9361_validate_rf_bw(phy, bandwidth_hz);
+
+ if (phy->current_tx_bw_Hz != bandwidth_hz)
+ ret = ad9361_update_rf_bandwidth(phy,
+ phy->current_rx_bw_Hz, bandwidth_hz);
+ else
+ ret = 0;
+
+ return ret;
+}
+
+/**
+ * Get the TX RF bandwidth.
+ * @param phy The AD9361 current state structure.
+ * @param bandwidth_hz A variable to store the bandwidth value (Hz).
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_tx_rf_bandwidth (struct ad9361_rf_phy *phy,
+ uint32_t *bandwidth_hz)
+{
+ *bandwidth_hz = phy->current_tx_bw_Hz;
+
+ return 0;
+}
+
+/**
+ * Set the TX sampling frequency.
+ * @param phy The AD9361 current state structure.
+ * @param sampling_freq_hz The desired frequency (Hz).
+ * Example:
+ * 30720000 (30.72 MHz)
+ * @return 0 in case of success, negative error code otherwise.
+ *
+ * Note: This function will/may affect the data path.
+ */
+int32_t ad9361_set_tx_sampling_freq (struct ad9361_rf_phy *phy,
+ uint32_t sampling_freq_hz)
+{
+ int32_t ret;
+ uint32_t rx[6], tx[6];
+
+ ret = ad9361_calculate_rf_clock_chain(phy, sampling_freq_hz,
+ phy->rate_governor, rx, tx);
+ if (ret < 0)
+ return ret;
+
+ ad9361_set_trx_clock_chain(phy, rx, tx);
+
+ ret = ad9361_update_rf_bandwidth(phy, phy->current_rx_bw_Hz,
+ phy->current_tx_bw_Hz);
+
+ return ret;
+}
+
+/**
+ * Get current TX sampling frequency.
+ * @param phy The AD9361 current state structure.
+ * @param sampling_freq_hz A variable to store the frequency value (Hz).
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_tx_sampling_freq (struct ad9361_rf_phy *phy,
+ uint32_t *sampling_freq_hz)
+{
+ *sampling_freq_hz = (uint32_t)clk_get_rate(phy,
+ phy->ref_clk_scale[TX_SAMPL_CLK]);
+
+ return 0;
+}
+
+/**
+ * Set the TX LO frequency.
+ * @param phy The AD9361 current state structure.
+ * @param lo_freq_hz The desired frequency (Hz).
+ * Example:
+ * 2400000000 (2.4 GHz)
+ * @return 0 in case of success, negative error code otherwise.
+ *
+ * Note: This function will/may affect the data path.
+ */
+int32_t ad9361_set_tx_lo_freq (struct ad9361_rf_phy *phy,
+ uint64_t lo_freq_hz)
+{
+ int32_t ret;
+
+ ret = clk_set_rate(phy, phy->ref_clk_scale[TX_RFPLL],
+ ad9361_to_clk(lo_freq_hz));
+
+ return ret;
+}
+
+/**
+ * Get current TX LO frequency.
+ * @param phy The AD9361 current state structure.
+ * @param lo_freq_hz A variable to store the frequency value (Hz).
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_tx_lo_freq (struct ad9361_rf_phy *phy,
+ uint64_t *lo_freq_hz)
+{
+ *lo_freq_hz = ad9361_from_clk(clk_get_rate(phy,
+ phy->ref_clk_scale[TX_RFPLL]));
+
+ return 0;
+}
+
+/**
+ * Switch between internal and external LO.
+ * @param phy The AD9361 state structure.
+ * @param int_ext The selected lo (INT_LO, EXT_LO).
+ * Accepted values:
+ * INT_LO
+ * EXT_LO
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_set_tx_lo_int_ext(struct ad9361_rf_phy *phy, uint8_t int_ext)
+{
+ if ((phy->dev_sel == ID_AD9363A) && (int_ext = EXT_LO)) {
+ printf("%s : EXT_LO is not supported by AD9363!\n", __func__);
+ return -1;
+ }
+
+ phy->pdata->use_ext_tx_lo = int_ext;
+
+ return ad9361_clk_mux_set_parent(phy->ref_clk_scale[TX_RFPLL], int_ext);
+}
+
+/**
+ * Set the TX FIR filter configuration.
+ * @param phy The AD9361 current state structure.
+ * @param fir_cfg FIR filter configuration.
+ * @return 0 in case of success, negative error code otherwise.
+ *
+ * Note: This function will/may affect the data path.
+ */
+int32_t ad9361_set_tx_fir_config (struct ad9361_rf_phy *phy,
+ AD9361_TXFIRConfig fir_cfg)
+{
+ int32_t ret;
+
+ phy->tx_fir_int = fir_cfg.tx_int;
+ ret = ad9361_load_fir_filter_coef(phy, (enum fir_dest)fir_cfg.tx,
+ fir_cfg.tx_gain, fir_cfg.tx_coef_size, fir_cfg.tx_coef);
+
+ return ret;
+}
+
+/**
+ * Get the TX FIR filter configuration.
+ * @param phy The AD9361 current state structure.
+ * @param tx_ch The selected TX channel (TX1, TX2).
+ * Accepted values:
+ * TX1 (0)
+ * TX2 (1)
+ * @param fir_cfg FIR filter configuration output file.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_tx_fir_config(struct ad9361_rf_phy *phy, uint8_t tx_ch, AD9361_TXFIRConfig *fir_cfg)
+{
+ int32_t ret;
+ uint32_t fir_conf;
+ uint8_t index;
+
+ tx_ch += 1;
+
+ ret = ad9361_spi_read(phy->spi, REG_TX_FILTER_CONF);
+ if(ret < 0)
+ return ret;
+ fir_conf = ret;
+ fir_cfg->tx_coef_size = (((fir_conf & FIR_NUM_TAPS(7)) >> 5) + 1) * 16;
+ fir_cfg->tx_gain = -6 * (fir_conf & TX_FIR_GAIN_6DB);
+ fir_cfg->tx = tx_ch;
+
+ fir_conf &= ~FIR_SELECT(3);
+ fir_conf |= FIR_SELECT(tx_ch) | FIR_START_CLK;
+ ad9361_spi_write(phy->spi, REG_TX_FILTER_CONF, fir_conf);
+
+ for(index = 0; index < 128; index++)
+ {
+ ad9361_spi_write(phy->spi, REG_TX_FILTER_COEF_ADDR, index);
+ ret = ad9361_spi_read(phy->spi, REG_TX_FILTER_COEF_READ_DATA_1);
+ if(ret < 0)
+ return ret;
+ fir_cfg->tx_coef[index] = ret;
+ ret = ad9361_spi_read(phy->spi, REG_TX_FILTER_COEF_READ_DATA_2);
+ if(ret < 0)
+ return ret;
+ fir_cfg->tx_coef[index] |= (ret << 8);
+ }
+
+ fir_conf &= ~FIR_START_CLK;
+ ad9361_spi_write(phy->spi, REG_TX_FILTER_CONF, fir_conf);
+
+ fir_cfg->tx_int = phy->tx_fir_int;
+
+ return 0;
+}
+
+/**
+ * Enable/disable the TX FIR filter.
+ * @param phy The AD9361 current state structure.
+ * @param en_dis The option (ENABLE, DISABLE).
+ * Accepted values:
+ * ENABLE (1)
+ * DISABLE (0)
+ * @return 0 in case of success, negative error code otherwise.
+ *
+ * Note: This function will/may affect the data path.
+ */
+int32_t ad9361_set_tx_fir_en_dis (struct ad9361_rf_phy *phy,
+ uint8_t en_dis)
+{
+ int32_t ret = 0;
+
+ if(phy->bypass_tx_fir == !en_dis)
+ return ret;
+
+ phy->bypass_tx_fir = !en_dis;
+ ret = ad9361_validate_enable_fir(phy);
+ if (ret < 0) {
+ phy->bypass_tx_fir = true;
+ }
+
+ return ret;
+}
+
+/**
+ * Get the status of the TX FIR filter.
+ * @param phy The AD9361 current state structure.
+ * @param en_dis The enable/disable status buffer.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_tx_fir_en_dis (struct ad9361_rf_phy *phy,
+ uint8_t *en_dis)
+{
+ *en_dis = !phy->bypass_tx_fir;
+
+ return 0;
+}
+
+/**
+ * Get the TX RSSI for the selected channel (TX_MON should be enabled).
+ * @param phy The AD9361 current state structure.
+ * @param ch The desired channel (TX1, TX2).
+ * Accepted values:
+ * TX1 (0)
+ * TX2 (1)
+ * @param rssi_db_x_1000 A variable to store the RSSI.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_tx_rssi (struct ad9361_rf_phy *phy,
+ uint8_t ch,
+ uint32_t *rssi_db_x_1000)
+{
+ uint8_t reg_val_buf[3];
+ uint32_t val;
+ int32_t ret;
+
+ ret = ad9361_spi_readm(phy->spi, REG_TX_RSSI_LSB,
+ reg_val_buf, ARRAY_SIZE(reg_val_buf));
+ if (ret < 0) {
+ return ret;
+ }
+
+ switch (ch) {
+ case 0:
+ val = (reg_val_buf[2] << 1) | (reg_val_buf[0] & TX_RSSI_1);
+ break;
+ case 1:
+ val = (reg_val_buf[1] << 1) | ((reg_val_buf[0] & TX_RSSI_2) >> 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val *= RSSI_RESOLUTION;
+
+ *rssi_db_x_1000 = ((val / RSSI_MULTIPLIER) * 1000) +
+ (val % RSSI_MULTIPLIER);
+
+ return 0;
+}
+
+/**
+ * Set the TX RF output port.
+ * @param phy The AD9361 current state structure.
+ * @param mode The RF port.
+ * Accepted values:
+ * TXA (0)
+ * TXB (1)
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_set_tx_rf_port_output (struct ad9361_rf_phy *phy,
+ uint32_t mode)
+{
+ int32_t ret;
+
+ phy->pdata->rf_tx_output_sel = mode;
+
+ ret = ad9361_rf_port_setup(phy, true,
+ phy->pdata->rf_rx_input_sel,
+ phy->pdata->rf_tx_output_sel);
+
+ return ret;
+}
+
+/**
+ * Get the selected TX RF output port.
+ * @param phy The AD9361 current state structure.
+ * @param mode The RF port.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_tx_rf_port_output (struct ad9361_rf_phy *phy,
+ uint32_t *mode)
+{
+ *mode = phy->pdata->rf_tx_output_sel;
+
+ return 0;
+}
+
+/**
+ * Enable/disable the auto calibration.
+ * @param phy The AD9361 current state structure.
+ * @param en_dis The option (ENABLE, DISABLE).
+ * Accepted values:
+ * ENABLE (1)
+ * DISABLE (0)
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_set_tx_auto_cal_en_dis (struct ad9361_rf_phy *phy, uint8_t en_dis)
+{
+ if (en_dis == 0)
+ phy->auto_cal_en = 0;
+ else
+ phy->auto_cal_en = 1;
+
+ return 0;
+}
+
+/**
+ * Get the status of the auto calibration flag.
+ * @param phy The AD9361 current state structure.
+ * @param en_dis The enable/disable status buffer.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_tx_auto_cal_en_dis (struct ad9361_rf_phy *phy, uint8_t *en_dis)
+{
+ *en_dis = phy->auto_cal_en;
+
+ return 0;
+}
+
+/**
+ * Store TX fastlock profile.
+ * To create a profile tune the synthesizer (ad9361_set_tx_lo_freq()) and then
+ * call this function specifying the target profile number.
+ * @param phy The AD9361 state structure.
+ * @param profile The profile number (0 - 7).
+ * Accepted values:
+ * 0 - 7
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_tx_fastlock_store(struct ad9361_rf_phy *phy, uint32_t profile)
+{
+ return ad9361_fastlock_store(phy, 1, profile);
+}
+
+/**
+ * Recall specified TX fastlock profile.
+ * When in fastlock pin select mode (init_param->tx_fastlock_pincontrol_enable),
+ * the function needs to be called before then the pin-control can be used.
+ * @param phy The AD9361 state structure.
+ * @param profile The profile number (0 - 7).
+ * Accepted values:
+ * 0 - 7
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_tx_fastlock_recall(struct ad9361_rf_phy *phy, uint32_t profile)
+{
+ return ad9361_fastlock_recall(phy, 1, profile);
+}
+
+/**
+ * Load TX fastlock profile. A previously saved profile can be loaded in any
+ * of the 8 available slots.
+ * @param phy The AD9361 state structure.
+ * @param profile The profile number (0 - 7).
+ * Accepted values:
+ * 0 - 7
+ * @param values Fastlock profile program data.
+ * Example:
+ * val0,val1,val2,Â…,val15
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_tx_fastlock_load(struct ad9361_rf_phy *phy, uint32_t profile, uint8_t *values)
+{
+ return ad9361_fastlock_load(phy, 1, profile, values);
+}
+
+/**
+ * Save TX fastlock profile. In order to use more than 8 Profiles, an existing
+ * profile can be read back and stored by the user application.
+ * @param phy The AD9361 state structure.
+ * @param profile The profile number (0 - 7).
+ * Accepted values:
+ * 0 - 7
+ * @param values Fastlock profile program data.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_tx_fastlock_save(struct ad9361_rf_phy *phy, uint32_t profile, uint8_t *values)
+{
+ return ad9361_fastlock_save(phy, 1, profile, values);
+}
+
+/**
+ * Set the RX and TX path rates.
+ * @param phy The AD9361 state structure.
+ * @param rx_path_clks RX path rates buffer.
+ * @param tx_path_clks TX path rates buffer.
+ * @return 0 in case of success, negative error code otherwise.
+ *
+ * Note: This function will/may affect the data path.
+ */
+int32_t ad9361_set_trx_path_clks(struct ad9361_rf_phy *phy,
+ uint32_t *rx_path_clks,
+ uint32_t *tx_path_clks)
+{
+ int32_t ret;
+
+ ret = ad9361_set_trx_clock_chain(phy, rx_path_clks, tx_path_clks);
+ if (ret < 0)
+ return ret;
+
+ ret = ad9361_update_rf_bandwidth(phy, phy->current_rx_bw_Hz,
+ phy->current_tx_bw_Hz);
+
+ return ret;
+}
+
+/**
+ * Get the RX and TX path rates.
+ * @param phy The AD9361 state structure.
+ * @param rx_path_clks RX path rates buffer.
+ * @param tx_path_clks TX path rates buffer.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_trx_path_clks(struct ad9361_rf_phy *phy,
+ uint32_t *rx_path_clks,
+ uint32_t *tx_path_clks)
+{
+ return ad9361_get_trx_clock_chain(phy, rx_path_clks, tx_path_clks);
+}
+
+/**
+ * Set the number of channels mode.
+ * @param phy The AD9361 state structure.
+ * @param ch_mode Number of channels mode (MODE_1x1, MODE_2x2).
+ * Accepted values:
+ * MODE_1x1 (1)
+ * MODE_2x2 (2)
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_set_no_ch_mode(struct ad9361_rf_phy *phy, uint8_t no_ch_mode)
+{
+ switch (no_ch_mode) {
+ case 1:
+ phy->pdata->rx2tx2 = 0;
+ break;
+ case 2:
+ phy->pdata->rx2tx2 = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+#ifndef AXI_ADC_NOT_PRESENT
+ phy->adc_conv->chip_info = &axiadc_chip_info_tbl[phy->pdata->rx2tx2 ? ID_AD9361 : ID_AD9364];
+#endif
+ ad9361_reset(phy);
+ ad9361_spi_write(phy->spi, REG_SPI_CONF, SOFT_RESET | _SOFT_RESET);
+ ad9361_spi_write(phy->spi, REG_SPI_CONF, 0x0);
+
+ phy->clks[TX_REFCLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[TX_REFCLK], phy->clk_refin->rate);
+ phy->clks[TX_REFCLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[TX_REFCLK], phy->clk_refin->rate);
+ phy->clks[RX_REFCLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[RX_REFCLK], phy->clk_refin->rate);
+ phy->clks[BB_REFCLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[BB_REFCLK], phy->clk_refin->rate);
+ phy->clks[BBPLL_CLK]->rate = ad9361_bbpll_recalc_rate(phy->ref_clk_scale[BBPLL_CLK], phy->clks[BB_REFCLK]->rate);
+ phy->clks[ADC_CLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[ADC_CLK], phy->clks[BBPLL_CLK]->rate);
+ phy->clks[R2_CLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[R2_CLK], phy->clks[ADC_CLK]->rate);
+ phy->clks[R1_CLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[R1_CLK], phy->clks[R2_CLK]->rate);
+ phy->clks[CLKRF_CLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[CLKRF_CLK], phy->clks[R1_CLK]->rate);
+ phy->clks[RX_SAMPL_CLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[RX_SAMPL_CLK], phy->clks[CLKRF_CLK]->rate);
+ phy->clks[DAC_CLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[DAC_CLK], phy->clks[ADC_CLK]->rate);
+ phy->clks[T2_CLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[T2_CLK], phy->clks[DAC_CLK]->rate);
+ phy->clks[T1_CLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[T1_CLK], phy->clks[T2_CLK]->rate);
+ phy->clks[CLKTF_CLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[CLKTF_CLK], phy->clks[T1_CLK]->rate);
+ phy->clks[TX_SAMPL_CLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[TX_SAMPL_CLK], phy->clks[CLKTF_CLK]->rate);
+ phy->clks[RX_RFPLL_INT]->rate = ad9361_rfpll_int_recalc_rate(phy->ref_clk_scale[RX_RFPLL_INT], phy->clks[RX_REFCLK]->rate);
+ phy->clks[TX_RFPLL_INT]->rate = ad9361_rfpll_int_recalc_rate(phy->ref_clk_scale[TX_RFPLL_INT], phy->clks[TX_REFCLK]->rate);
+ phy->clks[RX_RFPLL_DUMMY]->rate = ad9361_rfpll_dummy_recalc_rate(phy->ref_clk_scale[RX_RFPLL_DUMMY]);
+ phy->clks[TX_RFPLL_DUMMY]->rate = ad9361_rfpll_dummy_recalc_rate(phy->ref_clk_scale[TX_RFPLL_DUMMY]);
+ phy->clks[RX_RFPLL]->rate = ad9361_rfpll_recalc_rate(phy->ref_clk_scale[RX_RFPLL]);
+ phy->clks[TX_RFPLL]->rate = ad9361_rfpll_recalc_rate(phy->ref_clk_scale[TX_RFPLL]);
+
+#ifndef AXI_ADC_NOT_PRESENT
+#ifdef NUAND_MODIFICATIONS
+ // use alternate axiadc accessors
+ axiadc_init(phy, phy->adc_state->userdata);
+#else
+ axiadc_init(phy);
+#endif // NUAND_MODIFICATIONS
+#endif
+
+ ad9361_setup(phy);
+#ifndef AXI_ADC_NOT_PRESENT
+ /* platform specific wrapper to call ad9361_post_setup() */
+ axiadc_post_setup(phy);
+#endif
+
+ return 0;
+}
+
+/**
+ * Do multi chip synchronization.
+ * @param phy_master The AD9361 Master state structure.
+ * @param phy_slave The AD9361 Slave state structure.
+ * @return 0 in case of success, negative error code otherwise.
+ *
+ * Note: This function will/may affect the data path.
+ */
+int32_t ad9361_do_mcs(struct ad9361_rf_phy *phy_master, struct ad9361_rf_phy *phy_slave)
+{
+ uint32_t ensm_mode;
+ int32_t step;
+ int32_t reg;
+
+ if ((phy_master->dev_sel == ID_AD9363A) ||
+ (phy_slave->dev_sel == ID_AD9363A)) {
+ printf("%s : MCS is not supported by AD9363!\n", __func__);
+ return -1;
+ }
+
+ reg = ad9361_spi_read(phy_master->spi, REG_RX_CLOCK_DATA_DELAY);
+ ad9361_spi_write(phy_slave->spi, REG_RX_CLOCK_DATA_DELAY, reg);
+ reg = ad9361_spi_read(phy_master->spi, REG_TX_CLOCK_DATA_DELAY);
+ ad9361_spi_write(phy_slave->spi, REG_TX_CLOCK_DATA_DELAY, reg);
+
+ ad9361_get_en_state_machine_mode(phy_master, &ensm_mode);
+
+ ad9361_set_en_state_machine_mode(phy_master, ENSM_MODE_ALERT);
+ ad9361_set_en_state_machine_mode(phy_slave, ENSM_MODE_ALERT);
+
+ for (step = 0; step <= 5; step++)
+ {
+ ad9361_mcs(phy_slave, step);
+ ad9361_mcs(phy_master, step);
+ mdelay(100);
+ }
+
+ ad9361_set_en_state_machine_mode(phy_master, ensm_mode);
+ ad9361_set_en_state_machine_mode(phy_slave, ensm_mode);
+
+ return 0;
+}
+
+/**
+ * Enable/disable the TRX FIR filters.
+ * @param phy The AD9361 current state structure.
+ * @param en_dis The option (ENABLE, DISABLE).
+ * Accepted values:
+ * ENABLE (1)
+ * DISABLE (0)
+ * @return 0 in case of success, negative error code otherwise.
+ *
+ * Note: This function will/may affect the data path.
+ */
+int32_t ad9361_set_trx_fir_en_dis (struct ad9361_rf_phy *phy,
+ uint8_t en_dis)
+{
+ int32_t ret = 0;
+
+ if ((phy->bypass_rx_fir == phy->bypass_tx_fir) &&
+ (phy->bypass_rx_fir == !en_dis))
+ return ret;
+
+ phy->bypass_rx_fir = !en_dis;
+ phy->bypass_tx_fir = !en_dis;
+ ret = ad9361_validate_enable_fir(phy);
+ if (ret < 0) {
+ phy->bypass_rx_fir = true;
+ phy->bypass_tx_fir = true;
+ }
+
+ return ret;
+}
+
+/**
+ * Set the OSR rate governor.
+ * @param phy The AD9361 current state structure.
+ * @param rate_gov OSR rate governor (highest, nominal).
+ * Accepted values:
+ * HIGHEST_OSR (0 - highest OSR)
+ * NOMINAL_OSR (1 - nominal)
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_set_trx_rate_gov (struct ad9361_rf_phy *phy, uint32_t rate_gov)
+{
+ if (rate_gov == 0)
+ phy->rate_governor = 0;
+ else
+ phy->rate_governor = 1;
+
+ return 0;
+}
+
+/**
+ * Get the OSR rate governor.
+ * @param phy The AD9361 current state structure.
+ * @param rate_gov Option buffer.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_get_trx_rate_gov (struct ad9361_rf_phy *phy, uint32_t *rate_gov)
+{
+ *rate_gov = phy->rate_governor;
+
+ return 0;
+}
+
+/**
+ * Perform the selected calibration.
+ * @param phy The AD9361 state structure.
+ * @param cal The selected calibration (TX_QUAD_CAL, RFDC_CAL).
+ * Accepted values:
+ * TX_QUAD_CAL
+ * RFDC_CAL
+ * @param arg For TX_QUAD_CAL - the optional RX phase value overwrite (set to zero).
+ * @return 0 in case of success, negative error code otherwise.
+ *
+ * Note: This function will/may affect the data path.
+ */
+int32_t ad9361_do_calib(struct ad9361_rf_phy *phy, uint32_t cal, int32_t arg)
+{
+ return ad9361_do_calib_run(phy, cal, arg);
+}
+
+/**
+ * Load and enable TRX FIR filters configurations.
+ * @param phy The AD9361 current state structure.
+ * @param rx_fir_cfg RX FIR filter configuration.
+ * @param tx_fir_cfg TX FIR filter configuration.
+ * @return 0 in case of success, negative error code otherwise.
+ *
+ * Note: This function will/may affect the data path.
+ */
+int32_t ad9361_trx_load_enable_fir(struct ad9361_rf_phy *phy,
+ AD9361_RXFIRConfig rx_fir_cfg,
+ AD9361_TXFIRConfig tx_fir_cfg)
+{
+ int32_t rtx = -1, rrx = -1;
+
+ phy->filt_rx_bw_Hz = 0;
+ phy->filt_tx_bw_Hz = 0;
+ phy->filt_valid = false;
+
+ if (tx_fir_cfg.tx_path_clks[TX_SAMPL_FREQ]) {
+ memcpy(phy->filt_tx_path_clks, tx_fir_cfg.tx_path_clks,
+ sizeof(phy->filt_tx_path_clks));
+ rtx = 0;
+ }
+
+ if (rx_fir_cfg.rx_path_clks[RX_SAMPL_FREQ]) {
+ memcpy(phy->filt_rx_path_clks, rx_fir_cfg.rx_path_clks,
+ sizeof(phy->filt_rx_path_clks));
+ rrx = 0;
+ }
+
+ if (tx_fir_cfg.tx_bandwidth) {
+ phy->filt_tx_bw_Hz = tx_fir_cfg.tx_bandwidth;
+ }
+
+ if (rx_fir_cfg.rx_bandwidth) {
+ phy->filt_rx_bw_Hz = rx_fir_cfg.rx_bandwidth;
+ }
+
+ ad9361_set_tx_fir_config(phy, tx_fir_cfg);
+ ad9361_set_rx_fir_config(phy, rx_fir_cfg);
+
+ if (!(rrx | rtx))
+ phy->filt_valid = true;
+
+ ad9361_set_trx_fir_en_dis(phy, 1);
+
+ return 0;
+}
+
+/**
+ * Do DCXO coarse tuning.
+ * @param phy The AD9361 current state structure.
+ * @param coarse The DCXO coarse tuning value.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_do_dcxo_tune_coarse(struct ad9361_rf_phy *phy,
+ uint32_t coarse)
+{
+ phy->pdata->dcxo_coarse = coarse;
+
+ return ad9361_set_dcxo_tune(phy, phy->pdata->dcxo_coarse,
+ phy->pdata->dcxo_fine);
+}
+
+/**
+ * Do DCXO fine tuning.
+ * @param phy The AD9361 current state structure.
+ * @param fine The DCXO fine tuning value.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_do_dcxo_tune_fine(struct ad9361_rf_phy *phy,
+ uint32_t fine)
+{
+ phy->pdata->dcxo_fine = fine;
+
+ return ad9361_set_dcxo_tune(phy, phy->pdata->dcxo_coarse,
+ phy->pdata->dcxo_fine);
+}
diff --git a/Radio/HW/BladeRF/common/thirdparty/ad936x/ad9361_api.h b/Radio/HW/BladeRF/common/thirdparty/ad936x/ad9361_api.h
new file mode 100644
index 0000000..ff5d7c5
--- /dev/null
+++ b/Radio/HW/BladeRF/common/thirdparty/ad936x/ad9361_api.h
@@ -0,0 +1,506 @@
+/***************************************************************************//**
+ * @file ad9361_api.h
+ * @brief Header file of AD9361 API Driver.
+ * @author DBogdan (dragos.bogdan@analog.com)
+********************************************************************************
+ * Copyright 2013(c) Analog Devices, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * - Neither the name of Analog Devices, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * - The use of this software may or may not infringe the patent rights
+ * of one or more patent holders. This license does not release you
+ * from the requirement that you obtain separate licenses from these
+ * patent holders to use this software.
+ * - Use of the software either in source or binary form, must be run
+ * on or directly connected to an Analog Devices Inc. component.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*******************************************************************************/
+#ifndef AD9361_API_H_
+#define AD9361_API_H_
+
+/******************************************************************************/
+/***************************** Include Files **********************************/
+/******************************************************************************/
+#include "util.h"
+
+/******************************************************************************/
+/*************************** Types Declarations *******************************/
+/******************************************************************************/
+typedef struct
+{
+ /* Device selection */
+ enum dev_id dev_sel;
+ /* Identification number */
+ uint8_t id_no;
+ /* Reference Clock */
+ uint32_t reference_clk_rate;
+ /* Base Configuration */
+ uint8_t two_rx_two_tx_mode_enable; /* adi,2rx-2tx-mode-enable */
+ uint8_t one_rx_one_tx_mode_use_rx_num; /* adi,1rx-1tx-mode-use-rx-num */
+ uint8_t one_rx_one_tx_mode_use_tx_num; /* adi,1rx-1tx-mode-use-tx-num */
+ uint8_t frequency_division_duplex_mode_enable; /* adi,frequency-division-duplex-mode-enable */
+ uint8_t frequency_division_duplex_independent_mode_enable; /* adi,frequency-division-duplex-independent-mode-enable */
+ uint8_t tdd_use_dual_synth_mode_enable; /* adi,tdd-use-dual-synth-mode-enable */
+ uint8_t tdd_skip_vco_cal_enable; /* adi,tdd-skip-vco-cal-enable */
+ uint32_t tx_fastlock_delay_ns; /* adi,tx-fastlock-delay-ns */
+ uint32_t rx_fastlock_delay_ns; /* adi,rx-fastlock-delay-ns */
+ uint8_t rx_fastlock_pincontrol_enable; /* adi,rx-fastlock-pincontrol-enable */
+ uint8_t tx_fastlock_pincontrol_enable; /* adi,tx-fastlock-pincontrol-enable */
+ uint8_t external_rx_lo_enable; /* adi,external-rx-lo-enable */
+ uint8_t external_tx_lo_enable; /* adi,external-tx-lo-enable */
+ uint8_t dc_offset_tracking_update_event_mask; /* adi,dc-offset-tracking-update-event-mask */
+ uint8_t dc_offset_attenuation_high_range; /* adi,dc-offset-attenuation-high-range */
+ uint8_t dc_offset_attenuation_low_range; /* adi,dc-offset-attenuation-low-range */
+ uint8_t dc_offset_count_high_range; /* adi,dc-offset-count-high-range */
+ uint8_t dc_offset_count_low_range; /* adi,dc-offset-count-low-range */
+ uint8_t split_gain_table_mode_enable; /* adi,split-gain-table-mode-enable */
+ uint32_t trx_synthesizer_target_fref_overwrite_hz; /* adi,trx-synthesizer-target-fref-overwrite-hz */
+ uint8_t qec_tracking_slow_mode_enable; /* adi,qec-tracking-slow-mode-enable */
+ /* ENSM Control */
+ uint8_t ensm_enable_pin_pulse_mode_enable; /* adi,ensm-enable-pin-pulse-mode-enable */
+ uint8_t ensm_enable_txnrx_control_enable; /* adi,ensm-enable-txnrx-control-enable */
+ /* LO Control */
+ uint64_t rx_synthesizer_frequency_hz; /* adi,rx-synthesizer-frequency-hz */
+ uint64_t tx_synthesizer_frequency_hz; /* adi,tx-synthesizer-frequency-hz */
+ /* Rate & BW Control */
+ uint32_t rx_path_clock_frequencies[6]; /* adi,rx-path-clock-frequencies */
+ uint32_t tx_path_clock_frequencies[6]; /* adi,tx-path-clock-frequencies */
+ uint32_t rf_rx_bandwidth_hz; /* adi,rf-rx-bandwidth-hz */
+ uint32_t rf_tx_bandwidth_hz; /* adi,rf-tx-bandwidth-hz */
+ /* RF Port Control */
+ uint32_t rx_rf_port_input_select; /* adi,rx-rf-port-input-select */
+ uint32_t tx_rf_port_input_select; /* adi,tx-rf-port-input-select */
+ /* TX Attenuation Control */
+ int32_t tx_attenuation_mdB; /* adi,tx-attenuation-mdB */
+ uint8_t update_tx_gain_in_alert_enable; /* adi,update-tx-gain-in-alert-enable */
+ /* Reference Clock Control */
+ uint8_t xo_disable_use_ext_refclk_enable; /* adi,xo-disable-use-ext-refclk-enable */
+ uint32_t dcxo_coarse_and_fine_tune[2]; /* adi,dcxo-coarse-and-fine-tune */
+ uint32_t clk_output_mode_select; /* adi,clk-output-mode-select */
+ /* Gain Control */
+ uint8_t gc_rx1_mode; /* adi,gc-rx1-mode */
+ uint8_t gc_rx2_mode; /* adi,gc-rx2-mode */
+ uint8_t gc_adc_large_overload_thresh; /* adi,gc-adc-large-overload-thresh */
+ uint8_t gc_adc_ovr_sample_size; /* adi,gc-adc-ovr-sample-size */
+ uint8_t gc_adc_small_overload_thresh; /* adi,gc-adc-small-overload-thresh */
+ uint16_t gc_dec_pow_measurement_duration; /* adi,gc-dec-pow-measurement-duration */
+ uint8_t gc_dig_gain_enable; /* adi,gc-dig-gain-enable */
+ uint16_t gc_lmt_overload_high_thresh; /* adi,gc-lmt-overload-high-thresh */
+ uint16_t gc_lmt_overload_low_thresh; /* adi,gc-lmt-overload-low-thresh */
+ uint8_t gc_low_power_thresh; /* adi,gc-low-power-thresh */
+ uint8_t gc_max_dig_gain; /* adi,gc-max-dig-gain */
+ /* Gain MGC Control */
+ uint8_t mgc_dec_gain_step; /* adi,mgc-dec-gain-step */
+ uint8_t mgc_inc_gain_step; /* adi,mgc-inc-gain-step */
+ uint8_t mgc_rx1_ctrl_inp_enable; /* adi,mgc-rx1-ctrl-inp-enable */
+ uint8_t mgc_rx2_ctrl_inp_enable; /* adi,mgc-rx2-ctrl-inp-enable */
+ uint8_t mgc_split_table_ctrl_inp_gain_mode; /* adi,mgc-split-table-ctrl-inp-gain-mode */
+ /* Gain AGC Control */
+ uint8_t agc_adc_large_overload_exceed_counter; /* adi,agc-adc-large-overload-exceed-counter */
+ uint8_t agc_adc_large_overload_inc_steps; /* adi,agc-adc-large-overload-inc-steps */
+ uint8_t agc_adc_lmt_small_overload_prevent_gain_inc_enable; /* adi,agc-adc-lmt-small-overload-prevent-gain-inc-enable */
+ uint8_t agc_adc_small_overload_exceed_counter; /* adi,agc-adc-small-overload-exceed-counter */
+ uint8_t agc_dig_gain_step_size; /* adi,agc-dig-gain-step-size */
+ uint8_t agc_dig_saturation_exceed_counter; /* adi,agc-dig-saturation-exceed-counter */
+ uint32_t agc_gain_update_interval_us; /* adi,agc-gain-update-interval-us */
+ uint8_t agc_immed_gain_change_if_large_adc_overload_enable; /* adi,agc-immed-gain-change-if-large-adc-overload-enable */
+ uint8_t agc_immed_gain_change_if_large_lmt_overload_enable; /* adi,agc-immed-gain-change-if-large-lmt-overload-enable */
+ uint8_t agc_inner_thresh_high; /* adi,agc-inner-thresh-high */
+ uint8_t agc_inner_thresh_high_dec_steps; /* adi,agc-inner-thresh-high-dec-steps */
+ uint8_t agc_inner_thresh_low; /* adi,agc-inner-thresh-low */
+ uint8_t agc_inner_thresh_low_inc_steps; /* adi,agc-inner-thresh-low-inc-steps */
+ uint8_t agc_lmt_overload_large_exceed_counter; /* adi,agc-lmt-overload-large-exceed-counter */
+ uint8_t agc_lmt_overload_large_inc_steps; /* adi,agc-lmt-overload-large-inc-steps */
+ uint8_t agc_lmt_overload_small_exceed_counter; /* adi,agc-lmt-overload-small-exceed-counter */
+ uint8_t agc_outer_thresh_high; /* adi,agc-outer-thresh-high */
+ uint8_t agc_outer_thresh_high_dec_steps; /* adi,agc-outer-thresh-high-dec-steps */
+ uint8_t agc_outer_thresh_low; /* adi,agc-outer-thresh-low */
+ uint8_t agc_outer_thresh_low_inc_steps; /* adi,agc-outer-thresh-low-inc-steps */
+ uint32_t agc_attack_delay_extra_margin_us; /* adi,agc-attack-delay-extra-margin-us */
+ uint8_t agc_sync_for_gain_counter_enable; /* adi,agc-sync-for-gain-counter-enable */
+ /* Fast AGC */
+ uint32_t fagc_dec_pow_measuremnt_duration; /* adi,fagc-dec-pow-measurement-duration */
+ uint32_t fagc_state_wait_time_ns; /* adi,fagc-state-wait-time-ns */
+ /* Fast AGC - Low Power */
+ uint8_t fagc_allow_agc_gain_increase; /* adi,fagc-allow-agc-gain-increase-enable */
+ uint32_t fagc_lp_thresh_increment_time; /* adi,fagc-lp-thresh-increment-time */
+ uint32_t fagc_lp_thresh_increment_steps; /* adi,fagc-lp-thresh-increment-steps */
+ /* Fast AGC - Lock Level */
+ uint32_t fagc_lock_level; /* adi,fagc-lock-level */
+ uint8_t fagc_lock_level_lmt_gain_increase_en; /* adi,fagc-lock-level-lmt-gain-increase-enable */
+ uint32_t fagc_lock_level_gain_increase_upper_limit; /* adi,fagc-lock-level-gain-increase-upper-limit */
+ /* Fast AGC - Peak Detectors and Final Settling */
+ uint32_t fagc_lpf_final_settling_steps; /* adi,fagc-lpf-final-settling-steps */
+ uint32_t fagc_lmt_final_settling_steps; /* adi,fagc-lmt-final-settling-steps */
+ uint32_t fagc_final_overrange_count; /* adi,fagc-final-overrange-count */
+ /* Fast AGC - Final Power Test */
+ uint8_t fagc_gain_increase_after_gain_lock_en; /* adi,fagc-gain-increase-after-gain-lock-enable */
+ /* Fast AGC - Unlocking the Gain */
+ uint32_t fagc_gain_index_type_after_exit_rx_mode; /* adi,fagc-gain-index-type-after-exit-rx-mode */
+ uint8_t fagc_use_last_lock_level_for_set_gain_en; /* adi,fagc-use-last-lock-level-for-set-gain-enable */
+ uint8_t fagc_rst_gla_stronger_sig_thresh_exceeded_en; /* adi,fagc-rst-gla-stronger-sig-thresh-exceeded-enable */
+ uint32_t fagc_optimized_gain_offset; /* adi,fagc-optimized-gain-offset */
+ uint32_t fagc_rst_gla_stronger_sig_thresh_above_ll; /* adi,fagc-rst-gla-stronger-sig-thresh-above-ll */
+ uint8_t fagc_rst_gla_engergy_lost_sig_thresh_exceeded_en; /* adi,fagc-rst-gla-engergy-lost-sig-thresh-exceeded-enable */
+ uint8_t fagc_rst_gla_engergy_lost_goto_optim_gain_en; /* adi,fagc-rst-gla-engergy-lost-goto-optim-gain-enable */
+ uint32_t fagc_rst_gla_engergy_lost_sig_thresh_below_ll; /* adi,fagc-rst-gla-engergy-lost-sig-thresh-below-ll */
+ uint32_t fagc_energy_lost_stronger_sig_gain_lock_exit_cnt; /* adi,fagc-energy-lost-stronger-sig-gain-lock-exit-cnt */
+ uint8_t fagc_rst_gla_large_adc_overload_en; /* adi,fagc-rst-gla-large-adc-overload-enable */
+ uint8_t fagc_rst_gla_large_lmt_overload_en; /* adi,fagc-rst-gla-large-lmt-overload-enable */
+ uint8_t fagc_rst_gla_en_agc_pulled_high_en; /* adi,fagc-rst-gla-en-agc-pulled-high-enable */
+ uint32_t fagc_rst_gla_if_en_agc_pulled_high_mode; /* adi,fagc-rst-gla-if-en-agc-pulled-high-mode */
+ uint32_t fagc_power_measurement_duration_in_state5; /* adi,fagc-power-measurement-duration-in-state5 */
+ /* RSSI Control */
+ uint32_t rssi_delay; /* adi,rssi-delay */
+ uint32_t rssi_duration; /* adi,rssi-duration */
+ uint8_t rssi_restart_mode; /* adi,rssi-restart-mode */
+ uint8_t rssi_unit_is_rx_samples_enable; /* adi,rssi-unit-is-rx-samples-enable */
+ uint32_t rssi_wait; /* adi,rssi-wait */
+ /* Aux ADC Control */
+ uint32_t aux_adc_decimation; /* adi,aux-adc-decimation */
+ uint32_t aux_adc_rate; /* adi,aux-adc-rate */
+ /* AuxDAC Control */
+ uint8_t aux_dac_manual_mode_enable; /* adi,aux-dac-manual-mode-enable */
+ uint32_t aux_dac1_default_value_mV; /* adi,aux-dac1-default-value-mV */
+ uint8_t aux_dac1_active_in_rx_enable; /* adi,aux-dac1-active-in-rx-enable */
+ uint8_t aux_dac1_active_in_tx_enable; /* adi,aux-dac1-active-in-tx-enable */
+ uint8_t aux_dac1_active_in_alert_enable; /* adi,aux-dac1-active-in-alert-enable */
+ uint32_t aux_dac1_rx_delay_us; /* adi,aux-dac1-rx-delay-us */
+ uint32_t aux_dac1_tx_delay_us; /* adi,aux-dac1-tx-delay-us */
+ uint32_t aux_dac2_default_value_mV; /* adi,aux-dac2-default-value-mV */
+ uint8_t aux_dac2_active_in_rx_enable; /* adi,aux-dac2-active-in-rx-enable */
+ uint8_t aux_dac2_active_in_tx_enable; /* adi,aux-dac2-active-in-tx-enable */
+ uint8_t aux_dac2_active_in_alert_enable; /* adi,aux-dac2-active-in-alert-enable */
+ uint32_t aux_dac2_rx_delay_us; /* adi,aux-dac2-rx-delay-us */
+ uint32_t aux_dac2_tx_delay_us; /* adi,aux-dac2-tx-delay-us */
+ /* Temperature Sensor Control */
+ uint32_t temp_sense_decimation; /* adi,temp-sense-decimation */
+ uint16_t temp_sense_measurement_interval_ms; /* adi,temp-sense-measurement-interval-ms */
+ int8_t temp_sense_offset_signed; /* adi,temp-sense-offset-signed */
+ uint8_t temp_sense_periodic_measurement_enable; /* adi,temp-sense-periodic-measurement-enable */
+ /* Control Out Setup */
+ uint8_t ctrl_outs_enable_mask; /* adi,ctrl-outs-enable-mask */
+ uint8_t ctrl_outs_index; /* adi,ctrl-outs-index */
+ /* External LNA Control */
+ uint32_t elna_settling_delay_ns; /* adi,elna-settling-delay-ns */
+ uint32_t elna_gain_mdB; /* adi,elna-gain-mdB */
+ uint32_t elna_bypass_loss_mdB; /* adi,elna-bypass-loss-mdB */
+ uint8_t elna_rx1_gpo0_control_enable; /* adi,elna-rx1-gpo0-control-enable */
+ uint8_t elna_rx2_gpo1_control_enable; /* adi,elna-rx2-gpo1-control-enable */
+ uint8_t elna_gaintable_all_index_enable; /* adi,elna-gaintable-all-index-enable */
+ /* Digital Interface Control */
+ uint8_t digital_interface_tune_skip_mode; /* adi,digital-interface-tune-skip-mode */
+ uint8_t digital_interface_tune_fir_disable; /* adi,digital-interface-tune-fir-disable */
+ uint8_t pp_tx_swap_enable; /* adi,pp-tx-swap-enable */
+ uint8_t pp_rx_swap_enable; /* adi,pp-rx-swap-enable */
+ uint8_t tx_channel_swap_enable; /* adi,tx-channel-swap-enable */
+ uint8_t rx_channel_swap_enable; /* adi,rx-channel-swap-enable */
+ uint8_t rx_frame_pulse_mode_enable; /* adi,rx-frame-pulse-mode-enable */
+ uint8_t two_t_two_r_timing_enable; /* adi,2t2r-timing-enable */
+ uint8_t invert_data_bus_enable; /* adi,invert-data-bus-enable */
+ uint8_t invert_data_clk_enable; /* adi,invert-data-clk-enable */
+ uint8_t fdd_alt_word_order_enable; /* adi,fdd-alt-word-order-enable */
+ uint8_t invert_rx_frame_enable; /* adi,invert-rx-frame-enable */
+ uint8_t fdd_rx_rate_2tx_enable; /* adi,fdd-rx-rate-2tx-enable */
+ uint8_t swap_ports_enable; /* adi,swap-ports-enable */
+ uint8_t single_data_rate_enable; /* adi,single-data-rate-enable */
+ uint8_t lvds_mode_enable; /* adi,lvds-mode-enable */
+ uint8_t half_duplex_mode_enable; /* adi,half-duplex-mode-enable */
+ uint8_t single_port_mode_enable; /* adi,single-port-mode-enable */
+ uint8_t full_port_enable; /* adi,full-port-enable */
+ uint8_t full_duplex_swap_bits_enable; /* adi,full-duplex-swap-bits-enable */
+ uint32_t delay_rx_data; /* adi,delay-rx-data */
+ uint32_t rx_data_clock_delay; /* adi,rx-data-clock-delay */
+ uint32_t rx_data_delay; /* adi,rx-data-delay */
+ uint32_t tx_fb_clock_delay; /* adi,tx-fb-clock-delay */
+ uint32_t tx_data_delay; /* adi,tx-data-delay */
+ uint32_t lvds_bias_mV; /* adi,lvds-bias-mV */
+ uint8_t lvds_rx_onchip_termination_enable; /* adi,lvds-rx-onchip-termination-enable */
+ uint8_t rx1rx2_phase_inversion_en; /* adi,rx1-rx2-phase-inversion-enable */
+ uint8_t lvds_invert1_control; /* adi,lvds-invert1-control */
+ uint8_t lvds_invert2_control; /* adi,lvds-invert2-control */
+#ifdef NUAND_MODIFICATIONS
+ // add digital interface drive and clock slew
+ uint8_t clk_out_drive;
+ uint8_t dataclk_drive;
+ uint8_t data_port_drive;
+ uint8_t clk_out_slew;
+ uint8_t dataclk_slew;
+ uint8_t data_port_slew;
+#endif // NUAND_MODIFICATIONS
+ /* GPO Control */
+ uint8_t gpo0_inactive_state_high_enable; /* adi,gpo0-inactive-state-high-enable */
+ uint8_t gpo1_inactive_state_high_enable; /* adi,gpo1-inactive-state-high-enable */
+ uint8_t gpo2_inactive_state_high_enable; /* adi,gpo2-inactive-state-high-enable */
+ uint8_t gpo3_inactive_state_high_enable; /* adi,gpo3-inactive-state-high-enable */
+ uint8_t gpo0_slave_rx_enable; /* adi,gpo0-slave-rx-enable */
+ uint8_t gpo0_slave_tx_enable; /* adi,gpo0-slave-tx-enable */
+ uint8_t gpo1_slave_rx_enable; /* adi,gpo1-slave-rx-enable */
+ uint8_t gpo1_slave_tx_enable; /* adi,gpo1-slave-tx-enable */
+ uint8_t gpo2_slave_rx_enable; /* adi,gpo2-slave-rx-enable */
+ uint8_t gpo2_slave_tx_enable; /* adi,gpo2-slave-tx-enable */
+ uint8_t gpo3_slave_rx_enable; /* adi,gpo3-slave-rx-enable */
+ uint8_t gpo3_slave_tx_enable; /* adi,gpo3-slave-tx-enable */
+ uint8_t gpo0_rx_delay_us; /* adi,gpo0-rx-delay-us */
+ uint8_t gpo0_tx_delay_us; /* adi,gpo0-tx-delay-us */
+ uint8_t gpo1_rx_delay_us; /* adi,gpo1-rx-delay-us */
+ uint8_t gpo1_tx_delay_us; /* adi,gpo1-tx-delay-us */
+ uint8_t gpo2_rx_delay_us; /* adi,gpo2-rx-delay-us */
+ uint8_t gpo2_tx_delay_us; /* adi,gpo2-tx-delay-us */
+ uint8_t gpo3_rx_delay_us; /* adi,gpo3-rx-delay-us */
+ uint8_t gpo3_tx_delay_us; /* adi,gpo3-tx-delay-us */
+ /* Tx Monitor Control */
+ uint32_t low_high_gain_threshold_mdB; /* adi,txmon-low-high-thresh */
+ uint32_t low_gain_dB; /* adi,txmon-low-gain */
+ uint32_t high_gain_dB; /* adi,txmon-high-gain */
+ uint8_t tx_mon_track_en; /* adi,txmon-dc-tracking-enable */
+ uint8_t one_shot_mode_en; /* adi,txmon-one-shot-mode-enable */
+ uint32_t tx_mon_delay; /* adi,txmon-delay */
+ uint32_t tx_mon_duration; /* adi,txmon-duration */
+ uint32_t tx1_mon_front_end_gain; /* adi,txmon-1-front-end-gain */
+ uint32_t tx2_mon_front_end_gain; /* adi,txmon-2-front-end-gain */
+ uint32_t tx1_mon_lo_cm; /* adi,txmon-1-lo-cm */
+ uint32_t tx2_mon_lo_cm; /* adi,txmon-2-lo-cm */
+ /* GPIO definitions */
+ int32_t gpio_resetb; /* reset-gpios */
+ /* MCS Sync */
+ int32_t gpio_sync; /* sync-gpios */
+ int32_t gpio_cal_sw1; /* cal-sw1-gpios */
+ int32_t gpio_cal_sw2; /* cal-sw2-gpios */
+ /* External LO clocks */
+ uint32_t (*ad9361_rfpll_ext_recalc_rate)(struct refclk_scale *clk_priv);
+ int32_t (*ad9361_rfpll_ext_round_rate)(struct refclk_scale *clk_priv, uint32_t rate);
+ int32_t (*ad9361_rfpll_ext_set_rate)(struct refclk_scale *clk_priv, uint32_t rate);
+}AD9361_InitParam;
+
+typedef struct
+{
+ uint32_t rx; /* 1, 2, 3(both) */
+ int32_t rx_gain; /* -12, -6, 0, 6 */
+ uint32_t rx_dec; /* 1, 2, 4 */
+ int16_t rx_coef[128];
+ uint8_t rx_coef_size;
+ uint32_t rx_path_clks[6];
+ uint32_t rx_bandwidth;
+}AD9361_RXFIRConfig;
+
+typedef struct
+{
+ uint32_t tx; /* 1, 2, 3(both) */
+ int32_t tx_gain; /* -6, 0 */
+ uint32_t tx_int; /* 1, 2, 4 */
+ int16_t tx_coef[128];
+ uint8_t tx_coef_size;
+ uint32_t tx_path_clks[6];
+ uint32_t tx_bandwidth;
+}AD9361_TXFIRConfig;
+
+enum ad9361_ensm_mode {
+ ENSM_MODE_TX,
+ ENSM_MODE_RX,
+ ENSM_MODE_ALERT,
+ ENSM_MODE_FDD,
+ ENSM_MODE_WAIT,
+ ENSM_MODE_SLEEP,
+ ENSM_MODE_PINCTRL,
+ ENSM_MODE_PINCTRL_FDD_INDEP,
+};
+
+#define ENABLE 1
+#define DISABLE 0
+
+#define RX1 0
+#define RX2 1
+
+#define TX1 0
+#define TX2 1
+
+#define A_BALANCED 0
+#define B_BALANCED 1
+#define C_BALANCED 2
+#define A_N 3
+#define A_P 4
+#define B_N 5
+#define B_P 6
+#define C_N 7
+#define C_P 8
+#define TX_MON1 9
+#define TX_MON2 10
+#define TX_MON1_2 11
+
+#define TXA 0
+#define TXB 1
+
+#define MODE_1x1 1
+#define MODE_2x2 2
+
+#define HIGHEST_OSR 0
+#define NOMINAL_OSR 1
+
+#define INT_LO 0
+#define EXT_LO 1
+
+/******************************************************************************/
+/************************ Functions Declarations ******************************/
+/******************************************************************************/
+#ifdef NUAND_MODIFICATIONS
+/* Initialize the AD9361 part. */
+int32_t ad9361_init (struct ad9361_rf_phy **ad9361_phy, AD9361_InitParam *init_param, void *userdata);
+/* Deinitialize the AD9361 part. */
+int32_t ad9361_deinit (struct ad9361_rf_phy *phy);
+#else
+/* Initialize the AD9361 part. */
+int32_t ad9361_init (struct ad9361_rf_phy **ad9361_phy, AD9361_InitParam *init_param);
+#endif // NUAND_MODIFICATIONS
+/* Set the Enable State Machine (ENSM) mode. */
+int32_t ad9361_set_en_state_machine_mode (struct ad9361_rf_phy *phy, uint32_t mode);
+/* Get the Enable State Machine (ENSM) mode. */
+int32_t ad9361_get_en_state_machine_mode (struct ad9361_rf_phy *phy, uint32_t *mode);
+/* Set the receive RF gain for the selected channel. */
+int32_t ad9361_set_rx_rf_gain (struct ad9361_rf_phy *phy, uint8_t ch, int32_t gain_db);
+/* Get current receive RF gain for the selected channel. */
+int32_t ad9361_get_rx_rf_gain (struct ad9361_rf_phy *phy, uint8_t ch, int32_t *gain_db);
+/* Set the RX RF bandwidth. */
+int32_t ad9361_set_rx_rf_bandwidth (struct ad9361_rf_phy *phy, uint32_t bandwidth_hz);
+/* Get the RX RF bandwidth. */
+int32_t ad9361_get_rx_rf_bandwidth (struct ad9361_rf_phy *phy, uint32_t *bandwidth_hz);
+/* Set the RX sampling frequency. */
+int32_t ad9361_set_rx_sampling_freq (struct ad9361_rf_phy *phy, uint32_t sampling_freq_hz);
+/* Get current RX sampling frequency. */
+int32_t ad9361_get_rx_sampling_freq (struct ad9361_rf_phy *phy, uint32_t *sampling_freq_hz);
+/* Set the RX LO frequency. */
+int32_t ad9361_set_rx_lo_freq (struct ad9361_rf_phy *phy, uint64_t lo_freq_hz);
+/* Get current RX LO frequency. */
+int32_t ad9361_get_rx_lo_freq (struct ad9361_rf_phy *phy, uint64_t *lo_freq_hz);
+/* Switch between internal and external LO. */
+int32_t ad9361_set_rx_lo_int_ext(struct ad9361_rf_phy *phy, uint8_t int_ext);
+/* Get the RSSI for the selected channel. */
+int32_t ad9361_get_rx_rssi (struct ad9361_rf_phy *phy, uint8_t ch, struct rf_rssi *rssi);
+/* Set the gain control mode for the selected channel. */
+int32_t ad9361_set_rx_gain_control_mode (struct ad9361_rf_phy *phy, uint8_t ch, uint8_t gc_mode);
+/* Get the gain control mode for the selected channel. */
+int32_t ad9361_get_rx_gain_control_mode (struct ad9361_rf_phy *phy, uint8_t ch, uint8_t *gc_mode);
+/* Set the RX FIR filter configuration. */
+int32_t ad9361_set_rx_fir_config (struct ad9361_rf_phy *phy, AD9361_RXFIRConfig fir_cfg);
+/* Get the RX FIR filter configuration. */
+int32_t ad9361_get_rx_fir_config(struct ad9361_rf_phy *phy, uint8_t rx_ch, AD9361_RXFIRConfig *fir_cfg);
+/* Enable/disable the RX FIR filter. */
+int32_t ad9361_set_rx_fir_en_dis (struct ad9361_rf_phy *phy, uint8_t en_dis);
+/* Get the status of the RX FIR filter. */
+int32_t ad9361_get_rx_fir_en_dis (struct ad9361_rf_phy *phy, uint8_t *en_dis);
+/* Enable/disable the RX RFDC Tracking. */
+int32_t ad9361_set_rx_rfdc_track_en_dis (struct ad9361_rf_phy *phy, uint8_t en_dis);
+/* Get the status of the RX RFDC Tracking. */
+int32_t ad9361_get_rx_rfdc_track_en_dis (struct ad9361_rf_phy *phy, uint8_t *en_dis);
+/* Enable/disable the RX BasebandDC Tracking. */
+int32_t ad9361_set_rx_bbdc_track_en_dis (struct ad9361_rf_phy *phy, uint8_t en_dis);
+/* Get the status of the RX BasebandDC Tracking. */
+int32_t ad9361_get_rx_bbdc_track_en_dis (struct ad9361_rf_phy *phy, uint8_t *en_dis);
+/* Enable/disable the RX Quadrature Tracking. */
+int32_t ad9361_set_rx_quad_track_en_dis (struct ad9361_rf_phy *phy, uint8_t en_dis);
+/* Get the status of the RX Quadrature Tracking. */
+int32_t ad9361_get_rx_quad_track_en_dis (struct ad9361_rf_phy *phy, uint8_t *en_dis);
+/* Set the RX RF input port. */
+int32_t ad9361_set_rx_rf_port_input (struct ad9361_rf_phy *phy, uint32_t mode);
+/* Get the selected RX RF input port. */
+int32_t ad9361_get_rx_rf_port_input (struct ad9361_rf_phy *phy, uint32_t *mode);
+/* Store RX fastlock profile. */
+int32_t ad9361_rx_fastlock_store(struct ad9361_rf_phy *phy, uint32_t profile);
+/* Recall RX fastlock profile. */
+int32_t ad9361_rx_fastlock_recall(struct ad9361_rf_phy *phy, uint32_t profile);
+/* Load RX fastlock profile. */
+int32_t ad9361_rx_fastlock_load(struct ad9361_rf_phy *phy, uint32_t profile, uint8_t *values);
+/* Save RX fastlock profile. */
+int32_t ad9361_rx_fastlock_save(struct ad9361_rf_phy *phy, uint32_t profile, uint8_t *values);
+/* Set the transmit attenuation for the selected channel. */
+int32_t ad9361_set_tx_attenuation (struct ad9361_rf_phy *phy, uint8_t ch, uint32_t attenuation_mdb);
+/* Get current transmit attenuation for the selected channel. */
+int32_t ad9361_get_tx_attenuation (struct ad9361_rf_phy *phy, uint8_t ch, uint32_t *attenuation_mdb);
+/* Set the TX RF bandwidth. */
+int32_t ad9361_set_tx_rf_bandwidth (struct ad9361_rf_phy *phy, uint32_t bandwidth_hz);
+/* Get the TX RF bandwidth. */
+int32_t ad9361_get_tx_rf_bandwidth (struct ad9361_rf_phy *phy, uint32_t *bandwidth_hz);
+/* Set the TX sampling frequency. */
+int32_t ad9361_set_tx_sampling_freq (struct ad9361_rf_phy *phy, uint32_t sampling_freq_hz);
+/* Get current TX sampling frequency. */
+int32_t ad9361_get_tx_sampling_freq (struct ad9361_rf_phy *phy, uint32_t *sampling_freq_hz);
+/* Set the TX LO frequency. */
+int32_t ad9361_set_tx_lo_freq (struct ad9361_rf_phy *phy, uint64_t lo_freq_hz);
+/* Get current TX LO frequency. */
+int32_t ad9361_get_tx_lo_freq (struct ad9361_rf_phy *phy, uint64_t *lo_freq_hz);
+/* Switch between internal and external LO. */
+int32_t ad9361_set_tx_lo_int_ext(struct ad9361_rf_phy *phy, uint8_t int_ext);
+/* Set the TX FIR filter configuration. */
+int32_t ad9361_set_tx_fir_config (struct ad9361_rf_phy *phy, AD9361_TXFIRConfig fir_cfg);
+/* Get the TX FIR filter configuration. */
+int32_t ad9361_get_tx_fir_config(struct ad9361_rf_phy *phy, uint8_t tx_ch, AD9361_TXFIRConfig *fir_cfg);
+/* Enable/disable the TX FIR filter. */
+int32_t ad9361_set_tx_fir_en_dis (struct ad9361_rf_phy *phy, uint8_t en_dis);
+/* Get the status of the TX FIR filter. */
+int32_t ad9361_get_tx_fir_en_dis (struct ad9361_rf_phy *phy, uint8_t *en_dis);
+/* Get the TX RSSI for the selected channel. */
+int32_t ad9361_get_tx_rssi (struct ad9361_rf_phy *phy, uint8_t ch, uint32_t *rssi_db_x_1000);
+/* Set the TX RF output port. */
+int32_t ad9361_set_tx_rf_port_output (struct ad9361_rf_phy *phy, uint32_t mode);
+/* Get the selected TX RF output port. */
+int32_t ad9361_get_tx_rf_port_output (struct ad9361_rf_phy *phy, uint32_t *mode);
+/* Enable/disable the auto calibration. */
+int32_t ad9361_set_tx_auto_cal_en_dis (struct ad9361_rf_phy *phy, uint8_t en_dis);
+/* Get the status of the auto calibration flag. */
+int32_t ad9361_get_tx_auto_cal_en_dis (struct ad9361_rf_phy *phy, uint8_t *en_dis);
+/* Store TX fastlock profile. */
+int32_t ad9361_tx_fastlock_store(struct ad9361_rf_phy *phy, uint32_t profile);
+/* Recall TX fastlock profile. */
+int32_t ad9361_tx_fastlock_recall(struct ad9361_rf_phy *phy, uint32_t profile);
+/* Load TX fastlock profile. */
+int32_t ad9361_tx_fastlock_load(struct ad9361_rf_phy *phy, uint32_t profile, uint8_t *values);
+/* Save TX fastlock profile. */
+int32_t ad9361_tx_fastlock_save(struct ad9361_rf_phy *phy, uint32_t profile, uint8_t *values);
+/* Set the RX and TX path rates. */
+int32_t ad9361_set_trx_path_clks(struct ad9361_rf_phy *phy, uint32_t *rx_path_clks, uint32_t *tx_path_clks);
+/* Get the RX and TX path rates. */
+int32_t ad9361_get_trx_path_clks(struct ad9361_rf_phy *phy, uint32_t *rx_path_clks, uint32_t *tx_path_clks);
+/* Set the number of channels mode. */
+int32_t ad9361_set_no_ch_mode(struct ad9361_rf_phy *phy, uint8_t no_ch_mode);
+/* Do multi chip synchronization. */
+int32_t ad9361_do_mcs(struct ad9361_rf_phy *phy_master, struct ad9361_rf_phy *phy_slave);
+/* Enable/disable the TRX FIR filters. */
+int32_t ad9361_set_trx_fir_en_dis (struct ad9361_rf_phy *phy, uint8_t en_dis);
+/* Set the OSR rate governor. */
+int32_t ad9361_set_trx_rate_gov (struct ad9361_rf_phy *phy, uint32_t rate_gov);
+/* Get the OSR rate governor. */
+int32_t ad9361_get_trx_rate_gov (struct ad9361_rf_phy *phy, uint32_t *rate_gov);
+/* Perform the selected calibration. */
+int32_t ad9361_do_calib(struct ad9361_rf_phy *phy, uint32_t cal, int32_t arg);
+/* Load and enable TRX FIR filters configurations. */
+int32_t ad9361_trx_load_enable_fir(struct ad9361_rf_phy *phy,
+ AD9361_RXFIRConfig rx_fir_cfg,
+ AD9361_TXFIRConfig tx_fir_cfg);
+/* Do DCXO coarse tuning. */
+int32_t ad9361_do_dcxo_tune_coarse(struct ad9361_rf_phy *phy,
+ uint32_t coarse);
+/* Do DCXO fine tuning. */
+int32_t ad9361_do_dcxo_tune_fine(struct ad9361_rf_phy *phy,
+ uint32_t fine);
+#endif
diff --git a/Radio/HW/BladeRF/common/thirdparty/ad936x/ad9361_conv.c b/Radio/HW/BladeRF/common/thirdparty/ad936x/ad9361_conv.c
new file mode 100644
index 0000000..0491f91
--- /dev/null
+++ b/Radio/HW/BladeRF/common/thirdparty/ad936x/ad9361_conv.c
@@ -0,0 +1,751 @@
+
+/***************************************************************************//**
+ * @file ad9361_conv.c
+ * @brief Implementation of AD9361 Conv Driver.
+********************************************************************************
+ * Copyright 2014-2015(c) Analog Devices, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * - Neither the name of Analog Devices, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * - The use of this software may or may not infringe the patent rights
+ * of one or more patent holders. This license does not release you
+ * from the requirement that you obtain separate licenses from these
+ * patent holders to use this software.
+ * - Use of the software either in source or binary form, must be run
+ * on or directly connected to an Analog Devices Inc. component.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*******************************************************************************/
+
+/******************************************************************************/
+/***************************** Include Files **********************************/
+/******************************************************************************/
+#include <inttypes.h>
+#include <string.h>
+#include "ad9361.h"
+#include "platform.h"
+#include "config.h"
+
+#ifndef AXI_ADC_NOT_PRESENT
+/**
+ * HDL loopback enable/disable.
+ * @param phy The AD9361 state structure.
+ * @param enable Enable/disable option.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_hdl_loopback(struct ad9361_rf_phy *phy, bool enable)
+{
+ struct axiadc_converter *conv = phy->adc_conv;
+ struct axiadc_state *st = phy->adc_state;
+ int32_t reg, addr, chan;
+
+#ifdef NUAND_MODIFICATIONS
+ int ret;
+ uint32_t readval;
+
+ ret = axiadc_read(st, 0x4000, &readval);
+ if (ret < 0)
+ return ret;
+
+ uint32_t version = readval;
+#else
+ uint32_t version = axiadc_read(st, 0x4000);
+#endif // NUAND_MODIFICATIONS
+
+ /* Still there but implemented a bit different */
+ if (PCORE_VERSION_MAJOR(version) > 7)
+ addr = 0x4418;
+ else
+ addr = 0x4414;
+
+ for (chan = 0; chan < conv->chip_info->num_channels; chan++) {
+#ifdef NUAND_MODIFICATIONS
+ ret = axiadc_read(st, addr + (chan) * 0x40, &readval);
+ if (ret < 0)
+ return ret;
+
+ reg = (int32_t)readval;
+#else
+ reg = axiadc_read(st, addr + (chan) * 0x40);
+#endif // NUAND_MODIFICATIONS
+
+ if (PCORE_VERSION_MAJOR(version) > 7) {
+ if (enable && reg != 0x8) {
+ conv->scratch_reg[chan] = reg;
+ reg = 0x8;
+ } else if (reg == 0x8) {
+ reg = conv->scratch_reg[chan];
+ }
+ } else {
+ /* DAC_LB_ENB If set enables loopback of receive data */
+ if (enable)
+ reg |= BIT(1);
+ else
+ reg &= ~BIT(1);
+ }
+ axiadc_write(st, addr + (chan) * 0x40, reg);
+ }
+
+ return 0;
+}
+
+/**
+ * Set IO delay.
+ * @param st The AXI ADC state structure.
+ * @param lane Lane number.
+ * @param val Value.
+ * @param tx The Synthesizer TX = 1, RX = 0.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_iodelay_set(struct axiadc_state *st, unsigned lane,
+ unsigned val, bool tx)
+{
+ if (tx) {
+ if (PCORE_VERSION_MAJOR(st->pcore_version) > 8)
+ axiadc_write(st, 0x4000 + ADI_REG_DELAY(lane), val);
+ else
+ return -ENODEV;
+ } else {
+ axiadc_idelay_set(st, lane, val);
+ }
+
+ return 0;
+}
+
+/**
+ * Set midscale IO delay.
+ * @param phy The AD9361 state structure.
+ * @param tx The Synthesizer TX = 1, RX = 0.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_midscale_iodelay(struct ad9361_rf_phy *phy, bool tx)
+{
+ struct axiadc_state *st = phy->adc_state;
+ int32_t ret = 0, i;
+
+ for (i = 0; i < 7; i++)
+ ret |= ad9361_iodelay_set(st, i, 15, tx);
+
+ return 0;
+}
+
+/**
+ * Digital tune IO delay.
+ * @param phy The AD9361 state structure.
+ * @param tx The Synthesizer TX = 1, RX = 0.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static int32_t ad9361_dig_tune_iodelay(struct ad9361_rf_phy *phy, bool tx)
+{
+ struct axiadc_converter *conv = phy->adc_conv;
+ struct axiadc_state *st = phy->adc_state;
+ int32_t ret, i, j, chan, num_chan;
+ uint32_t s0, c0;
+ uint8_t field[32];
+
+ num_chan = (conv->chip_info->num_channels > 4) ? 4 : conv->chip_info->num_channels;
+
+ for (i = 0; i < 7; i++) {
+ for (j = 0; j < 32; j++) {
+ ad9361_iodelay_set(st, i, j, tx);
+ mdelay(1);
+
+ for (chan = 0; chan < num_chan; chan++)
+ axiadc_write(st, ADI_REG_CHAN_STATUS(chan),
+ ADI_PN_ERR | ADI_PN_OOS);
+ mdelay(10);
+
+#ifdef NUAND_MODIFICATIONS
+ uint32_t stat, tmp;
+
+ for (chan = 0, stat = 0; chan < num_chan; chan++) {
+ ret = axiadc_read(st, ADI_REG_CHAN_STATUS(chan), &tmp);
+ if (ret < 0)
+ return ret;
+ stat |= tmp;
+ }
+ field[j] = stat;
+#else
+
+ for (chan = 0, ret = 0; chan < num_chan; chan++)
+ ret |= axiadc_read(st, ADI_REG_CHAN_STATUS(chan));
+
+ field[j] = ret;
+#endif // NUAND_MODIFICATIONS
+ }
+
+ c0 = ad9361_find_opt(&field[0], 32, &s0);
+ ad9361_iodelay_set(st, i, s0 + c0 / 2, tx);
+
+ dev_dbg(&phy->spi->dev,
+ "%s Lane %"PRId32", window cnt %"PRIu32" , start %"PRIu32", IODELAY set to %"PRIu32"\n",
+ tx ? "TX" :"RX", i , c0, s0, s0 + c0 / 2);
+ }
+
+ return 0;
+}
+
+/**
+ * Digital tune verbose print.
+ * @param phy The AD9361 state structure.
+ * @param field Field.
+ * @param tx The Synthesizer TX = 1, RX = 0.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+static void ad9361_dig_tune_verbose_print(struct ad9361_rf_phy *phy,
+ uint8_t field[][16], bool tx)
+{
+ int32_t i, j;
+
+ printk("SAMPL CLK: %"PRIu32" tuning: %s\n",
+ clk_get_rate(phy, phy->ref_clk_scale[RX_SAMPL_CLK]), tx ? "TX" : "RX");
+ printk(" ");
+ for (i = 0; i < 16; i++)
+ printk("%"PRIx32":", i);
+ printk("\n");
+
+ for (i = 0; i < 2; i++) {
+ printk("%"PRIx32":", i);
+ for (j = 0; j < 16; j++) {
+ printk("%c ", (field[i][j] ? '#' : 'o'));
+ }
+ printk("\n");
+ }
+ printk("\n");
+}
+
+/**
+ * Digital interface timing analysis.
+ * @param phy The AD9361 state structure.
+ * @param buf The buffer.
+ * @param buflen The buffer length.
+ * @return The size in case of success, negative error code otherwise.
+ */
+int32_t ad9361_dig_interface_timing_analysis(struct ad9361_rf_phy *phy,
+ char *buf, int32_t buflen)
+{
+ struct axiadc_state *st = phy->adc_state;
+ int32_t ret, i, j, chan, len = 0;
+ uint8_t field[16][16];
+ uint8_t rx;
+
+ dev_dbg(&phy->spi->dev, "%s:\n", __func__);
+
+ rx = ad9361_spi_read(phy->spi, REG_RX_CLOCK_DATA_DELAY);
+
+ ad9361_bist_prbs(phy, BIST_INJ_RX);
+
+ for (i = 0; i < 16; i++) {
+ for (j = 0; j < 16; j++) {
+ ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY,
+ DATA_CLK_DELAY(j) | RX_DATA_DELAY(i));
+ for (chan = 0; chan < 4; chan++)
+ axiadc_write(st, ADI_REG_CHAN_STATUS(chan),
+ ADI_PN_ERR | ADI_PN_OOS);
+
+ mdelay(1);
+
+#ifdef NUAND_MODIFICATIONS
+ uint32_t tmp, stat;
+ ret = axiadc_read(st, ADI_REG_STATUS, &stat);
+ if (ret < 0)
+ return ret;
+
+ if (stat & ADI_STATUS) {
+ for (chan = 0, stat = 0; chan < 4; chan++) {
+ ret = axiadc_read(st, ADI_REG_CHAN_STATUS(chan), &tmp);
+ if (ret < 0)
+ return ret;
+ stat |= tmp;
+ }
+ } else {
+ stat = 1;
+ }
+#else
+ if (axiadc_read(st, ADI_REG_STATUS) & ADI_STATUS) {
+ for (chan = 0, ret = 0; chan < 4; chan++)
+ ret |= axiadc_read(st, ADI_REG_CHAN_STATUS(chan));
+ }
+ else {
+ ret = 1;
+ }
+#endif // NUAND_MODIFICATIONS
+
+ field[i][j] = ret;
+ }
+ }
+
+ ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY, rx);
+
+ ad9361_bist_prbs(phy, BIST_DISABLE);
+
+ len += snprintf(buf + len, buflen, "CLK: %"PRIu32" Hz 'o' = PASS\n",
+ clk_get_rate(phy, phy->ref_clk_scale[RX_SAMPL_CLK]));
+ len += snprintf(buf + len, buflen, "DC");
+ for (i = 0; i < 16; i++)
+ len += snprintf(buf + len, buflen, "%"PRIx32":", i);
+ len += snprintf(buf + len, buflen, "\n");
+
+ for (i = 0; i < 16; i++) {
+ len += snprintf(buf + len, buflen, "%"PRIx32":", i);
+ for (j = 0; j < 16; j++) {
+ len += snprintf(buf + len, buflen, "%c ",
+ (field[i][j] ? '.' : 'o'));
+ }
+ len += snprintf(buf + len, buflen, "\n");
+ }
+ len += snprintf(buf + len, buflen, "\n");
+
+ return len;
+}
+
+/**
+ * Digital tune.
+ * @param phy The AD9361 state structure.
+ * @param max_freq Maximum frequency.
+ * @param flags Flags: BE_VERBOSE, BE_MOREVERBOSE, DO_IDELAY, DO_ODELAY.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_dig_tune(struct ad9361_rf_phy *phy, uint32_t max_freq,
+ enum dig_tune_flags flags)
+{
+ struct axiadc_converter *conv = phy->adc_conv;
+ struct axiadc_state *st = phy->adc_state;
+ int32_t ret, i, j, k, chan, t, num_chan, err = 0;
+ uint32_t s0, s1, c0, c1, tmp, saved = 0;
+ uint8_t field[2][16];
+ uint32_t saved_dsel[4], saved_chan_ctrl6[4], saved_chan_ctrl0[4];
+ uint32_t rates[3] = {25000000U, 40000000U, 61440000U};
+ uint32_t hdl_dac_version;
+
+ dev_dbg(&phy->spi->dev, "%s: freq %"PRIu32" flags 0x%X\n", __func__,
+ max_freq, flags);
+
+#ifdef NUAND_MODIFICATIONS
+ ret = axiadc_read(st, 0x4000, &hdl_dac_version);
+ if (ret < 0)
+ return ret;
+#else
+ hdl_dac_version = axiadc_read(st, 0x4000);
+#endif // NUAND_MODIFICATIONS
+
+ if ((phy->pdata->dig_interface_tune_skipmode == 2) ||
+ (flags & RESTORE_DEFAULT)) {
+ /* skip completely and use defaults */
+ ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY,
+ phy->pdata->port_ctrl.rx_clk_data_delay);
+
+ ad9361_spi_write(phy->spi, REG_TX_CLOCK_DATA_DELAY,
+ phy->pdata->port_ctrl.tx_clk_data_delay);
+
+ return 0;
+ }
+
+ /* Mute TX, we don't want to transmit the PRBS */
+ ad9361_tx_mute(phy, 1);
+
+ if (flags & DO_IDELAY)
+ ad9361_midscale_iodelay(phy, 0);
+
+ if (flags & DO_ODELAY)
+ ad9361_midscale_iodelay(phy, 1);
+
+ if (!phy->pdata->fdd) {
+ ad9361_set_ensm_mode(phy, true, false);
+ ad9361_ensm_force_state(phy, ENSM_STATE_FDD);
+ } else {
+ ad9361_ensm_force_state(phy, ENSM_STATE_ALERT);
+ ad9361_ensm_restore_prev_state(phy);
+ }
+
+ num_chan = (conv->chip_info->num_channels > 4) ? 4 :
+ conv->chip_info->num_channels;
+
+ ad9361_bist_prbs(phy, BIST_INJ_RX);
+
+ for (t = 0; t < 2; t++) {
+ memset(field, 0, 32);
+ for (k = 0; (uint32_t)k < (max_freq ? ARRAY_SIZE(rates) : 1); k++) {
+ if (max_freq)
+ ad9361_set_trx_clock_chain_freq(phy,
+ ((phy->pdata->port_ctrl.pp_conf[2] & LVDS_MODE) || !phy->pdata->rx2tx2) ?
+ rates[k] : rates[k] / 2);
+ for (i = 0; i < 2; i++) {
+ for (j = 0; j < 16; j++) {
+ ad9361_spi_write(phy->spi,
+ REG_RX_CLOCK_DATA_DELAY + t,
+ RX_DATA_DELAY(i == 0 ? j : 0) |
+ DATA_CLK_DELAY(i ? j : 0));
+ for (chan = 0; chan < num_chan; chan++)
+ axiadc_write(st, ADI_REG_CHAN_STATUS(chan),
+ ADI_PN_ERR | ADI_PN_OOS);
+ mdelay(4);
+
+#ifdef NUAND_MODIFICATIONS
+ uint32_t stat;
+ ret = axiadc_read(st, ADI_REG_STATUS, &stat);
+ if (ret < 0)
+ return ret;
+
+ if ((t == 1) || (stat & ADI_STATUS)) {
+ for (chan = 0, stat = 0; chan < num_chan; chan++) {
+ ret = axiadc_read(st, ADI_REG_CHAN_STATUS(chan), &tmp);
+ if (ret < 0)
+ return ret;
+ stat |= tmp;
+ }
+ } else {
+ stat = 1;
+ }
+
+ field[i][j] |= stat;
+#else
+ if ((t == 1) || (axiadc_read(st, ADI_REG_STATUS) & ADI_STATUS)) {
+ for (chan = 0, ret = 0; chan < num_chan; chan++) {
+ ret |= axiadc_read(st, ADI_REG_CHAN_STATUS(chan));
+ }
+ }
+ else {
+ ret = 1;
+ }
+
+ field[i][j] |= ret;
+#endif // NUAND_MODIFICATIONS
+ }
+ }
+ if ((flags & BE_MOREVERBOSE) && max_freq) {
+ ad9361_dig_tune_verbose_print(phy, field, t);
+ }
+ }
+
+ c0 = ad9361_find_opt(&field[0][0], 16, &s0);
+ c1 = ad9361_find_opt(&field[1][0], 16, &s1);
+
+ if (!c0 && !c1) {
+ ad9361_dig_tune_verbose_print(phy, field, t);
+ dev_err(&phy->spi->dev, "%s: Tuning %s FAILED!", __func__,
+ t ? "TX" : "RX");
+ err |= -EIO;
+ } else if (flags & BE_VERBOSE) {
+ ad9361_dig_tune_verbose_print(phy, field, t);
+ }
+
+ if (c1 > c0)
+ ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY + t,
+ DATA_CLK_DELAY(s1 + c1 / 2) |
+ RX_DATA_DELAY(0));
+ else
+ ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY + t,
+ DATA_CLK_DELAY(0) |
+ RX_DATA_DELAY(s0 + c0 / 2));
+
+ if (t == 0) {
+ if (flags & DO_IDELAY)
+ ad9361_dig_tune_iodelay(phy, 0);
+
+ /* Now do the loopback and tune the digital out */
+ ad9361_bist_prbs(phy, BIST_DISABLE);
+
+ axiadc_write(st, ADI_REG_RSTN, ADI_MMCM_RSTN);
+ axiadc_write(st, ADI_REG_RSTN, ADI_RSTN | ADI_MMCM_RSTN);
+
+ if (phy->pdata->dig_interface_tune_skipmode == 1) {
+ /* skip TX */
+ if (!(flags & SKIP_STORE_RESULT))
+ phy->pdata->port_ctrl.rx_clk_data_delay =
+ ad9361_spi_read(phy->spi, REG_RX_CLOCK_DATA_DELAY);
+
+ if (!phy->pdata->fdd) {
+ ad9361_set_ensm_mode(phy, phy->pdata->fdd,
+ phy->pdata->ensm_pin_ctrl);
+ ad9361_ensm_restore_prev_state(phy);
+ }
+
+ ad9361_tx_mute(phy, 0);
+
+ return 0;
+ }
+
+ ad9361_bist_loopback(phy, 1);
+ axiadc_write(st, 0x4000 + ADI_REG_RSTN, ADI_RSTN | ADI_MMCM_RSTN);
+
+ for (chan = 0; chan < num_chan; chan++) {
+#ifdef NUAND_MODIFICATIONS
+ ret = axiadc_read(st, ADI_REG_CHAN_CNTRL(chan), &saved_chan_ctrl0[chan]);
+ if (ret < 0)
+ return ret;
+
+ ret = axiadc_write(st, ADI_REG_CHAN_CNTRL(chan),
+ ADI_FORMAT_SIGNEXT | ADI_FORMAT_ENABLE |
+ ADI_ENABLE | ADI_IQCOR_ENB);
+ if (ret < 0)
+ return ret;
+
+ ret = axiadc_set_pnsel(st, chan, ADC_PN_CUSTOM);
+ if (ret < 0)
+ return ret;
+
+ ret = axiadc_read(st, 0x4414 + (chan) * 0x40, &saved_chan_ctrl6[chan]);
+ if (ret < 0)
+ return ret;
+
+ if (PCORE_VERSION_MAJOR(hdl_dac_version) > 7)
+ {
+ ret = axiadc_read(st, 0x4418 + (chan) * 0x40, &saved_dsel[chan]);
+ if (ret < 0)
+ return ret;
+
+ ret = axiadc_write(st, 0x4418 + (chan) * 0x40, 9);
+ if (ret < 0)
+ return ret;
+
+ ret = axiadc_write(st, 0x4414 + (chan) * 0x40, 0); /* !IQCOR_ENB */
+ if (ret < 0)
+ return ret;
+
+ ret = axiadc_write(st, 0x4044, 0x1);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = axiadc_write(st, 0x4414 + (chan) * 0x40, 1); /* DAC_PN_ENB */
+ if (ret < 0)
+ return ret;
+ }
+#else
+ saved_chan_ctrl0[chan] = axiadc_read(st, ADI_REG_CHAN_CNTRL(chan));
+ axiadc_write(st, ADI_REG_CHAN_CNTRL(chan),
+ ADI_FORMAT_SIGNEXT | ADI_FORMAT_ENABLE |
+ ADI_ENABLE | ADI_IQCOR_ENB);
+ axiadc_set_pnsel(st, chan, ADC_PN_CUSTOM);
+ saved_chan_ctrl6[chan] = axiadc_read(st, 0x4414 + (chan) * 0x40);
+ if (PCORE_VERSION_MAJOR(hdl_dac_version) > 7)
+ {
+ saved_dsel[chan] = axiadc_read(st, 0x4418 + (chan) * 0x40);
+ axiadc_write(st, 0x4418 + (chan) * 0x40, 9);
+ axiadc_write(st, 0x4414 + (chan) * 0x40, 0); /* !IQCOR_ENB */
+ axiadc_write(st, 0x4044, 0x1);
+ }
+ else
+ axiadc_write(st, 0x4414 + (chan) * 0x40, 1); /* DAC_PN_ENB */
+#endif // NUAND_MODIFICATIONS
+ }
+ if (PCORE_VERSION_MAJOR(hdl_dac_version) < 8) {
+#ifdef NUAND_MODIFICATIONS
+ ret = axiadc_read(st, 0x4048, &tmp);
+ if (ret < 0)
+ return ret;
+
+ saved = tmp;
+#else
+ saved = tmp = axiadc_read(st, 0x4048);
+#endif // NUAND_MODIFICATIONS
+ tmp &= ~0xF;
+ tmp |= 1;
+ axiadc_write(st, 0x4048, tmp);
+
+ }
+ } else {
+ if (flags & DO_ODELAY)
+ ad9361_dig_tune_iodelay(phy, 1);
+
+ ad9361_bist_loopback(phy, 0);
+
+ if (PCORE_VERSION_MAJOR(hdl_dac_version) < 8)
+ axiadc_write(st, 0x4048, saved);
+
+ for (chan = 0; chan < num_chan; chan++) {
+ axiadc_write(st, ADI_REG_CHAN_CNTRL(chan),
+ saved_chan_ctrl0[chan]);
+ axiadc_set_pnsel(st, chan, ADC_PN9);
+ if (PCORE_VERSION_MAJOR(hdl_dac_version) > 7)
+ {
+ axiadc_write(st, 0x4418 + (chan) * 0x40, saved_dsel[chan]);
+ axiadc_write(st, 0x4044, 0x1);
+ }
+
+ axiadc_write(st, 0x4414 + (chan) * 0x40, saved_chan_ctrl6[chan]);
+
+ }
+
+ if (err == -EIO) {
+ ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY,
+ phy->pdata->port_ctrl.rx_clk_data_delay);
+
+ ad9361_spi_write(phy->spi, REG_TX_CLOCK_DATA_DELAY,
+ phy->pdata->port_ctrl.tx_clk_data_delay);
+ if (!max_freq)
+ err = 0;
+ } else if (!(flags & SKIP_STORE_RESULT)) {
+ phy->pdata->port_ctrl.rx_clk_data_delay =
+ ad9361_spi_read(phy->spi, REG_RX_CLOCK_DATA_DELAY);
+ phy->pdata->port_ctrl.tx_clk_data_delay =
+ ad9361_spi_read(phy->spi, REG_TX_CLOCK_DATA_DELAY);
+ }
+
+ if (!phy->pdata->fdd) {
+ ad9361_set_ensm_mode(phy, phy->pdata->fdd, phy->pdata->ensm_pin_ctrl);
+ ad9361_ensm_restore_prev_state(phy);
+ }
+
+ axiadc_write(st, ADI_REG_RSTN, ADI_MMCM_RSTN);
+ axiadc_write(st, ADI_REG_RSTN, ADI_RSTN | ADI_MMCM_RSTN);
+
+ ad9361_tx_mute(phy, 0);
+
+ return err;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/**
+* Setup the AD9361 device.
+* @param phy The AD9361 state structure.
+* @return 0 in case of success, negative error code otherwise.
+*/
+int32_t ad9361_post_setup(struct ad9361_rf_phy *phy)
+{
+ struct axiadc_converter *conv = phy->adc_conv;
+ struct axiadc_state *st = phy->adc_state;
+ int32_t rx2tx2 = phy->pdata->rx2tx2;
+ int32_t tmp, num_chan, flags;
+ int32_t i, ret;
+
+ num_chan = (conv->chip_info->num_channels > 4) ? 4 : conv->chip_info->num_channels;
+
+#ifdef NUAND_MODIFICATIONS
+ ret = axiadc_write(st, ADI_REG_CNTRL, rx2tx2 ? 0 : ADI_R1_MODE);
+ if (ret < 0)
+ return ret;
+
+ uint32_t u32tmp;
+ ret = axiadc_read(st, 0x4048, &u32tmp);
+ if (ret < 0)
+ return ret;
+
+ tmp = (int32_t)u32tmp;
+#else
+ axiadc_write(st, ADI_REG_CNTRL, rx2tx2 ? 0 : ADI_R1_MODE);
+ tmp = axiadc_read(st, 0x4048);
+#endif // NUAND_MODIFICATIONS
+
+ if (!rx2tx2) {
+ axiadc_write(st, 0x4048, tmp | BIT(5)); /* R1_MODE */
+ axiadc_write(st, 0x404c,
+ (phy->pdata->port_ctrl.pp_conf[2] & LVDS_MODE) ? 1 : 0); /* RATE */
+ }
+ else {
+ tmp &= ~BIT(5);
+ axiadc_write(st, 0x4048, tmp);
+ axiadc_write(st, 0x404c,
+ (phy->pdata->port_ctrl.pp_conf[2] & LVDS_MODE) ? 3 : 1); /* RATE */
+ }
+
+#ifdef ALTERA_PLATFORM
+ axiadc_write(st, 0x404c, 1);
+#endif
+
+ for (i = 0; i < num_chan; i++) {
+ axiadc_write(st, ADI_REG_CHAN_CNTRL_1(i),
+ ADI_DCFILT_OFFSET(0));
+ axiadc_write(st, ADI_REG_CHAN_CNTRL_2(i),
+ (i & 1) ? 0x00004000 : 0x40000000);
+ axiadc_write(st, ADI_REG_CHAN_CNTRL(i),
+ ADI_FORMAT_SIGNEXT | ADI_FORMAT_ENABLE |
+ ADI_ENABLE | ADI_IQCOR_ENB);
+ }
+
+ flags = 0x0;
+
+#ifdef NUAND_MODIFICATIONS
+ ret = axiadc_read(st, 0x0004, &u32tmp);
+ if (ret < 0)
+ return ret;
+ ret = ad9361_dig_tune(phy, ((conv->chip_info->num_channels > 4) ||
+ u32tmp) ? 0 : 61440000, flags);
+#else
+ ret = ad9361_dig_tune(phy, ((conv->chip_info->num_channels > 4) ||
+ axiadc_read(st, 0x0004)) ? 0 : 61440000, flags);
+#endif // NUAND_MODIFICATIONS
+ if (ret < 0)
+ return ret;
+
+ if (flags & (DO_IDELAY | DO_ODELAY)) {
+#ifdef NUAND_MODIFICATIONS
+ ret = axiadc_read(st, ADI_REG_ID, &u32tmp);
+ if (ret < 0)
+ return ret;
+ ret = ad9361_dig_tune(phy, u32tmp ? 0 : 61440000, flags & BE_VERBOSE);
+#else
+ ret = ad9361_dig_tune(phy, (axiadc_read(st, ADI_REG_ID)) ?
+ 0 : 61440000, flags & BE_VERBOSE);
+#endif // NUAND_MODIFICATIONS
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = ad9361_set_trx_clock_chain(phy,
+ phy->pdata->rx_path_clks,
+ phy->pdata->tx_path_clks);
+
+ ad9361_ensm_force_state(phy, ENSM_STATE_ALERT);
+ ad9361_ensm_restore_prev_state(phy);
+
+ return ret;
+}
+#else
+/**
+ * HDL loopback enable/disable.
+ * @param phy The AD9361 state structure.
+ * @param enable Enable/disable option.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_hdl_loopback(struct ad9361_rf_phy *phy, bool enable)
+{
+ return -ENODEV;
+}
+
+/**
+ * Digital tune.
+ * @param phy The AD9361 state structure.
+ * @param max_freq Maximum frequency.
+ * @param flags Flags: BE_VERBOSE, BE_MOREVERBOSE, DO_IDELAY, DO_ODELAY.
+ * @return 0 in case of success, negative error code otherwise.
+ */
+int32_t ad9361_dig_tune(struct ad9361_rf_phy *phy, uint32_t max_freq,
+ enum dig_tune_flags flags)
+{
+ return 0;
+}
+
+/**
+* Setup the AD9361 device.
+* @param phy The AD9361 state structure.
+* @return 0 in case of success, negative error code otherwise.
+*/
+int32_t ad9361_post_setup(struct ad9361_rf_phy *phy)
+{
+ return 0;
+}
+#endif
diff --git a/Radio/HW/BladeRF/common/thirdparty/ad936x/common.h b/Radio/HW/BladeRF/common/thirdparty/ad936x/common.h
new file mode 100644
index 0000000..b140494
--- /dev/null
+++ b/Radio/HW/BladeRF/common/thirdparty/ad936x/common.h
@@ -0,0 +1,100 @@
+/***************************************************************************//**
+ * @file common.h
+ * @brief Header file of Common Driver.
+ * @author DBogdan (dragos.bogdan@analog.com)
+********************************************************************************
+ * Copyright 2013(c) Analog Devices, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * - Neither the name of Analog Devices, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * - The use of this software may or may not infringe the patent rights
+ * of one or more patent holders. This license does not release you
+ * from the requirement that you obtain separate licenses from these
+ * patent holders to use this software.
+ * - Use of the software either in source or binary form, must be run
+ * on or directly connected to an Analog Devices Inc. component.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*******************************************************************************/
+#ifndef COMMON_H_
+#define COMMON_H_
+
+/******************************************************************************/
+/***************************** Include Files **********************************/
+/******************************************************************************/
+#include <stdint.h>
+
+/******************************************************************************/
+/********************** Macros and Constants Definitions **********************/
+/******************************************************************************/
+#ifdef NUAND_MODIFICATIONS
+// just use errno
+#include <errno.h>
+#else
+#define EIO 5 /* I/O error */
+#define EAGAIN 11 /* Try again */
+#define ENOMEM 12 /* Out of memory */
+#define EFAULT 14 /* Bad address */
+#define ENODEV 19 /* No such device */
+#define EINVAL 22 /* Invalid argument */
+#define ETIMEDOUT 110 /* Connection timed out */
+#endif // NUAND_MODIFICATIONS
+
+/******************************************************************************/
+/*************************** Types Declarations *******************************/
+/******************************************************************************/
+
+#ifdef NUAND_MODIFICATIONS
+// just use stdbool
+#include <stdbool.h>
+#else
+#if defined (__STDC__) && (__STDC_VERSION__ >= 199901L)
+#include <stdbool.h>
+#else
+typedef enum { false, true } bool;
+#endif
+#endif // NUAND_MODIFICATIONS
+
+struct clk {
+ const char *name;
+ uint32_t rate;
+};
+
+struct clk_hw {
+ struct clk *clk;
+};
+
+struct clk_init_data {
+ const char *name;
+ const struct clk_ops *ops;
+ const char **parent_names;
+ uint8_t num_parents;
+ uint32_t flags;
+};
+
+struct clk_onecell_data {
+ struct clk **clks;
+ uint32_t clk_num;
+};
+
+#endif
diff --git a/Radio/HW/BladeRF/common/thirdparty/ad936x/util.c b/Radio/HW/BladeRF/common/thirdparty/ad936x/util.c
new file mode 100644
index 0000000..8965553
--- /dev/null
+++ b/Radio/HW/BladeRF/common/thirdparty/ad936x/util.c
@@ -0,0 +1,347 @@
+/***************************************************************************//**
+ * @file util.c
+ * @brief Implementation of Util Driver.
+ * @author DBogdan (dragos.bogdan@analog.com)
+********************************************************************************
+ * Copyright 2013(c) Analog Devices, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * - Neither the name of Analog Devices, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * - The use of this software may or may not infringe the patent rights
+ * of one or more patent holders. This license does not release you
+ * from the requirement that you obtain separate licenses from these
+ * patent holders to use this software.
+ * - Use of the software either in source or binary form, must be run
+ * on or directly connected to an Analog Devices Inc. component.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*******************************************************************************/
+
+/******************************************************************************/
+/***************************** Include Files **********************************/
+/******************************************************************************/
+#include "util.h"
+#include "string.h"
+#include "platform.h"
+
+/******************************************************************************/
+/*************************** Macros Definitions *******************************/
+/******************************************************************************/
+#define BITS_PER_LONG 32
+
+/***************************************************************************//**
+ * @brief clk_prepare_enable
+*******************************************************************************/
+int32_t clk_prepare_enable(struct clk *clk)
+{
+ if (clk) {
+ // Unused variable - fix compiler warning
+ }
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief clk_get_rate
+*******************************************************************************/
+uint32_t clk_get_rate(struct ad9361_rf_phy *phy,
+ struct refclk_scale *clk_priv)
+{
+ uint32_t rate = 0;
+ uint32_t source;
+
+ source = clk_priv->source;
+
+ switch (source) {
+ case TX_REFCLK:
+ case RX_REFCLK:
+ case BB_REFCLK:
+ rate = ad9361_clk_factor_recalc_rate(clk_priv,
+ phy->clk_refin->rate);
+ break;
+ case TX_RFPLL_INT:
+ case RX_RFPLL_INT:
+ rate = ad9361_rfpll_int_recalc_rate(clk_priv,
+ phy->clks[clk_priv->parent_source]->rate);
+ case RX_RFPLL_DUMMY:
+ case TX_RFPLL_DUMMY:
+ rate = ad9361_rfpll_dummy_recalc_rate(clk_priv);
+ break;
+ case TX_RFPLL:
+ case RX_RFPLL:
+ rate = ad9361_rfpll_recalc_rate(clk_priv);
+ break;
+ case BBPLL_CLK:
+ rate = ad9361_bbpll_recalc_rate(clk_priv,
+ phy->clks[clk_priv->parent_source]->rate);
+ break;
+ case ADC_CLK:
+ case R2_CLK:
+ case R1_CLK:
+ case CLKRF_CLK:
+ case RX_SAMPL_CLK:
+ case DAC_CLK:
+ case T2_CLK:
+ case T1_CLK:
+ case CLKTF_CLK:
+ case TX_SAMPL_CLK:
+ rate = ad9361_clk_factor_recalc_rate(clk_priv,
+ phy->clks[clk_priv->parent_source]->rate);
+ break;
+ default:
+ break;
+ }
+
+ return rate;
+}
+
+/***************************************************************************//**
+ * @brief clk_set_rate
+*******************************************************************************/
+int32_t clk_set_rate(struct ad9361_rf_phy *phy,
+ struct refclk_scale *clk_priv,
+ uint32_t rate)
+{
+ uint32_t source;
+ int32_t i;
+ uint32_t round_rate;
+
+ source = clk_priv->source;
+ if(phy->clks[source]->rate != rate)
+ {
+ switch (source) {
+ case TX_REFCLK:
+ case RX_REFCLK:
+ case BB_REFCLK:
+ round_rate = ad9361_clk_factor_round_rate(clk_priv, rate,
+ &phy->clk_refin->rate);
+ ad9361_clk_factor_set_rate(clk_priv, round_rate,
+ phy->clk_refin->rate);
+ phy->clks[source]->rate = ad9361_clk_factor_recalc_rate(clk_priv,
+ phy->clk_refin->rate);
+ break;
+ case TX_RFPLL_INT:
+ case RX_RFPLL_INT:
+ round_rate = ad9361_rfpll_int_round_rate(clk_priv, rate,
+ &phy->clks[clk_priv->parent_source]->rate);
+ ad9361_rfpll_int_set_rate(clk_priv, round_rate,
+ phy->clks[clk_priv->parent_source]->rate);
+ phy->clks[source]->rate = ad9361_rfpll_int_recalc_rate(clk_priv,
+ phy->clks[clk_priv->parent_source]->rate);
+ break;
+ case RX_RFPLL_DUMMY:
+ case TX_RFPLL_DUMMY:
+ ad9361_rfpll_dummy_set_rate(clk_priv, rate);
+ case TX_RFPLL:
+ case RX_RFPLL:
+ round_rate = ad9361_rfpll_round_rate(clk_priv, rate);
+ ad9361_rfpll_set_rate(clk_priv, round_rate);
+ phy->clks[source]->rate = ad9361_rfpll_recalc_rate(clk_priv);
+ break;
+ case BBPLL_CLK:
+ round_rate = ad9361_bbpll_round_rate(clk_priv, rate,
+ &phy->clks[clk_priv->parent_source]->rate);
+ ad9361_bbpll_set_rate(clk_priv, round_rate,
+ phy->clks[clk_priv->parent_source]->rate);
+ phy->clks[source]->rate = ad9361_bbpll_recalc_rate(clk_priv,
+ phy->clks[clk_priv->parent_source]->rate);
+ phy->bbpll_initialized = true;
+ break;
+ case ADC_CLK:
+ case R2_CLK:
+ case R1_CLK:
+ case CLKRF_CLK:
+ case RX_SAMPL_CLK:
+ case DAC_CLK:
+ case T2_CLK:
+ case T1_CLK:
+ case CLKTF_CLK:
+ case TX_SAMPL_CLK:
+ round_rate = ad9361_clk_factor_round_rate(clk_priv, rate,
+ &phy->clks[clk_priv->parent_source]->rate);
+ ad9361_clk_factor_set_rate(clk_priv, round_rate,
+ phy->clks[clk_priv->parent_source]->rate);
+ phy->clks[source]->rate = ad9361_clk_factor_recalc_rate(clk_priv,
+ phy->clks[clk_priv->parent_source]->rate);
+ break;
+ default:
+ break;
+ }
+ for(i = BB_REFCLK; i < BBPLL_CLK; i++)
+ {
+ phy->clks[i]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[i],
+ phy->clk_refin->rate);
+ }
+ phy->clks[BBPLL_CLK]->rate = ad9361_bbpll_recalc_rate(phy->ref_clk_scale[BBPLL_CLK],
+ phy->clks[phy->ref_clk_scale[BBPLL_CLK]->parent_source]->rate);
+ for(i = ADC_CLK; i < RX_RFPLL_INT; i++)
+ {
+ phy->clks[i]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[i],
+ phy->clks[phy->ref_clk_scale[i]->parent_source]->rate);
+ }
+ for(i = RX_RFPLL_INT; i < RX_RFPLL_DUMMY; i++)
+ {
+ phy->clks[i]->rate = ad9361_rfpll_int_recalc_rate(phy->ref_clk_scale[i],
+ phy->clks[phy->ref_clk_scale[i]->parent_source]->rate);
+ }
+ for(i = RX_RFPLL_DUMMY; i < RX_RFPLL; i++)
+ {
+ phy->clks[i]->rate = ad9361_rfpll_dummy_recalc_rate(phy->ref_clk_scale[i]);
+ }
+ for(i = RX_RFPLL; i < NUM_AD9361_CLKS; i++)
+ {
+ phy->clks[i]->rate = ad9361_rfpll_recalc_rate(phy->ref_clk_scale[i]);
+ }
+ } else {
+ if ((source == BBPLL_CLK) && !phy->bbpll_initialized) {
+ round_rate = ad9361_bbpll_round_rate(clk_priv, rate,
+ &phy->clks[clk_priv->parent_source]->rate);
+ ad9361_bbpll_set_rate(clk_priv, round_rate,
+ phy->clks[clk_priv->parent_source]->rate);
+ phy->clks[source]->rate = ad9361_bbpll_recalc_rate(clk_priv,
+ phy->clks[clk_priv->parent_source]->rate);
+ phy->bbpll_initialized = true;
+ }
+ }
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief int_sqrt
+*******************************************************************************/
+uint32_t int_sqrt(uint32_t x)
+{
+ uint32_t b, m, y = 0;
+
+ if (x <= 1)
+ return x;
+
+ m = 1UL << (BITS_PER_LONG - 2);
+ while (m != 0) {
+ b = y + m;
+ y >>= 1;
+
+ if (x >= b) {
+ x -= b;
+ y += m;
+ }
+ m >>= 2;
+ }
+
+ return y;
+}
+
+/***************************************************************************//**
+ * @brief ilog2
+*******************************************************************************/
+int32_t ilog2(int32_t x)
+{
+ int32_t A = !(!(x >> 16));
+ int32_t count = 0;
+ int32_t x_copy = x;
+
+ count = count + (A << 4);
+
+ x_copy = (((~A + 1) & (x >> 16)) + (~(~A + 1) & x));
+
+ A = !(!(x_copy >> 8));
+ count = count + (A << 3);
+ x_copy = (((~A + 1) & (x_copy >> 8)) + (~(~A + 1) & x_copy));
+
+ A = !(!(x_copy >> 4));
+ count = count + (A << 2);
+ x_copy = (((~A + 1) & (x_copy >> 4)) + (~(~A + 1) & x_copy));
+
+ A = !(!(x_copy >> 2));
+ count = count + (A << 1);
+ x_copy = (((~A + 1) & (x_copy >> 2)) + (~(~A + 1) & x_copy));
+
+ A = !(!(x_copy >> 1));
+ count = count + A;
+
+ return count;
+}
+
+/***************************************************************************//**
+ * @brief do_div
+*******************************************************************************/
+uint64_t do_div(uint64_t* n, uint64_t base)
+{
+ uint64_t mod = 0;
+
+ mod = *n % base;
+ *n = *n / base;
+
+ return mod;
+}
+
+/***************************************************************************//**
+ * @brief find_first_bit
+*******************************************************************************/
+uint32_t find_first_bit(uint32_t word)
+{
+ int32_t num = 0;
+
+ if ((word & 0xffff) == 0) {
+ num += 16;
+ word >>= 16;
+ }
+ if ((word & 0xff) == 0) {
+ num += 8;
+ word >>= 8;
+ }
+ if ((word & 0xf) == 0) {
+ num += 4;
+ word >>= 4;
+ }
+ if ((word & 0x3) == 0) {
+ num += 2;
+ word >>= 2;
+ }
+ if ((word & 0x1) == 0)
+ num += 1;
+ return num;
+}
+
+/***************************************************************************//**
+ * @brief ERR_PTR
+*******************************************************************************/
+void * ERR_PTR(long error)
+{
+ uintptr_t val = error;
+ return (void *) val;
+}
+
+/***************************************************************************//**
+ * @brief zmalloc
+*******************************************************************************/
+void *zmalloc(size_t size)
+{
+ void *ptr = malloc(size);
+ if (ptr)
+ memset(ptr, 0, size);
+ mdelay(1);
+
+ return ptr;
+}
diff --git a/Radio/HW/BladeRF/common/thirdparty/ad936x/util.h b/Radio/HW/BladeRF/common/thirdparty/ad936x/util.h
new file mode 100644
index 0000000..2b3f25e
--- /dev/null
+++ b/Radio/HW/BladeRF/common/thirdparty/ad936x/util.h
@@ -0,0 +1,186 @@
+/***************************************************************************//**
+ * @file util.h
+ * @brief Header file of Util driver.
+ * @author DBogdan (dragos.bogdan@analog.com)
+********************************************************************************
+ * Copyright 2013(c) Analog Devices, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * - Neither the name of Analog Devices, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * - The use of this software may or may not infringe the patent rights
+ * of one or more patent holders. This license does not release you
+ * from the requirement that you obtain separate licenses from these
+ * patent holders to use this software.
+ * - Use of the software either in source or binary form, must be run
+ * on or directly connected to an Analog Devices Inc. component.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*******************************************************************************/
+#ifndef __NO_OS_PORT_H__
+#define __NO_OS_PORT_H__
+
+/******************************************************************************/
+/***************************** Include Files **********************************/
+/******************************************************************************/
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "ad9361.h"
+#include "common.h"
+#include "config.h"
+
+#ifdef NUAND_MODIFICATIONS
+// include adc/dac core headers
+#include "adc_core.h"
+#include "dac_core.h"
+#endif // NUAND_MODIFICATIONS
+
+/******************************************************************************/
+/********************** Macros and Constants Definitions **********************/
+/******************************************************************************/
+#define SUCCESS 0
+#ifdef NUAND_MODIFICATIONS
+// guard on ARRAY_SIZE (already defined in libbladeRF)
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#endif
+#else
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#endif // NUAND_MODIFICATIONS
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+#define min_t(type, x, y) (type)min((type)(x), (type)(y))
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define max_t(type, x, y) (type)max((type)(x), (type)(y))
+#define clamp(val, min_val, max_val) (max(min((val), (max_val)), (min_val)))
+#define clamp_t(type, val, min_val, max_val) (type)clamp((type)(val), (type)(min_val), (type)(max_val))
+#define DIV_ROUND_UP(x, y) (((x) + (y) - 1) / (y))
+#define DIV_ROUND_CLOSEST(x, divisor) (((x) + (divisor) / 2) / (divisor))
+#define BIT(x) (1 << (x))
+#define CLK_IGNORE_UNUSED BIT(3)
+#define CLK_GET_RATE_NOCACHE BIT(6)
+
+#ifdef NUAND_MODIFICATIONS
+// Fix a whole slew of MSVC compiler errors...
+#if defined(HAVE_VERBOSE_MESSAGES)
+#define dev_err(dev, format, ...) {printf(format, ## __VA_ARGS__);printf("\n"); }
+#define dev_warn(dev, format, ...) {printf(format, ## __VA_ARGS__);printf("\n"); }
+#if defined(HAVE_DEBUG_MESSAGES)
+#define dev_dbg(dev, format, ...) {printf(format, ## __VA_ARGS__);printf("\n"); }
+#else
+#define dev_dbg(dev, format, ...) { if (0) printf(format, ## __VA_ARGS__); }
+#endif
+#define printk(format, ...) printf(format, ## __VA_ARGS__)
+#else
+#define dev_err(dev, format, ...) { if (0) printf(format, ## __VA_ARGS__); }
+#define dev_warn(dev, format, ...) { if (0) printf(format, ## __VA_ARGS__); }
+#define dev_dbg(dev, format, ...) { if (0) printf(format, ## __VA_ARGS__); }
+#define printk(format, ...) { if (0) printf(format, ## __VA_ARGS__); }
+#endif
+#else
+#if defined(HAVE_VERBOSE_MESSAGES)
+#define dev_err(dev, format, ...) ({printf(format, ## __VA_ARGS__);printf("\n"); })
+#define dev_warn(dev, format, ...) ({printf(format, ## __VA_ARGS__);printf("\n"); })
+#if defined(HAVE_DEBUG_MESSAGES)
+#define dev_dbg(dev, format, ...) ({printf(format, ## __VA_ARGS__);printf("\n"); })
+#else
+#define dev_dbg(dev, format, ...) ({ if (0) printf(format, ## __VA_ARGS__); })
+#endif
+#define printk(format, ...) printf(format, ## __VA_ARGS__)
+#else
+#define dev_err(dev, format, ...) ({ if (0) printf(format, ## __VA_ARGS__); })
+#define dev_warn(dev, format, ...) ({ if (0) printf(format, ## __VA_ARGS__); })
+#define dev_dbg(dev, format, ...) ({ if (0) printf(format, ## __VA_ARGS__); })
+#define printk(format, ...) ({ if (0) printf(format, ## __VA_ARGS__); })
+#endif
+#endif // NUAND_MODIFICATIONS
+
+struct device {
+#ifdef NUAND_MODIFICATIONS
+ // fix MSVC code C2016
+ void *noop;
+#endif // NUAND_MODIFICATIONS
+};
+
+struct spi_device {
+ struct device dev;
+ uint8_t id_no;
+#ifdef NUAND_MODIFICATIONS
+ // add userdata field
+ void *userdata;
+#endif // NUAND_MODIFICATIONS
+};
+
+#ifdef NUAND_MODIFICATIONS
+// gpio_device struct
+struct gpio_device {
+ void *userdata;
+};
+#endif // NUAND_MODIFICATIONS
+
+struct axiadc_state {
+ struct ad9361_rf_phy *phy;
+ uint32_t pcore_version;
+#ifdef NUAND_MODIFICATIONS
+ // add DAC DDS state, userdata
+ struct dds_state dac_dds_state;
+ void *userdata;
+#endif // NUAND_MODIFICATIONS
+};
+
+struct axiadc_chip_info {
+ char *name;
+ int32_t num_channels;
+};
+
+struct axiadc_converter {
+ struct axiadc_chip_info *chip_info;
+ uint32_t scratch_reg[16];
+};
+
+#ifdef WIN32
+#include "basetsd.h"
+typedef SSIZE_T ssize_t;
+#define strsep(s, ct) 0
+#define snprintf(s, n, format, ...) 0
+#define __func__ __FUNCTION__
+#endif
+
+/******************************************************************************/
+/************************ Functions Declarations ******************************/
+/******************************************************************************/
+int32_t clk_prepare_enable(struct clk *clk);
+uint32_t clk_get_rate(struct ad9361_rf_phy *phy,
+ struct refclk_scale *clk_priv);
+int32_t clk_set_rate(struct ad9361_rf_phy *phy,
+ struct refclk_scale *clk_priv,
+ uint32_t rate);
+uint32_t int_sqrt(uint32_t x);
+int32_t ilog2(int32_t x);
+uint64_t do_div(uint64_t* n,
+ uint64_t base);
+uint32_t find_first_bit(uint32_t word);
+void * ERR_PTR(long error);
+void *zmalloc(size_t size);
+
+#endif
diff --git a/Radio/HW/BladeRF/firmware_common/bladeRF.h b/Radio/HW/BladeRF/firmware_common/bladeRF.h
new file mode 100644
index 0000000..bfc8c0f
--- /dev/null
+++ b/Radio/HW/BladeRF/firmware_common/bladeRF.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2013 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef _BLADERF_FIRMWARE_COMMON_H_
+#define _BLADERF_FIRMWARE_COMMON_H_
+
+#define BLADE_USB_CMD_QUERY_VERSION 0
+#define BLADE_USB_CMD_QUERY_FPGA_STATUS 1
+#define BLADE_USB_CMD_BEGIN_PROG 2
+#define BLADE_USB_CMD_END_PROG 3
+#define BLADE_USB_CMD_RF_RX 4
+#define BLADE_USB_CMD_RF_TX 5
+#define BLADE_USB_CMD_QUERY_DEVICE_READY 6
+#define BLADE_USB_CMD_QUERY_FLASH_ID 7
+#define BLADE_USB_CMD_QUERY_FPGA_SOURCE 8
+#define BLADE_USB_CMD_FLASH_READ 100
+#define BLADE_USB_CMD_FLASH_WRITE 101
+#define BLADE_USB_CMD_FLASH_ERASE 102
+#define BLADE_USB_CMD_READ_OTP 103
+#define BLADE_USB_CMD_WRITE_OTP 104
+#define BLADE_USB_CMD_RESET 105
+#define BLADE_USB_CMD_JUMP_TO_BOOTLOADER 106
+#define BLADE_USB_CMD_READ_PAGE_BUFFER 107
+#define BLADE_USB_CMD_WRITE_PAGE_BUFFER 108
+#define BLADE_USB_CMD_LOCK_OTP 109
+#define BLADE_USB_CMD_READ_CAL_CACHE 110
+#define BLADE_USB_CMD_INVALIDATE_CAL_CACHE 111
+#define BLADE_USB_CMD_REFRESH_CAL_CACHE 112
+#define BLADE_USB_CMD_SET_LOOPBACK 113
+#define BLADE_USB_CMD_GET_LOOPBACK 114
+#define BLADE_USB_CMD_READ_LOG_ENTRY 115
+
+/* String descriptor indices */
+#define BLADE_USB_STR_INDEX_MFR 1 /* Manufacturer */
+#define BLADE_USB_STR_INDEX_PRODUCT 2 /* Product */
+#define BLADE_USB_STR_INDEX_SERIAL 3 /* Serial number */
+#define BLADE_USB_STR_INDEX_FW_VER 4 /* Firmware version */
+
+#define CAL_BUFFER_SIZE 256
+#define CAL_PAGE 768
+
+#define AUTOLOAD_BUFFER_SIZE 256
+#define AUTOLOAD_PAGE 1024
+
+#ifdef _MSC_VER
+# define PACK(decl_to_pack_) \
+ __pragma(pack(push,1)) \
+ decl_to_pack_ \
+ __pragma(pack(pop))
+#elif defined(__GNUC__)
+# define PACK(decl_to_pack_) \
+ decl_to_pack_ __attribute__((__packed__))
+#else
+#error "Unexpected compiler/environment"
+#endif
+
+PACK(
+struct bladerf_fx3_version {
+ unsigned short major;
+ unsigned short minor;
+});
+
+struct bladeRF_firmware {
+ unsigned int len;
+ unsigned char *ptr;
+};
+
+struct bladeRF_sector {
+ unsigned int idx;
+ unsigned int len;
+ unsigned char *ptr;
+};
+
+/**
+ * FPGA configuration source
+ *
+ * Note: the numbering of this enum must match bladerf_fpga_source in
+ * libbladeRF.h
+ */
+typedef enum {
+ NUAND_FPGA_CONFIG_SOURCE_INVALID = 0, /**< Uninitialized/invalid */
+ NUAND_FPGA_CONFIG_SOURCE_FLASH = 1, /**< Last FPGA load was from flash */
+ NUAND_FPGA_CONFIG_SOURCE_HOST = 2 /**< Last FPGA load was from host */
+} NuandFpgaConfigSource;
+
+#define USB_CYPRESS_VENDOR_ID 0x04b4
+#define USB_FX3_PRODUCT_ID 0x00f3
+
+#define BLADE_USB_TYPE_OUT 0x40
+#define BLADE_USB_TYPE_IN 0xC0
+#define BLADE_USB_TIMEOUT_MS 1000
+
+#define USB_NUAND_VENDOR_ID 0x2cf0
+#define USB_NUAND_BLADERF_PRODUCT_ID 0x5246
+#define USB_NUAND_BLADERF_BOOT_PRODUCT_ID 0x5247
+#define USB_NUAND_BLADERF2_PRODUCT_ID 0x5250
+
+#define USB_NUAND_LEGACY_VENDOR_ID 0x1d50
+#define USB_NUAND_BLADERF_LEGACY_PRODUCT_ID 0x6066
+#define USB_NUAND_BLADERF_LEGACY_BOOT_PRODUCT_ID 0x6080
+
+#define USB_NUAND_BLADERF_MINOR_BASE 193
+#define NUM_CONCURRENT 8
+#define NUM_DATA_URB (1024)
+#define DATA_BUF_SZ (1024*4)
+
+
+/* Interface numbers */
+#define USB_IF_LEGACY_CONFIG 0
+#define USB_IF_NULL 0
+#define USB_IF_RF_LINK 1
+#define USB_IF_SPI_FLASH 2
+#define USB_IF_CONFIG 3
+
+#endif /* _BLADERF_FIRMWARE_COMMON_H_ */
diff --git a/Radio/HW/BladeRF/firmware_common/logger_entry.h b/Radio/HW/BladeRF/firmware_common/logger_entry.h
new file mode 100644
index 0000000..03eec3f
--- /dev/null
+++ b/Radio/HW/BladeRF/firmware_common/logger_entry.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef LOGGER_ENTRY
+#define LOGGER_ENTRY
+
+#include <stdint.h>
+
+/* A log entry word is laid out as follows. All values are little endian.
+ *
+ * +--------+--------+-------------------------------------------------------+
+ * | Bit | Length | Description |
+ * +========+========+=======================================================+
+ * | 0 | 16 | Data to include with the logged event |
+ * +--------+--------+-------------------------------------------------------+
+ * | 16 | 11 | Source line number where the event was logged |
+ * +--------+--------+-------------------------------------------------------+
+ * | 27 | 5 | Source file ID where the event was logged |
+ * +--------+--------+-------------------------------------------------------+
+ *
+ */
+typedef uint32_t logger_entry;
+
+#define LOG_DATA_SHIFT 0
+#define LOG_DATA_MASK 0xffff
+
+#define LOG_LINE_SHIFT 16
+#define LOG_LINE_MASK 0x7ff
+
+#define LOG_FILE_SHIFT 27
+#define LOG_FILE_MASK 0x1f
+
+#define LOG_EOF 0x00000000
+#define LOG_ERR 0xffffffff
+
+static inline logger_entry logger_entry_pack(uint8_t file, uint16_t line,
+ uint16_t data)
+{
+ logger_entry e;
+
+ e = (data & LOG_DATA_MASK) << LOG_DATA_SHIFT;
+ e |= (line & LOG_LINE_MASK) << LOG_LINE_SHIFT;
+ e |= (file & LOG_FILE_MASK) << LOG_FILE_SHIFT;
+
+ return e;
+}
+
+static inline void logger_entry_unpack(logger_entry e, uint8_t *file,
+ uint16_t *line, uint16_t *data)
+{
+ *data = (e >> LOG_DATA_SHIFT) & LOG_DATA_MASK;
+ *line = (e >> LOG_LINE_SHIFT) & LOG_LINE_MASK;
+ *file = (e >> LOG_FILE_SHIFT) & LOG_FILE_MASK;
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/firmware_common/misc.h b/Radio/HW/BladeRF/firmware_common/misc.h
new file mode 100644
index 0000000..b3b434e
--- /dev/null
+++ b/Radio/HW/BladeRF/firmware_common/misc.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MISC_LIB
+#define MISC_LIB
+
+#include <stdint.h>
+
+uint16_t zcrc(uint8_t *data, size_t len){
+ uint16_t ret = 0;
+ int b, msb;
+ unsigned i;
+
+ for (i = 0; i < len; i++) {
+ ret ^= data[i] << 8;
+ for (b = 0; b < 8; b++) {
+ msb = ret & 0x8000;
+ ret <<= 1;
+ if (msb) {
+ ret ^= 0x1021;
+ }
+ }
+ }
+ return ret;
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/include/ad936x.h b/Radio/HW/BladeRF/fpga_common/include/ad936x.h
new file mode 100644
index 0000000..5fd43e7
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/ad936x.h
@@ -0,0 +1,858 @@
+/**
+ * @file ad936x.h
+ *
+ * @brief Interface to the library for the AD936X RFIC family
+ *
+ * Copyright (c) 2018 Nuand LLC.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef AD936X_H_
+#define AD936X_H_
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+/**
+ * The purpose of this header file is to allow the use of libad9361 without
+ * including all of the unnecessary defines, etc, used during compilation.
+ *
+ * This file is largely copied from the files named in each section. Only
+ * necessary declarations are present.
+ *
+ * In general, defines are prefixed with AD936X_ to avoid conflicts.
+ *
+ * Comments have been removed for brevity. Please see the original header
+ * files from the third-party ADI library for further details.
+ */
+
+/******************************************************************************
+ * From common.h
+ ******************************************************************************/
+
+struct clk_onecell_data {
+ struct clk **clks;
+ uint32_t clk_num;
+};
+
+/******************************************************************************
+ * From ad9361.h
+ ******************************************************************************/
+
+#define AD936X_REG_TX1_OUT_1_PHASE_CORR 0x08E
+#define AD936X_REG_TX1_OUT_1_GAIN_CORR 0x08F
+#define AD936X_REG_TX2_OUT_1_PHASE_CORR 0x090
+#define AD936X_REG_TX2_OUT_1_GAIN_CORR 0x091
+#define AD936X_REG_TX1_OUT_1_OFFSET_I 0x092
+#define AD936X_REG_TX1_OUT_1_OFFSET_Q 0x093
+#define AD936X_REG_TX2_OUT_1_OFFSET_I 0x094
+#define AD936X_REG_TX2_OUT_1_OFFSET_Q 0x095
+#define AD936X_REG_TX1_OUT_2_PHASE_CORR 0x096
+#define AD936X_REG_TX1_OUT_2_GAIN_CORR 0x097
+#define AD936X_REG_TX2_OUT_2_PHASE_CORR 0x098
+#define AD936X_REG_TX2_OUT_2_GAIN_CORR 0x099
+#define AD936X_REG_TX1_OUT_2_OFFSET_I 0x09A
+#define AD936X_REG_TX1_OUT_2_OFFSET_Q 0x09B
+#define AD936X_REG_TX2_OUT_2_OFFSET_I 0x09C
+#define AD936X_REG_TX2_OUT_2_OFFSET_Q 0x09D
+#define AD936X_REG_TX_FORCE_BITS 0x09F
+
+#define AD936X_REG_RX1_INPUT_A_PHASE_CORR 0x170
+#define AD936X_REG_RX1_INPUT_A_GAIN_CORR 0x171
+#define AD936X_REG_RX2_INPUT_A_PHASE_CORR 0x172
+#define AD936X_REG_RX2_INPUT_A_GAIN_CORR 0x173
+#define AD936X_REG_RX1_INPUT_A_Q_OFFSET 0x174
+#define AD936X_REG_RX1_INPUT_A_OFFSETS 0x175
+#define AD936X_REG_INPUT_A_OFFSETS_1 0x176
+#define AD936X_REG_RX2_INPUT_A_OFFSETS 0x177
+#define AD936X_REG_RX2_INPUT_A_I_OFFSET 0x178
+#define AD936X_REG_RX1_INPUT_BC_PHASE_CORR 0x179
+#define AD936X_REG_RX1_INPUT_BC_GAIN_CORR 0x17A
+#define AD936X_REG_RX2_INPUT_BC_PHASE_CORR 0x17B
+#define AD936X_REG_RX2_INPUT_BC_GAIN_CORR 0x17C
+#define AD936X_REG_RX1_INPUT_BC_Q_OFFSET 0x17D
+#define AD936X_REG_RX1_INPUT_BC_OFFSETS 0x17E
+#define AD936X_REG_INPUT_BC_OFFSETS_1 0x17F
+#define AD936X_REG_RX2_INPUT_BC_OFFSETS 0x180
+#define AD936X_REG_RX2_INPUT_BC_I_OFFSET 0x181
+#define AD936X_REG_FORCE_BITS 0x182
+
+#define AD936X_READ (0 << 15)
+#define AD936X_WRITE (1 << 15)
+#define AD936X_CNT(x) ((((x)-1) & 0x7) << 12)
+#define AD936X_ADDR(x) ((x)&0x3FF)
+
+enum dev_id { ID_AD9361, ID_AD9364, ID_AD9363A };
+
+enum ad9361_clocks {
+ BB_REFCLK,
+ RX_REFCLK,
+ TX_REFCLK,
+ BBPLL_CLK,
+ ADC_CLK,
+ R2_CLK,
+ R1_CLK,
+ CLKRF_CLK,
+ RX_SAMPL_CLK,
+ DAC_CLK,
+ T2_CLK,
+ T1_CLK,
+ CLKTF_CLK,
+ TX_SAMPL_CLK,
+ RX_RFPLL_INT,
+ TX_RFPLL_INT,
+ RX_RFPLL_DUMMY,
+ TX_RFPLL_DUMMY,
+ RX_RFPLL,
+ TX_RFPLL,
+ NUM_AD9361_CLKS,
+ EXT_REF_CLK,
+};
+
+enum rx_gain_table_name {
+ TBL_200_1300_MHZ,
+ TBL_1300_4000_MHZ,
+ TBL_4000_6000_MHZ,
+ RXGAIN_TBLS_END,
+};
+
+enum rx_gain_table_type {
+ RXGAIN_FULL_TBL,
+ RXGAIN_SPLIT_TBL,
+};
+
+struct rx_gain_info {
+ enum rx_gain_table_type tbl_type;
+ int32_t starting_gain_db;
+ int32_t max_gain_db;
+ int32_t gain_step_db;
+ int32_t max_idx;
+ int32_t idx_step_offset;
+};
+
+enum ad9361_pdata_rx_freq {
+ BBPLL_FREQ,
+ ADC_FREQ,
+ R2_FREQ,
+ R1_FREQ,
+ CLKRF_FREQ,
+ RX_SAMPL_FREQ,
+ NUM_RX_CLOCKS,
+};
+
+enum ad9361_pdata_tx_freq {
+ IGNORE_FREQ,
+ DAC_FREQ,
+ T2_FREQ,
+ T1_FREQ,
+ CLKTF_FREQ,
+ TX_SAMPL_FREQ,
+ NUM_TX_CLOCKS,
+};
+
+struct ad9361_fastlock_entry {
+ //#define FASTLOOK_INIT 1
+ uint8_t flags;
+ uint8_t alc_orig;
+ uint8_t alc_written;
+};
+
+struct ad9361_fastlock {
+ uint8_t save_profile;
+ uint8_t current_profile[2];
+ struct ad9361_fastlock_entry entry[2][8];
+};
+
+enum ad9361_bist_mode {
+ BIST_DISABLE,
+ BIST_INJ_TX,
+ BIST_INJ_RX,
+};
+
+enum ad9361_clkout {
+ CLKOUT_DISABLE,
+ BUFFERED_XTALN_DCXO,
+ ADC_CLK_DIV_2,
+ ADC_CLK_DIV_3,
+ ADC_CLK_DIV_4,
+ ADC_CLK_DIV_8,
+ ADC_CLK_DIV_16,
+};
+
+enum rf_gain_ctrl_mode {
+ RF_GAIN_MGC,
+ RF_GAIN_FASTATTACK_AGC,
+ RF_GAIN_SLOWATTACK_AGC,
+ RF_GAIN_HYBRID_AGC
+};
+
+enum f_agc_target_gain_index_type {
+ MAX_GAIN,
+ SET_GAIN,
+ OPTIMIZED_GAIN,
+ NO_GAIN_CHANGE,
+};
+
+enum rssi_restart_mode {
+ AGC_IN_FAST_ATTACK_MODE_LOCKS_THE_GAIN,
+ EN_AGC_PIN_IS_PULLED_HIGH,
+ ENTERS_RX_MODE,
+ GAIN_CHANGE_OCCURS,
+ SPI_WRITE_TO_REGISTER,
+ GAIN_CHANGE_OCCURS_OR_EN_AGC_PIN_PULLED_HIGH,
+};
+
+struct rssi_control {
+ enum rssi_restart_mode restart_mode;
+ bool rssi_unit_is_rx_samples;
+ uint32_t rssi_delay;
+ uint32_t rssi_wait;
+ uint32_t rssi_duration;
+};
+
+struct port_control {
+ uint8_t pp_conf[3];
+ uint8_t rx_clk_data_delay;
+ uint8_t tx_clk_data_delay;
+ uint8_t digital_io_ctrl;
+ uint8_t lvds_bias_ctrl;
+ uint8_t lvds_invert[2];
+ uint8_t clk_out_drive;
+ uint8_t dataclk_drive;
+ uint8_t data_port_drive;
+ uint8_t clk_out_slew;
+ uint8_t dataclk_slew;
+ uint8_t data_port_slew;
+};
+
+struct ctrl_outs_control {
+ uint8_t index;
+ uint8_t en_mask;
+};
+
+struct elna_control {
+ uint16_t gain_mdB;
+ uint16_t bypass_loss_mdB;
+ uint32_t settling_delay_ns;
+ bool elna_1_control_en;
+ bool elna_2_control_en;
+ bool elna_in_gaintable_all_index_en;
+};
+
+struct auxadc_control {
+ int8_t offset;
+ uint32_t temp_time_inteval_ms;
+ uint32_t temp_sensor_decimation;
+ bool periodic_temp_measuremnt;
+ uint32_t auxadc_clock_rate;
+ uint32_t auxadc_decimation;
+};
+
+struct auxdac_control {
+ uint16_t dac1_default_value;
+ uint16_t dac2_default_value;
+ bool auxdac_manual_mode_en;
+ bool dac1_in_rx_en;
+ bool dac1_in_tx_en;
+ bool dac1_in_alert_en;
+ bool dac2_in_rx_en;
+ bool dac2_in_tx_en;
+ bool dac2_in_alert_en;
+ uint8_t dac1_rx_delay_us;
+ uint8_t dac1_tx_delay_us;
+ uint8_t dac2_rx_delay_us;
+ uint8_t dac2_tx_delay_us;
+};
+
+struct gpo_control {
+ bool gpo0_inactive_state_high_en;
+ bool gpo1_inactive_state_high_en;
+ bool gpo2_inactive_state_high_en;
+ bool gpo3_inactive_state_high_en;
+ bool gpo0_slave_rx_en;
+ bool gpo0_slave_tx_en;
+ bool gpo1_slave_rx_en;
+ bool gpo1_slave_tx_en;
+ bool gpo2_slave_rx_en;
+ bool gpo2_slave_tx_en;
+ bool gpo3_slave_rx_en;
+ bool gpo3_slave_tx_en;
+ uint8_t gpo0_rx_delay_us;
+ uint8_t gpo0_tx_delay_us;
+ uint8_t gpo1_rx_delay_us;
+ uint8_t gpo1_tx_delay_us;
+ uint8_t gpo2_rx_delay_us;
+ uint8_t gpo2_tx_delay_us;
+ uint8_t gpo3_rx_delay_us;
+ uint8_t gpo3_tx_delay_us;
+};
+
+struct tx_monitor_control {
+ bool tx_mon_track_en;
+ bool one_shot_mode_en;
+ uint32_t low_high_gain_threshold_mdB;
+ uint8_t low_gain_dB;
+ uint8_t high_gain_dB;
+ uint16_t tx_mon_delay;
+ uint16_t tx_mon_duration;
+ uint8_t tx1_mon_front_end_gain;
+ uint8_t tx2_mon_front_end_gain;
+ uint8_t tx1_mon_lo_cm;
+ uint8_t tx2_mon_lo_cm;
+};
+
+struct gain_control {
+ enum rf_gain_ctrl_mode rx1_mode;
+ enum rf_gain_ctrl_mode rx2_mode;
+ uint8_t adc_ovr_sample_size;
+ uint8_t adc_small_overload_thresh;
+ uint8_t adc_large_overload_thresh;
+ uint16_t lmt_overload_high_thresh;
+ uint16_t lmt_overload_low_thresh;
+ uint16_t dec_pow_measuremnt_duration;
+ uint8_t low_power_thresh;
+ bool dig_gain_en;
+ uint8_t max_dig_gain;
+ bool mgc_rx1_ctrl_inp_en;
+ bool mgc_rx2_ctrl_inp_en;
+ uint8_t mgc_inc_gain_step;
+ uint8_t mgc_dec_gain_step;
+ uint8_t mgc_split_table_ctrl_inp_gain_mode;
+ uint8_t agc_attack_delay_extra_margin_us;
+ uint8_t agc_outer_thresh_high;
+ uint8_t agc_outer_thresh_high_dec_steps;
+ uint8_t agc_inner_thresh_high;
+ uint8_t agc_inner_thresh_high_dec_steps;
+ uint8_t agc_inner_thresh_low;
+ uint8_t agc_inner_thresh_low_inc_steps;
+ uint8_t agc_outer_thresh_low;
+ uint8_t agc_outer_thresh_low_inc_steps;
+ uint8_t adc_small_overload_exceed_counter;
+ uint8_t adc_large_overload_exceed_counter;
+ uint8_t adc_large_overload_inc_steps;
+ bool adc_lmt_small_overload_prevent_gain_inc;
+ uint8_t lmt_overload_large_exceed_counter;
+ uint8_t lmt_overload_small_exceed_counter;
+ uint8_t lmt_overload_large_inc_steps;
+ uint8_t dig_saturation_exceed_counter;
+ uint8_t dig_gain_step_size;
+ bool sync_for_gain_counter_en;
+ uint32_t gain_update_interval_us;
+ bool immed_gain_change_if_large_adc_overload;
+ bool immed_gain_change_if_large_lmt_overload;
+ uint32_t f_agc_dec_pow_measuremnt_duration;
+ uint32_t f_agc_state_wait_time_ns;
+ bool f_agc_allow_agc_gain_increase;
+ uint8_t f_agc_lp_thresh_increment_time;
+ uint8_t f_agc_lp_thresh_increment_steps;
+ uint8_t f_agc_lock_level;
+ bool f_agc_lock_level_lmt_gain_increase_en;
+ uint8_t f_agc_lock_level_gain_increase_upper_limit;
+ uint8_t f_agc_lpf_final_settling_steps;
+ uint8_t f_agc_lmt_final_settling_steps;
+ uint8_t f_agc_final_overrange_count;
+ bool f_agc_gain_increase_after_gain_lock_en;
+ enum f_agc_target_gain_index_type f_agc_gain_index_type_after_exit_rx_mode;
+ bool f_agc_use_last_lock_level_for_set_gain_en;
+ uint8_t f_agc_optimized_gain_offset;
+ bool f_agc_rst_gla_stronger_sig_thresh_exceeded_en;
+ uint8_t f_agc_rst_gla_stronger_sig_thresh_above_ll;
+ bool f_agc_rst_gla_engergy_lost_sig_thresh_exceeded_en;
+ bool f_agc_rst_gla_engergy_lost_goto_optim_gain_en;
+ uint8_t f_agc_rst_gla_engergy_lost_sig_thresh_below_ll;
+ uint8_t f_agc_energy_lost_stronger_sig_gain_lock_exit_cnt;
+ bool f_agc_rst_gla_large_adc_overload_en;
+ bool f_agc_rst_gla_large_lmt_overload_en;
+ bool f_agc_rst_gla_en_agc_pulled_high_en;
+ enum f_agc_target_gain_index_type f_agc_rst_gla_if_en_agc_pulled_high_mode;
+ uint8_t f_agc_power_measurement_duration_in_state5;
+};
+
+struct ad9361_phy_platform_data {
+ bool rx2tx2;
+ bool fdd;
+ bool fdd_independent_mode;
+ bool split_gt;
+ bool use_extclk;
+ bool ensm_pin_pulse_mode;
+ bool ensm_pin_ctrl;
+ bool debug_mode;
+ bool tdd_use_dual_synth;
+ bool tdd_skip_vco_cal;
+ bool use_ext_rx_lo;
+ bool use_ext_tx_lo;
+ bool rx1rx2_phase_inversion_en;
+ bool qec_tracking_slow_mode_en;
+ uint8_t dc_offset_update_events;
+ uint8_t dc_offset_attenuation_high;
+ uint8_t dc_offset_attenuation_low;
+ uint8_t rf_dc_offset_count_high;
+ uint8_t rf_dc_offset_count_low;
+ uint8_t dig_interface_tune_skipmode;
+ uint8_t dig_interface_tune_fir_disable;
+ uint32_t dcxo_coarse;
+ uint32_t dcxo_fine;
+ uint32_t rf_rx_input_sel;
+ uint32_t rf_tx_output_sel;
+ uint32_t rx1tx1_mode_use_rx_num;
+ uint32_t rx1tx1_mode_use_tx_num;
+ uint32_t rx_path_clks[NUM_RX_CLOCKS];
+ uint32_t tx_path_clks[NUM_TX_CLOCKS];
+ uint32_t trx_synth_max_fref;
+ uint64_t rx_synth_freq;
+ uint64_t tx_synth_freq;
+ uint32_t rf_rx_bandwidth_Hz;
+ uint32_t rf_tx_bandwidth_Hz;
+ int32_t tx_atten;
+ bool update_tx_gain_via_alert;
+ uint32_t rx_fastlock_delay_ns;
+ uint32_t tx_fastlock_delay_ns;
+ bool trx_fastlock_pinctrl_en[2];
+ enum ad9361_clkout ad9361_clkout_mode;
+ struct gain_control gain_ctrl;
+ struct rssi_control rssi_ctrl;
+ struct port_control port_ctrl;
+ struct ctrl_outs_control ctrl_outs_ctrl;
+ struct elna_control elna_ctrl;
+ struct auxadc_control auxadc_ctrl;
+ struct auxdac_control auxdac_ctrl;
+ struct gpo_control gpo_ctrl;
+ struct tx_monitor_control txmon_ctrl;
+ int32_t gpio_resetb;
+ int32_t gpio_sync;
+ int32_t gpio_cal_sw1;
+ int32_t gpio_cal_sw2;
+};
+
+struct ad9361_rf_phy {
+ enum dev_id dev_sel;
+ uint8_t id_no;
+ struct spi_device *spi;
+ struct gpio_device *gpio;
+ struct clk *clk_refin;
+ struct clk *clks[NUM_AD9361_CLKS];
+ struct refclk_scale *ref_clk_scale[NUM_AD9361_CLKS];
+ struct clk_onecell_data clk_data;
+ uint32_t (*ad9361_rfpll_ext_recalc_rate)(struct refclk_scale *clk_priv);
+ int32_t (*ad9361_rfpll_ext_round_rate)(struct refclk_scale *clk_priv,
+ uint32_t rate);
+ int32_t (*ad9361_rfpll_ext_set_rate)(struct refclk_scale *clk_priv,
+ uint32_t rate);
+ struct ad9361_phy_platform_data *pdata;
+ uint8_t prev_ensm_state;
+ uint8_t curr_ensm_state;
+ uint8_t cached_rx_rfpll_div;
+ uint8_t cached_tx_rfpll_div;
+ struct rx_gain_info rx_gain[RXGAIN_TBLS_END];
+ enum rx_gain_table_name current_table;
+ bool ensm_pin_ctl_en;
+ bool auto_cal_en;
+ uint64_t last_tx_quad_cal_freq;
+ uint32_t last_tx_quad_cal_phase;
+ uint64_t current_tx_lo_freq;
+ uint64_t current_rx_lo_freq;
+ bool current_tx_use_tdd_table;
+ bool current_rx_use_tdd_table;
+ uint32_t flags;
+ uint32_t cal_threshold_freq;
+ uint32_t current_rx_bw_Hz;
+ uint32_t current_tx_bw_Hz;
+ uint32_t rxbbf_div;
+ uint32_t rate_governor;
+ bool bypass_rx_fir;
+ bool bypass_tx_fir;
+ bool rx_eq_2tx;
+ bool filt_valid;
+ uint32_t filt_rx_path_clks[NUM_RX_CLOCKS];
+ uint32_t filt_tx_path_clks[NUM_TX_CLOCKS];
+ uint32_t filt_rx_bw_Hz;
+ uint32_t filt_tx_bw_Hz;
+ uint8_t tx_fir_int;
+ uint8_t tx_fir_ntaps;
+ uint8_t rx_fir_dec;
+ uint8_t rx_fir_ntaps;
+ uint8_t agc_mode[2];
+ bool rfdc_track_en;
+ bool bbdc_track_en;
+ bool quad_track_en;
+ bool txmon_tdd_en;
+ uint16_t auxdac1_value;
+ uint16_t auxdac2_value;
+ uint32_t tx1_atten_cached;
+ uint32_t tx2_atten_cached;
+ struct ad9361_fastlock fastlock;
+ struct axiadc_converter *adc_conv;
+ struct axiadc_state *adc_state;
+ int32_t bist_loopback_mode;
+ enum ad9361_bist_mode bist_prbs_mode;
+ enum ad9361_bist_mode bist_tone_mode;
+ uint32_t bist_tone_freq_Hz;
+ uint32_t bist_tone_level_dB;
+ uint32_t bist_tone_mask;
+ bool bbpll_initialized;
+};
+
+struct rf_rx_gain {
+ uint32_t ant;
+ int32_t gain_db;
+ uint32_t fgt_lmt_index;
+ uint32_t lmt_gain;
+ uint32_t lpf_gain;
+ uint32_t digital_gain;
+ uint32_t lna_index;
+ uint32_t tia_index;
+ uint32_t mixer_index;
+};
+
+struct rf_rssi {
+ uint32_t ant;
+ uint32_t symbol;
+ uint32_t preamble;
+ int32_t multiplier;
+ uint8_t duration;
+};
+
+int32_t ad9361_get_rx_gain(struct ad9361_rf_phy *phy,
+ uint32_t rx_id,
+ struct rf_rx_gain *rx_gain);
+int32_t ad9361_spi_read(struct spi_device *spi, uint32_t reg);
+int32_t ad9361_spi_write(struct spi_device *spi, uint32_t reg, uint32_t val);
+void ad9361_get_bist_loopback(struct ad9361_rf_phy *phy, int32_t *mode);
+int32_t ad9361_bist_loopback(struct ad9361_rf_phy *phy, int32_t mode);
+
+/******************************************************************************
+ * From ad9361_api.h
+ ******************************************************************************/
+
+typedef struct {
+ enum dev_id dev_sel;
+ uint8_t id_no;
+ uint32_t reference_clk_rate;
+ uint8_t two_rx_two_tx_mode_enable;
+ uint8_t one_rx_one_tx_mode_use_rx_num;
+ uint8_t one_rx_one_tx_mode_use_tx_num;
+ uint8_t frequency_division_duplex_mode_enable;
+ uint8_t frequency_division_duplex_independent_mode_enable;
+ uint8_t tdd_use_dual_synth_mode_enable;
+ uint8_t tdd_skip_vco_cal_enable;
+ uint32_t tx_fastlock_delay_ns;
+ uint32_t rx_fastlock_delay_ns;
+ uint8_t rx_fastlock_pincontrol_enable;
+ uint8_t tx_fastlock_pincontrol_enable;
+ uint8_t external_rx_lo_enable;
+ uint8_t external_tx_lo_enable;
+ uint8_t dc_offset_tracking_update_event_mask;
+ uint8_t dc_offset_attenuation_high_range;
+ uint8_t dc_offset_attenuation_low_range;
+ uint8_t dc_offset_count_high_range;
+ uint8_t dc_offset_count_low_range;
+ uint8_t split_gain_table_mode_enable;
+ uint32_t trx_synthesizer_target_fref_overwrite_hz;
+ uint8_t qec_tracking_slow_mode_enable;
+ uint8_t ensm_enable_pin_pulse_mode_enable;
+ uint8_t ensm_enable_txnrx_control_enable;
+ uint64_t rx_synthesizer_frequency_hz;
+ uint64_t tx_synthesizer_frequency_hz;
+ uint32_t rx_path_clock_frequencies[6];
+ uint32_t tx_path_clock_frequencies[6];
+ uint32_t rf_rx_bandwidth_hz;
+ uint32_t rf_tx_bandwidth_hz;
+ uint32_t rx_rf_port_input_select;
+ uint32_t tx_rf_port_input_select;
+ int32_t tx_attenuation_mdB;
+ uint8_t update_tx_gain_in_alert_enable;
+ uint8_t xo_disable_use_ext_refclk_enable;
+ uint32_t dcxo_coarse_and_fine_tune[2];
+ uint32_t clk_output_mode_select;
+ uint8_t gc_rx1_mode;
+ uint8_t gc_rx2_mode;
+ uint8_t gc_adc_large_overload_thresh;
+ uint8_t gc_adc_ovr_sample_size;
+ uint8_t gc_adc_small_overload_thresh;
+ uint16_t gc_dec_pow_measurement_duration;
+ uint8_t gc_dig_gain_enable;
+ uint16_t gc_lmt_overload_high_thresh;
+ uint16_t gc_lmt_overload_low_thresh;
+ uint8_t gc_low_power_thresh;
+ uint8_t gc_max_dig_gain;
+ uint8_t mgc_dec_gain_step;
+ uint8_t mgc_inc_gain_step;
+ uint8_t mgc_rx1_ctrl_inp_enable;
+ uint8_t mgc_rx2_ctrl_inp_enable;
+ uint8_t mgc_split_table_ctrl_inp_gain_mode;
+ uint8_t agc_adc_large_overload_exceed_counter;
+ uint8_t agc_adc_large_overload_inc_steps;
+ uint8_t agc_adc_lmt_small_overload_prevent_gain_inc_enable;
+ uint8_t agc_adc_small_overload_exceed_counter;
+ uint8_t agc_dig_gain_step_size;
+ uint8_t agc_dig_saturation_exceed_counter;
+ uint32_t agc_gain_update_interval_us;
+ uint8_t agc_immed_gain_change_if_large_adc_overload_enable;
+ uint8_t agc_immed_gain_change_if_large_lmt_overload_enable;
+ uint8_t agc_inner_thresh_high;
+ uint8_t agc_inner_thresh_high_dec_steps;
+ uint8_t agc_inner_thresh_low;
+ uint8_t agc_inner_thresh_low_inc_steps;
+ uint8_t agc_lmt_overload_large_exceed_counter;
+ uint8_t agc_lmt_overload_large_inc_steps;
+ uint8_t agc_lmt_overload_small_exceed_counter;
+ uint8_t agc_outer_thresh_high;
+ uint8_t agc_outer_thresh_high_dec_steps;
+ uint8_t agc_outer_thresh_low;
+ uint8_t agc_outer_thresh_low_inc_steps;
+ uint32_t agc_attack_delay_extra_margin_us;
+ uint8_t agc_sync_for_gain_counter_enable;
+ uint32_t fagc_dec_pow_measuremnt_duration;
+ uint32_t fagc_state_wait_time_ns;
+ uint8_t fagc_allow_agc_gain_increase;
+ uint32_t fagc_lp_thresh_increment_time;
+ uint32_t fagc_lp_thresh_increment_steps;
+ uint8_t fagc_lock_level_lmt_gain_increase_en;
+ uint32_t fagc_lock_level_gain_increase_upper_limit;
+ uint32_t fagc_lpf_final_settling_steps;
+ uint32_t fagc_lmt_final_settling_steps;
+ uint32_t fagc_final_overrange_count;
+ uint8_t fagc_gain_increase_after_gain_lock_en;
+ uint32_t fagc_gain_index_type_after_exit_rx_mode;
+ uint8_t fagc_use_last_lock_level_for_set_gain_en;
+ uint8_t fagc_rst_gla_stronger_sig_thresh_exceeded_en;
+ uint32_t fagc_optimized_gain_offset;
+ uint32_t fagc_rst_gla_stronger_sig_thresh_above_ll;
+ uint8_t fagc_rst_gla_engergy_lost_sig_thresh_exceeded_en;
+ uint8_t fagc_rst_gla_engergy_lost_goto_optim_gain_en;
+ uint32_t fagc_rst_gla_engergy_lost_sig_thresh_below_ll;
+ uint32_t fagc_energy_lost_stronger_sig_gain_lock_exit_cnt;
+ uint8_t fagc_rst_gla_large_adc_overload_en;
+ uint8_t fagc_rst_gla_large_lmt_overload_en;
+ uint8_t fagc_rst_gla_en_agc_pulled_high_en;
+ uint32_t fagc_rst_gla_if_en_agc_pulled_high_mode;
+ uint32_t fagc_power_measurement_duration_in_state5;
+ uint32_t rssi_delay;
+ uint32_t rssi_duration;
+ uint8_t rssi_restart_mode;
+ uint8_t rssi_unit_is_rx_samples_enable;
+ uint32_t rssi_wait;
+ uint32_t aux_adc_decimation;
+ uint32_t aux_adc_rate;
+ uint8_t aux_dac_manual_mode_enable;
+ uint32_t aux_dac1_default_value_mV;
+ uint8_t aux_dac1_active_in_rx_enable;
+ uint8_t aux_dac1_active_in_tx_enable;
+ uint8_t aux_dac1_active_in_alert_enable;
+ uint32_t aux_dac1_rx_delay_us;
+ uint32_t aux_dac1_tx_delay_us;
+ uint32_t aux_dac2_default_value_mV;
+ uint8_t aux_dac2_active_in_rx_enable;
+ uint8_t aux_dac2_active_in_tx_enable;
+ uint8_t aux_dac2_active_in_alert_enable;
+ uint32_t aux_dac2_rx_delay_us;
+ uint32_t aux_dac2_tx_delay_us;
+ uint32_t temp_sense_decimation;
+ uint16_t temp_sense_measurement_interval_ms;
+ int8_t temp_sense_offset_signed;
+ uint8_t temp_sense_periodic_measurement_enable;
+ uint8_t ctrl_outs_enable_mask;
+ uint8_t ctrl_outs_index;
+ uint32_t elna_settling_delay_ns;
+ uint32_t elna_gain_mdB;
+ uint32_t elna_bypass_loss_mdB;
+ uint8_t elna_rx1_gpo0_control_enable;
+ uint8_t elna_rx2_gpo1_control_enable;
+ uint8_t elna_gaintable_all_index_enable;
+ uint8_t digital_interface_tune_skip_mode;
+ uint8_t digital_interface_tune_fir_disable;
+ uint8_t pp_tx_swap_enable;
+ uint8_t pp_rx_swap_enable;
+ uint8_t tx_channel_swap_enable;
+ uint8_t rx_channel_swap_enable;
+ uint8_t rx_frame_pulse_mode_enable;
+ uint8_t two_t_two_r_timing_enable;
+ uint8_t invert_data_bus_enable;
+ uint8_t invert_data_clk_enable;
+ uint8_t fdd_alt_word_order_enable;
+ uint8_t invert_rx_frame_enable;
+ uint8_t fdd_rx_rate_2tx_enable;
+ uint8_t swap_ports_enable;
+ uint8_t single_data_rate_enable;
+ uint8_t lvds_mode_enable;
+ uint8_t half_duplex_mode_enable;
+ uint8_t single_port_mode_enable;
+ uint8_t full_port_enable;
+ uint8_t full_duplex_swap_bits_enable;
+ uint32_t delay_rx_data;
+ uint32_t rx_data_clock_delay;
+ uint32_t rx_data_delay;
+ uint32_t tx_fb_clock_delay;
+ uint32_t tx_data_delay;
+ uint32_t lvds_bias_mV;
+ uint8_t lvds_rx_onchip_termination_enable;
+ uint8_t rx1rx2_phase_inversion_en;
+ uint8_t lvds_invert1_control;
+ uint8_t lvds_invert2_control;
+ uint8_t clk_out_drive;
+ uint8_t dataclk_drive;
+ uint8_t data_port_drive;
+ uint8_t clk_out_slew;
+ uint8_t dataclk_slew;
+ uint8_t data_port_slew;
+ uint8_t gpo0_inactive_state_high_enable;
+ uint8_t gpo1_inactive_state_high_enable;
+ uint8_t gpo2_inactive_state_high_enable;
+ uint8_t gpo3_inactive_state_high_enable;
+ uint8_t gpo0_slave_rx_enable;
+ uint8_t gpo0_slave_tx_enable;
+ uint8_t gpo1_slave_rx_enable;
+ uint8_t gpo1_slave_tx_enable;
+ uint8_t gpo2_slave_rx_enable;
+ uint8_t gpo2_slave_tx_enable;
+ uint8_t gpo3_slave_rx_enable;
+ uint8_t gpo3_slave_tx_enable;
+ uint8_t gpo0_rx_delay_us;
+ uint8_t gpo0_tx_delay_us;
+ uint8_t gpo1_rx_delay_us;
+ uint8_t gpo1_tx_delay_us;
+ uint8_t gpo2_rx_delay_us;
+ uint8_t gpo2_tx_delay_us;
+ uint8_t gpo3_rx_delay_us;
+ uint8_t gpo3_tx_delay_us;
+ uint32_t low_high_gain_threshold_mdB;
+ uint32_t low_gain_dB;
+ uint32_t high_gain_dB;
+ uint8_t tx_mon_track_en;
+ uint8_t one_shot_mode_en;
+ uint32_t tx_mon_delay;
+ uint32_t tx_mon_duration;
+ uint32_t tx1_mon_front_end_gain;
+ uint32_t tx2_mon_front_end_gain;
+ uint32_t tx1_mon_lo_cm;
+ uint32_t tx2_mon_lo_cm;
+ int32_t gpio_resetb;
+ int32_t gpio_sync;
+ int32_t gpio_cal_sw1;
+ int32_t gpio_cal_sw2;
+ uint32_t (*ad9361_rfpll_ext_recalc_rate)(struct refclk_scale *clk_priv);
+ int32_t (*ad9361_rfpll_ext_round_rate)(struct refclk_scale *clk_priv,
+ uint32_t rate);
+ int32_t (*ad9361_rfpll_ext_set_rate)(struct refclk_scale *clk_priv,
+ uint32_t rate);
+} AD9361_InitParam;
+
+typedef struct {
+ uint32_t rx; /* 1, 2, 3(both) */
+ int32_t rx_gain; /* -12, -6, 0, 6 */
+ uint32_t rx_dec; /* 1, 2, 4 */
+ int16_t rx_coef[128];
+ uint8_t rx_coef_size;
+ uint32_t rx_path_clks[6];
+ uint32_t rx_bandwidth;
+} AD9361_RXFIRConfig;
+
+typedef struct {
+ uint32_t tx; /* 1, 2, 3(both) */
+ int32_t tx_gain; /* -6, 0 */
+ uint32_t tx_int; /* 1, 2, 4 */
+ int16_t tx_coef[128];
+ uint8_t tx_coef_size;
+ uint32_t tx_path_clks[6];
+ uint32_t tx_bandwidth;
+} AD9361_TXFIRConfig;
+
+#define AD936X_A_BALANCED 0
+#define AD936X_B_BALANCED 1
+#define AD936X_C_BALANCED 2
+#define AD936X_A_N 3
+#define AD936X_A_P 4
+#define AD936X_B_N 5
+#define AD936X_B_P 6
+#define AD936X_C_N 7
+#define AD936X_C_P 8
+#define AD936X_TX_MON1 9
+#define AD936X_TX_MON2 10
+#define AD936X_TX_MON1_2 11
+
+#define AD936X_TXA 0
+#define AD936X_TXB 1
+
+int32_t ad9361_init(struct ad9361_rf_phy **ad9361_phy,
+ AD9361_InitParam *init_param,
+ void *userdata);
+int32_t ad9361_deinit(struct ad9361_rf_phy *phy);
+int32_t ad9361_set_rx_rf_gain(struct ad9361_rf_phy *phy,
+ uint8_t ch,
+ int32_t gain_db);
+int32_t ad9361_set_rx_rf_bandwidth(struct ad9361_rf_phy *phy,
+ uint32_t bandwidth_hz);
+int32_t ad9361_get_rx_rf_bandwidth(struct ad9361_rf_phy *phy,
+ uint32_t *bandwidth_hz);
+int32_t ad9361_set_rx_sampling_freq(struct ad9361_rf_phy *phy,
+ uint32_t sampling_freq_hz);
+int32_t ad9361_get_rx_sampling_freq(struct ad9361_rf_phy *phy,
+ uint32_t *sampling_freq_hz);
+int32_t ad9361_set_rx_lo_freq(struct ad9361_rf_phy *phy, uint64_t lo_freq_hz);
+int32_t ad9361_get_rx_lo_freq(struct ad9361_rf_phy *phy, uint64_t *lo_freq_hz);
+int32_t ad9361_get_rx_rssi(struct ad9361_rf_phy *phy,
+ uint8_t ch,
+ struct rf_rssi *rssi);
+int32_t ad9361_set_rx_gain_control_mode(struct ad9361_rf_phy *phy,
+ uint8_t ch,
+ uint8_t gc_mode);
+int32_t ad9361_get_rx_gain_control_mode(struct ad9361_rf_phy *phy,
+ uint8_t ch,
+ uint8_t *gc_mode);
+int32_t ad9361_set_rx_fir_config(struct ad9361_rf_phy *phy,
+ AD9361_RXFIRConfig fir_cfg);
+int32_t ad9361_set_rx_fir_en_dis(struct ad9361_rf_phy *phy, uint8_t en_dis);
+int32_t ad9361_set_rx_rf_port_input(struct ad9361_rf_phy *phy, uint32_t mode);
+int32_t ad9361_get_rx_rf_port_input(struct ad9361_rf_phy *phy, uint32_t *mode);
+int32_t ad9361_set_tx_attenuation(struct ad9361_rf_phy *phy,
+ uint8_t ch,
+ uint32_t attenuation_mdb);
+int32_t ad9361_get_tx_attenuation(struct ad9361_rf_phy *phy,
+ uint8_t ch,
+ uint32_t *attenuation_mdb);
+int32_t ad9361_set_tx_rf_bandwidth(struct ad9361_rf_phy *phy,
+ uint32_t bandwidth_hz);
+int32_t ad9361_get_tx_rf_bandwidth(struct ad9361_rf_phy *phy,
+ uint32_t *bandwidth_hz);
+int32_t ad9361_set_tx_sampling_freq(struct ad9361_rf_phy *phy,
+ uint32_t sampling_freq_hz);
+int32_t ad9361_get_tx_sampling_freq(struct ad9361_rf_phy *phy,
+ uint32_t *sampling_freq_hz);
+int32_t ad9361_set_tx_lo_freq(struct ad9361_rf_phy *phy, uint64_t lo_freq_hz);
+int32_t ad9361_get_tx_lo_freq(struct ad9361_rf_phy *phy, uint64_t *lo_freq_hz);
+int32_t ad9361_set_tx_fir_config(struct ad9361_rf_phy *phy,
+ AD9361_TXFIRConfig fir_cfg);
+int32_t ad9361_set_tx_fir_en_dis(struct ad9361_rf_phy *phy, uint8_t en_dis);
+int32_t ad9361_get_tx_rssi(struct ad9361_rf_phy *phy,
+ uint8_t ch,
+ uint32_t *rssi_db_x_1000);
+int32_t ad9361_set_tx_rf_port_output(struct ad9361_rf_phy *phy, uint32_t mode);
+int32_t ad9361_get_tx_rf_port_output(struct ad9361_rf_phy *phy, uint32_t *mode);
+int32_t ad9361_get_temp(struct ad9361_rf_phy *phy);
+int32_t ad9361_rx_fastlock_store(struct ad9361_rf_phy *phy, uint32_t profile);
+int32_t ad9361_rx_fastlock_save(struct ad9361_rf_phy *phy,
+ uint32_t profile,
+ uint8_t *values);
+int32_t ad9361_tx_fastlock_store(struct ad9361_rf_phy *phy, uint32_t profile);
+int32_t ad9361_tx_fastlock_save(struct ad9361_rf_phy *phy,
+ uint32_t profile,
+ uint8_t *values);
+int32_t ad9361_set_no_ch_mode(struct ad9361_rf_phy *phy, uint8_t no_ch_mode);
+
+#endif // AD936X_H_
diff --git a/Radio/HW/BladeRF/fpga_common/include/ad936x_helpers.h b/Radio/HW/BladeRF/fpga_common/include/ad936x_helpers.h
new file mode 100644
index 0000000..bd0037a
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/ad936x_helpers.h
@@ -0,0 +1,127 @@
+/*
+ * 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 FPGA_COMMON_AD936X_HELPERS_H_
+#define FPGA_COMMON_AD936X_HELPERS_H_
+
+#ifdef BLADERF_NIOS_BUILD
+#include "devices.h"
+#endif // BLADERF_NIOS_BUILD
+
+#if !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X)
+
+#if !defined(BLADERF_NIOS_BUILD) && !defined(BLADERF_NIOS_PC_SIMULATION)
+#include <libbladeRF.h>
+#else
+#include "libbladeRF_nios_compat.h"
+#endif
+
+#include "ad936x.h"
+
+/**
+ * @brief Retrieve current value in TX attenuation cache.
+ *
+ * @param phy RFIC handle
+ * @param[in] ch Channel
+ *
+ * @return Cached attenuation value
+ */
+uint32_t txmute_get_cached(struct ad9361_rf_phy *phy, bladerf_channel ch);
+
+/**
+ * @brief Save a new value to the TX attenuation cache.
+ *
+ * @param phy RFIC handle
+ * @param[in] ch Channel
+ * @param[in] atten Attenuation
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+int txmute_set_cached(struct ad9361_rf_phy *phy,
+ bladerf_channel ch,
+ uint32_t atten);
+
+/**
+ * @brief Get the transmit mute state
+ *
+ * @param phy RFIC handle
+ * @param[in] ch Channel
+ * @param[out] state Mute state: true for muted, false for unmuted
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+int txmute_get(struct ad9361_rf_phy *phy, bladerf_channel ch, bool *state);
+
+/**
+ * @brief Sets the transmit mute.
+ *
+ * If muted, the TX attenuation will be set to maximum to reduce leakage
+ * as much as possible.
+ *
+ * When unmuted, TX attenuation will be restored to its previous value.
+ *
+ * @param phy RFIC handle
+ * @param[in] ch Channel
+ * @param[in] state Mute state: true for muted, false for unmuted
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+int txmute_set(struct ad9361_rf_phy *phy, bladerf_channel ch, bool state);
+
+/**
+ * @brief Set AD9361 RFIC RF port
+ *
+ * @param phy RFIC handle
+ * @param[in] ch Channel
+ * @param[in] enabled True if the channel is enabled, false otherwise
+ * @param[in] freq Frequency
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+int set_ad9361_port_by_freq(struct ad9361_rf_phy *phy,
+ bladerf_channel ch,
+ bool enabled,
+ bladerf_frequency freq);
+
+/**
+ * @brief Translate bladerf_gain_mode to rf_gain_ctrl_mode
+ *
+ * @param[in] gainmode The libbladeRF gainmode
+ * @param[out] ok True if return value is valid, false otherwise
+ *
+ * @return rf_gain_ctrl_mode
+ */
+enum rf_gain_ctrl_mode gainmode_bladerf_to_ad9361(bladerf_gain_mode gainmode,
+ bool *ok);
+
+/**
+ * @brief Translate rf_gain_ctrl_mode to bladerf_gain_mode
+ *
+ * @param[in] gainmode The RFIC gainmode
+ * @param[out] ok True if return value is valid, false otherwise
+ *
+ * @return bladerf_gain_mode
+ */
+bladerf_gain_mode gainmode_ad9361_to_bladerf(enum rf_gain_ctrl_mode gainmode,
+ bool *ok);
+
+#endif // !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X)
+#endif // FPGA_COMMON_AD936X_HELPERS_H_
diff --git a/Radio/HW/BladeRF/fpga_common/include/band_select.h b/Radio/HW/BladeRF/fpga_common/include/band_select.h
new file mode 100644
index 0000000..bae469d
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/band_select.h
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef BAND_SELECT_H_
+#define BAND_SELECT_H_
+
+#include <stdbool.h>
+
+#if !defined(BLADERF_NIOS_BUILD) && !defined(BLADERF_NIOS_PC_SIMULATION)
+# include <libbladeRF.h>
+# include "board/board.h"
+# include "log.h"
+#else
+# include "libbladeRF_nios_compat.h"
+# include "devices.h"
+#endif
+
+/**
+ * Select the bladeRF's low or high band
+ *
+ * @param dev Device handle
+ * @param module Module to configure
+ * @param low_band Configure for low band (true) or high band (false)
+ *
+ * @return 0 on succes, BLADERF_ERR_* value on failure
+ */
+int band_select(struct bladerf *dev, bladerf_module module, bool low_band);
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/include/bladerf2_common.h b/Radio/HW/BladeRF/fpga_common/include/bladerf2_common.h
new file mode 100644
index 0000000..f58e044
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/bladerf2_common.h
@@ -0,0 +1,749 @@
+/* This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2018 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef FPGA_COMMON_BLADERF2_COMMON_H_
+#define FPGA_COMMON_BLADERF2_COMMON_H_
+
+#include <errno.h>
+
+#if !defined(BLADERF_NIOS_BUILD) && !defined(BLADERF_NIOS_PC_SIMULATION)
+#include <libbladeRF.h>
+#else
+#include "libbladeRF_nios_compat.h"
+#endif // !defined(BLADERF_NIOS_BUILD) && !defined(BLADERF_NIOS_PC_SIMULATION)
+
+#include "ad936x.h"
+#include "host_config.h"
+#include "nios_pkt_retune2.h"
+#include "range.h"
+
+/**
+ * Number of modules (directions) present. 1 RX, 1 TX = 2.
+ */
+#define NUM_MODULES 2
+
+/**
+ * Frequency of the system VCTCXO, in Hz.
+ */
+static bladerf_frequency const BLADERF_VCTCXO_FREQUENCY = 38400000;
+
+/**
+ * Default reference input frequency, in Hz.
+ */
+static bladerf_frequency const BLADERF_REFIN_DEFAULT = 10000000;
+
+/**
+ * RFIC reset frequency (arbitrary)
+ */
+static bladerf_frequency const RESET_FREQUENCY = 70000000;
+
+// clang-format off
+// Config GPIO
+#define CFG_GPIO_POWERSOURCE 0
+#define CFG_GPIO_PLL_EN 11
+#define CFG_GPIO_CLOCK_OUTPUT 17
+#define CFG_GPIO_CLOCK_SELECT 18
+
+// RFFE control
+#define RFFE_CONTROL_RESET_N 0
+#define RFFE_CONTROL_ENABLE 1
+#define RFFE_CONTROL_TXNRX 2
+#define RFFE_CONTROL_EN_AGC 3
+#define RFFE_CONTROL_SYNC_IN 4
+#define RFFE_CONTROL_RX_BIAS_EN 5
+#define RFFE_CONTROL_RX_SPDT_1 6 // 6 and 7
+#define RFFE_CONTROL_RX_SPDT_2 8 // 8 and 9
+#define RFFE_CONTROL_TX_BIAS_EN 10
+#define RFFE_CONTROL_TX_SPDT_1 11 // 11 and 12
+#define RFFE_CONTROL_TX_SPDT_2 13 // 13 and 14
+#define RFFE_CONTROL_MIMO_RX_EN_0 15
+#define RFFE_CONTROL_MIMO_TX_EN_0 16
+#define RFFE_CONTROL_MIMO_RX_EN_1 17
+#define RFFE_CONTROL_MIMO_TX_EN_1 18
+#define RFFE_CONTROL_ADF_MUXOUT 19 // input only
+#define RFFE_CONTROL_CTRL_OUT 24 // input only, 24 through 31
+#define RFFE_CONTROL_SPDT_MASK 0x3
+#define RFFE_CONTROL_SPDT_SHUTDOWN 0x0 // no connection
+#define RFFE_CONTROL_SPDT_LOWBAND 0x2 // RF1 <-> RF3
+#define RFFE_CONTROL_SPDT_HIGHBAND 0x1 // RF1 <-> RF2
+
+// Trim DAC control
+#define TRIMDAC_MASK 0x3FFC // 2 through 13
+#define TRIMDAC_EN 14 // 14 and 15
+#define TRIMDAC_EN_MASK 0x3
+#define TRIMDAC_EN_ACTIVE 0x0
+#define TRIMDAC_EN_HIGHZ 0x3
+
+/* Number of fast lock profiles that can be stored in the Nios
+ * Make sure this number matches that of the Nios' devices.h */
+#define NUM_BBP_FASTLOCK_PROFILES 256
+
+/* Number of fast lock profiles that can be stored in the RFFE
+ * Make sure this number matches that of the Nios' devices.h */
+#define NUM_RFFE_FASTLOCK_PROFILES 8
+// clang-format on
+
+/**
+ * RF front end bands
+ */
+enum bladerf2_band {
+ BAND_SHUTDOWN, /**< Inactive */
+ BAND_LOW, /**< Low-band */
+ BAND_HIGH, /**< High-band */
+};
+
+/**
+ * Mapping between libbladeRF gain modes and RFIC gain modes.
+ */
+struct bladerf_rfic_gain_mode_map {
+ bladerf_gain_mode brf_mode; /**< libbladeRF gain mode */
+ enum rf_gain_ctrl_mode rfic_mode; /**< RFIC gain mode */
+};
+
+/**
+ * Mapping between frequency ranges and gain ranges.
+ */
+struct bladerf_gain_range {
+ char const *name; /**< Gain stage name */
+ struct bladerf_range frequency; /**< Frequency range */
+ struct bladerf_range gain; /**< Applicable stage gain range */
+ float offset; /**< Offset in dB, for mapping dB gain to absolute dBm. */
+};
+
+/**
+ * Mapping between string names and RFIC port identifiers.
+ */
+struct bladerf_rfic_port_name_map {
+ char const *name; /**< Port name */
+ uint32_t id; /**< Port ID */
+};
+
+/**
+ * Mapping between RF front end bands, freqencies, and physical hardware
+ * configurations
+ */
+struct band_port_map {
+ struct bladerf_range const frequency; /**< Frequency range */
+ enum bladerf2_band band; /**< RF front end band */
+ uint32_t spdt; /**< RF switch configuration */
+ uint32_t rfic_port; /**< RFIC port configuration */
+};
+
+/**
+ * @brief Round a value into an int
+ *
+ * @param x Value to round
+ *
+ * @return int
+ */
+#define __round_int(x) (x >= 0 ? (int)(x + 0.5) : (int)(x - 0.5))
+
+/**
+ * @brief Round a value into an int64
+ *
+ * @param x Value to round
+ *
+ * @return int64
+ */
+#define __round_int64(x) (x >= 0 ? (int64_t)(x + 0.5) : (int64_t)(x - 0.5))
+
+/**
+ * Subcommands for the BLADERF_RFIC_COMMAND_INIT RFIC command.
+ */
+typedef enum {
+ BLADERF_RFIC_INIT_STATE_OFF = 0, /** Non-initialized state */
+ BLADERF_RFIC_INIT_STATE_ON, /** Initialized ("open") */
+ BLADERF_RFIC_INIT_STATE_STANDBY, /** Standby ("closed") */
+} bladerf_rfic_init_state;
+
+/**
+ * Commands available with the FPGA-based RFIC interface.
+ *
+ * There is an 8-bit address space (0x00 to 0xFF) available. Nuand will not
+ * assign values between 0x80 and 0xFF, so they may be used for custom
+ * applications.
+ */
+typedef enum {
+ /** Query the status register. (Read)
+ *
+ * Pass ::BLADERF_CHANNEL_INVALID as the `ch` parameter.
+ *
+ * Return structure:
+ * +================+========================+
+ * | Bit(s) | Value |
+ * +================+========================+
+ * | 63:16 | Reserved. Set to 0. |
+ * +----------------+------------------------+
+ * | 15:8 | count of items in |
+ * | | write queue |
+ * +----------------+------------------------+
+ * | 0 | 1 if initialized, 0 |
+ * | | otherwise |
+ * +----------------+------------------------+
+ */
+ BLADERF_RFIC_COMMAND_STATUS = 0x00,
+
+ /** Initialize the RFIC. (Read/Write)
+ *
+ * Pass ::BLADERF_CHANNEL_INVALID as the `ch` parameter.
+ *
+ * Pass/expect a ::bladerf_rfic_init_state value as the `data` parameter.
+ */
+ BLADERF_RFIC_COMMAND_INIT = 0x01,
+
+ /** Enable/disable a channel. (Read/Write)
+ *
+ * Set `data` to `true` to enable the channel, or `false` to disable it.
+ */
+ BLADERF_RFIC_COMMAND_ENABLE = 0x02,
+
+ /** Sample rate for a channel. (Read/Write)
+ *
+ * Value in samples per second.
+ */
+ BLADERF_RFIC_COMMAND_SAMPLERATE = 0x03,
+
+ /** Center frequency for a channel. (Read/Write)
+ *
+ * Value in Hz. Read or write.
+ */
+ BLADERF_RFIC_COMMAND_FREQUENCY = 0x04,
+
+ /** Bandwidth for a channel. (Read/Write)
+ *
+ * Value in Hz.
+ */
+ BLADERF_RFIC_COMMAND_BANDWIDTH = 0x05,
+
+ /** Gain mode for a channel. (Read/Write)
+ *
+ * Pass a ::bladerf_gain_mode value as the `data` parameter.
+ */
+ BLADERF_RFIC_COMMAND_GAINMODE = 0x06,
+
+ /** Overall gain for a channel. (Read/Write)
+ *
+ * Value in dB.
+ */
+ BLADERF_RFIC_COMMAND_GAIN = 0x07,
+
+ /** RSSI (received signal strength indication) for a channel. (Read)
+ *
+ * Value in dB.
+ */
+ BLADERF_RFIC_COMMAND_RSSI = 0x08,
+
+ /** FIR filter setting for a channel. (Read/Write)
+ *
+ * RX channels should pass a ::bladerf_rfic_rxfir value, TX channels should
+ * pass a ::bladerf_rfic_txfir value.
+ */
+ BLADERF_RFIC_COMMAND_FILTER = 0x09,
+
+ /** TX Mute setting for a channel. (Read/Write)
+ *
+ * 1 indicates TX mute is enabled, 0 indicates it is not.
+ */
+ BLADERF_RFIC_COMMAND_TXMUTE = 0x0A,
+
+ /** Store Fastlock profile. (Write)
+ *
+ * Stores the current tuning into a fastlock profile, for later recall
+ */
+ BLADERF_RFIC_COMMAND_FASTLOCK = 0x0B,
+
+ /** User-defined functionality (placeholder 1) */
+ BLADERF_RFIC_COMMAND_USER_001 = 0x80,
+
+ /** User-defined functionality (placeholder 128) */
+ BLADERF_RFIC_COMMAND_USER_128 = 0xFF,
+
+ /* Do not add additional commands beyond 0xFF */
+} bladerf_rfic_command;
+
+/** NIOS_PKT_16x64_RFIC_STATUS return structure
+ *
+ * +===============+===================================================+
+ * | Bit(s) | Value |
+ * +===============+===================================================+
+ * | 63:16 | Reserved. Set to 0. |
+ * +---------------+---------------------------------------------------+
+ * | 15:8 | count of items in write queue |
+ * +---------------+---------------------------------------------------+
+ * | 1 | 1 if the last job executed in the write queue was |
+ * | | successful, 0 otherwise |
+ * +---------------+---------------------------------------------------+
+ * | 0 | 1 if initialized, 0 otherwise |
+ * +---------------+---------------------------------------------------+
+ */
+// clang-format off
+#define BLADERF_RFIC_STATUS_INIT_SHIFT 0
+#define BLADERF_RFIC_STATUS_INIT_MASK 0x1
+#define BLADERF_RFIC_STATUS_WQSUCCESS_SHIFT 1
+#define BLADERF_RFIC_STATUS_WQSUCCESS_MASK 0x1
+#define BLADERF_RFIC_STATUS_WQLEN_SHIFT 8
+#define BLADERF_RFIC_STATUS_WQLEN_MASK 0xff
+
+#define BLADERF_RFIC_RSSI_MULT_SHIFT 32
+#define BLADERF_RFIC_RSSI_MULT_MASK 0xFFFF
+#define BLADERF_RFIC_RSSI_PRE_SHIFT 16
+#define BLADERF_RFIC_RSSI_PRE_MASK 0xFFFF
+#define BLADERF_RFIC_RSSI_SYM_SHIFT 0
+#define BLADERF_RFIC_RSSI_SYM_MASK 0xFFFF
+// clang-format on
+
+
+/******************************************************************************/
+/* Constants */
+/******************************************************************************/
+// clang-format off
+
+/* Gain mode mappings */
+static struct bladerf_rfic_gain_mode_map const bladerf2_rx_gain_mode_map[] = {
+ {
+ FIELD_INIT(.brf_mode, BLADERF_GAIN_MGC),
+ FIELD_INIT(.rfic_mode, RF_GAIN_MGC)
+ },
+ {
+ FIELD_INIT(.brf_mode, BLADERF_GAIN_FASTATTACK_AGC),
+ FIELD_INIT(.rfic_mode, RF_GAIN_FASTATTACK_AGC)
+ },
+ {
+ FIELD_INIT(.brf_mode, BLADERF_GAIN_SLOWATTACK_AGC),
+ FIELD_INIT(.rfic_mode, RF_GAIN_SLOWATTACK_AGC)
+ },
+ {
+ FIELD_INIT(.brf_mode, BLADERF_GAIN_HYBRID_AGC),
+ FIELD_INIT(.rfic_mode, RF_GAIN_HYBRID_AGC)
+ },
+};
+
+/* RX gain ranges */
+/* Reference: ad9361.c, ad9361_gt_tableindex and ad9361_init_gain_tables */
+static struct bladerf_gain_range const bladerf2_rx_gain_ranges[] = {
+ {
+ FIELD_INIT(.name, NULL),
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 0),
+ FIELD_INIT(.max, 1300000000),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.gain, {
+ FIELD_INIT(.min, 1 - 17),
+ FIELD_INIT(.max, 77 - 17),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.offset, -17.0f),
+ },
+ {
+ FIELD_INIT(.name, NULL),
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 1300000000UL),
+ FIELD_INIT(.max, 4000000000UL),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.gain, {
+ FIELD_INIT(.min, -4 - 11),
+ FIELD_INIT(.max, 71 - 11),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.offset, -11.0f),
+ },
+ {
+ FIELD_INIT(.name, NULL),
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 4000000000UL),
+ FIELD_INIT(.max, 6000000000UL),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.gain, {
+ FIELD_INIT(.min, -10 - 2),
+ FIELD_INIT(.max, 62 - 2),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.offset, -2.0f),
+ },
+ {
+ FIELD_INIT(.name, "full"),
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 0),
+ FIELD_INIT(.max, 1300000000),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.gain, {
+ FIELD_INIT(.min, 1),
+ FIELD_INIT(.max, 77),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.offset, 0),
+ },
+ {
+ FIELD_INIT(.name, "full"),
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 1300000000UL),
+ FIELD_INIT(.max, 4000000000UL),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.gain, {
+ FIELD_INIT(.min, -4),
+ FIELD_INIT(.max, 71),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.offset, 0),
+ },
+ {
+ FIELD_INIT(.name, "full"),
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 4000000000UL),
+ FIELD_INIT(.max, 6000000000UL),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.gain, {
+ FIELD_INIT(.min, -10),
+ FIELD_INIT(.max, 62),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.offset, 0),
+ },
+};
+
+/* Overall TX gain range */
+static struct bladerf_gain_range const bladerf2_tx_gain_ranges[] = {
+ {
+ /* TX gain offset: 60 dB system gain ~= 0 dBm output */
+ FIELD_INIT(.name, NULL),
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 47000000UL),
+ FIELD_INIT(.max, 6000000000UL),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.gain, {
+ FIELD_INIT(.min, __round_int64(1000*(-89.750 + 66.0))),
+ FIELD_INIT(.max, __round_int64(1000*(0 + 66.0))),
+ FIELD_INIT(.step, 250),
+ FIELD_INIT(.scale, 0.001F),
+ }),
+ FIELD_INIT(.offset, 66.0f),
+ },
+ {
+ FIELD_INIT(.name, "dsa"),
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 47000000UL),
+ FIELD_INIT(.max, 6000000000UL),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.gain, {
+ FIELD_INIT(.min, -89750),
+ FIELD_INIT(.max, 0),
+ FIELD_INIT(.step, 250),
+ FIELD_INIT(.scale, 0.001F),
+ }),
+ FIELD_INIT(.offset, 0),
+ },
+};
+
+/* RX gain modes */
+static struct bladerf_gain_modes const bladerf2_rx_gain_modes[] = {
+ {
+ FIELD_INIT(.name, "automatic"),
+ FIELD_INIT(.mode, BLADERF_GAIN_DEFAULT)
+ },
+ {
+ FIELD_INIT(.name, "manual"),
+ FIELD_INIT(.mode, BLADERF_GAIN_MGC)
+ },
+ {
+ FIELD_INIT(.name, "fast"),
+ FIELD_INIT(.mode, BLADERF_GAIN_FASTATTACK_AGC)
+ },
+ {
+ FIELD_INIT(.name, "slow"),
+ FIELD_INIT(.mode, BLADERF_GAIN_SLOWATTACK_AGC)
+ },
+ {
+ FIELD_INIT(.name, "hybrid"),
+ FIELD_INIT(.mode, BLADERF_GAIN_HYBRID_AGC)
+ }
+};
+
+/* Default RX gain control modes */
+static enum rf_gain_ctrl_mode const bladerf2_rx_gain_mode_default[2] = {
+ RF_GAIN_SLOWATTACK_AGC, RF_GAIN_SLOWATTACK_AGC
+};
+
+/* Sample Rate Range */
+static struct bladerf_range const bladerf2_sample_rate_range = {
+ FIELD_INIT(.min, 520834),
+ FIELD_INIT(.max, 122880000),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+};
+
+/* Sample Rate Base Range */
+static struct bladerf_range const bladerf2_sample_rate_range_base = {
+ FIELD_INIT(.min, 520834),
+ FIELD_INIT(.max, 61440000),
+ FIELD_INIT(.step, 2),
+ FIELD_INIT(.scale, 1),
+};
+
+/* Sample Rate Oversample Range */
+static struct bladerf_range const bladerf2_sample_rate_range_oversample = {
+ FIELD_INIT(.min, 6250000),
+ FIELD_INIT(.max, 122880000),
+ FIELD_INIT(.step, 2),
+ FIELD_INIT(.scale, 1),
+};
+
+/* Sample rates requiring a 4x interpolation/decimation */
+static struct bladerf_range const bladerf2_sample_rate_range_4x = {
+ FIELD_INIT(.min, 520834),
+ FIELD_INIT(.max, 2083334),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+};
+
+/* Bandwidth Range */
+static struct bladerf_range const bladerf2_bandwidth_range = {
+ FIELD_INIT(.min, 200000),
+ FIELD_INIT(.max, 56000000),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+};
+
+/* Frequency Ranges */
+static struct bladerf_range const bladerf2_rx_frequency_range = {
+ FIELD_INIT(.min, 70000000),
+ FIELD_INIT(.max, 6000000000),
+ FIELD_INIT(.step, 2),
+ FIELD_INIT(.scale, 1),
+};
+
+static struct bladerf_range const bladerf2_tx_frequency_range = {
+ FIELD_INIT(.min, 47000000),
+ FIELD_INIT(.max, 6000000000),
+ FIELD_INIT(.step, 2),
+ FIELD_INIT(.scale, 1),
+};
+
+
+/* RF Ports */
+static struct bladerf_rfic_port_name_map const bladerf2_rx_port_map[] = {
+ { FIELD_INIT(.name, "A_BALANCED"), FIELD_INIT(.id, AD936X_A_BALANCED), },
+ { FIELD_INIT(.name, "B_BALANCED"), FIELD_INIT(.id, AD936X_B_BALANCED), },
+ { FIELD_INIT(.name, "C_BALANCED"), FIELD_INIT(.id, AD936X_C_BALANCED), },
+ { FIELD_INIT(.name, "A_N"), FIELD_INIT(.id, AD936X_A_N), },
+ { FIELD_INIT(.name, "A_P"), FIELD_INIT(.id, AD936X_A_P), },
+ { FIELD_INIT(.name, "B_N"), FIELD_INIT(.id, AD936X_B_N), },
+ { FIELD_INIT(.name, "B_P"), FIELD_INIT(.id, AD936X_B_P), },
+ { FIELD_INIT(.name, "C_N"), FIELD_INIT(.id, AD936X_C_N), },
+ { FIELD_INIT(.name, "C_P"), FIELD_INIT(.id, AD936X_C_P), },
+ { FIELD_INIT(.name, "TX_MON1"), FIELD_INIT(.id, AD936X_TX_MON1), },
+ { FIELD_INIT(.name, "TX_MON2"), FIELD_INIT(.id, AD936X_TX_MON2), },
+ { FIELD_INIT(.name, "TX_MON1_2"), FIELD_INIT(.id, AD936X_TX_MON1_2), },
+};
+
+static struct bladerf_rfic_port_name_map const bladerf2_tx_port_map[] = {
+ { FIELD_INIT(.name, "TXA"), FIELD_INIT(.id, AD936X_TXA), },
+ { FIELD_INIT(.name, "TXB"), FIELD_INIT(.id, AD936X_TXB), },
+};
+
+static struct band_port_map const bladerf2_rx_band_port_map[] = {
+ {
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 0),
+ FIELD_INIT(.max, 0),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.band, BAND_SHUTDOWN),
+ FIELD_INIT(.spdt, RFFE_CONTROL_SPDT_SHUTDOWN),
+ FIELD_INIT(.rfic_port, 0),
+ },
+ {
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 70000000UL),
+ FIELD_INIT(.max, 3000000000UL),
+ FIELD_INIT(.step, 2),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.band, BAND_LOW),
+ FIELD_INIT(.spdt, RFFE_CONTROL_SPDT_LOWBAND),
+ FIELD_INIT(.rfic_port, AD936X_B_BALANCED),
+ },
+ {
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 3000000000UL),
+ FIELD_INIT(.max, 6000000000UL),
+ FIELD_INIT(.step, 2),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.band, BAND_HIGH),
+ FIELD_INIT(.spdt, RFFE_CONTROL_SPDT_HIGHBAND),
+ FIELD_INIT(.rfic_port, AD936X_A_BALANCED),
+ },
+};
+
+static struct band_port_map const bladerf2_tx_band_port_map[] = {
+ {
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 0),
+ FIELD_INIT(.max, 0),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.band, BAND_SHUTDOWN),
+ FIELD_INIT(.spdt, RFFE_CONTROL_SPDT_SHUTDOWN),
+ FIELD_INIT(.rfic_port, 0),
+ },
+ {
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 46875000UL),
+ FIELD_INIT(.max, 3000000000UL),
+ FIELD_INIT(.step, 2),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.band, BAND_LOW),
+ FIELD_INIT(.spdt, RFFE_CONTROL_SPDT_LOWBAND),
+ FIELD_INIT(.rfic_port, AD936X_TXB),
+ },
+ {
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 3000000000UL),
+ FIELD_INIT(.max, 6000000000UL),
+ FIELD_INIT(.step, 2),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.band, BAND_HIGH),
+ FIELD_INIT(.spdt, RFFE_CONTROL_SPDT_HIGHBAND),
+ FIELD_INIT(.rfic_port, AD936X_TXA),
+ },
+};
+
+// clang-format on
+
+
+/******************************************************************************/
+/* Helpers */
+/******************************************************************************/
+
+/**
+ * @brief Translate libad936x error codes to libbladeRF error codes
+ *
+ * @param[in] err The error
+ *
+ * @return value from \ref RETCODES list, or 0 if err is >= 0
+ */
+int errno_ad9361_to_bladerf(int err);
+
+/**
+ * @brief Gets the band port map by frequency.
+ *
+ * @param[in] ch Channel
+ * @param[in] freq Frequency. Use 0 for the "disabled" state.
+ *
+ * @return pointer to band_port_map
+ */
+struct band_port_map const *_get_band_port_map_by_freq(bladerf_channel ch,
+ bladerf_frequency freq);
+
+/**
+ * @brief Modifies reg to configure the RF switch SPDT bits
+ *
+ * @param reg RFFE control register ptr
+ * @param[in] ch Channel
+ * @param[in] enabled True if the channel is enabled, False otherwise
+ * @param[in] freq Frequency
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+int _modify_spdt_bits_by_freq(uint32_t *reg,
+ bladerf_channel ch,
+ bool enabled,
+ bladerf_frequency freq);
+
+/**
+ * @brief Look up the RFFE control register bit for a bladerf_direction
+ *
+ * @param[in] dir Direction
+ *
+ * @return Bit index
+ */
+int _get_rffe_control_bit_for_dir(bladerf_direction dir);
+
+/**
+ * @brief Look up the RFFE control register bit for a bladerf_channel
+ *
+ * @param[in] ch Channel
+ *
+ * @return Bit index
+ */
+int _get_rffe_control_bit_for_ch(bladerf_channel ch);
+
+/**
+ * @brief Determine if a channel is active
+ *
+ * @param[in] reg RFFE control register
+ * @param[in] ch Channel
+ *
+ * @return true if active, false otherwise
+ */
+bool _rffe_ch_enabled(uint32_t reg, bladerf_channel ch);
+
+/**
+ * @brief Determine if any channel in a direction is active
+ *
+ * @param[in] reg RFFE control register
+ * @param[in] dir Direction
+ *
+ * @return true if any channel is active, false otherwise
+ */
+bool _rffe_dir_enabled(uint32_t reg, bladerf_direction dir);
+
+/**
+ * @brief Determine if any *other* channel in a direction is active
+ *
+ * @param[in] reg RFFE control register
+ * @param[in] ch Channel
+ *
+ * @return true if any channel in the same direction as ch is active, false
+ * otherwise
+ */
+bool _rffe_dir_otherwise_enabled(uint32_t reg, bladerf_channel ch);
+
+#endif // FPGA_COMMON_BLADERF2_COMMON_H_
diff --git a/Radio/HW/BladeRF/fpga_common/include/lms.h b/Radio/HW/BladeRF/fpga_common/include/lms.h
new file mode 100644
index 0000000..9f67536
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/lms.h
@@ -0,0 +1,838 @@
+/**
+ * @file lms.h
+ *
+ * @brief LMS6002D support
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013-2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef LMS_H_
+#define LMS_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#if !defined(BLADERF_NIOS_BUILD) && !defined(BLADERF_NIOS_PC_SIMULATION)
+# include <libbladeRF.h>
+# include "board/board.h"
+# define LMS_WRITE(dev, addr, value) dev->backend->lms_write(dev, addr, value)
+# define LMS_READ(dev, addr, value) dev->backend->lms_read(dev, addr, value)
+#else
+# include "libbladeRF_nios_compat.h"
+# include "devices.h"
+#endif
+
+/*
+ * lms_freq.flags values
+ */
+
+/**
+ * If this bit is set, configure PLL output buffers for operation in the
+ * bladeRF's "low band." Otherwise, configure the device for operation in the
+ * "high band."
+ */
+#define LMS_FREQ_FLAGS_LOW_BAND (1 << 0)
+
+/**
+ * Use VCOCAP value as-is, rather as using it as a starting point hint
+ * to the tuning algorithm. This offers a faster retune, with a potential
+ * trade-off in phase noise.
+ */
+#define LMS_FREQ_FLAGS_FORCE_VCOCAP (1 << 1)
+
+/**
+ * This bit indicates whether the quicktune needs to set XB-200 parameters
+ */
+#define LMS_FREQ_XB_200_ENABLE (1 << 7)
+
+/*
+ * This bit indicates the quicktune is for the RX module, not setting this bit
+ * indicates the quicktune is for the TX module.
+ */
+#define LMS_FREQ_XB_200_MODULE_RX (1 << 6)
+
+/**
+ * This is the bit mask for the filter switch configuration for the XB-200.
+ */
+#define LMS_FREQ_XB_200_FILTER_SW (3 << 4)
+
+/**
+ * Macro that indicates the number of bitshifts necessary to get to the filter
+ * switch field
+ */
+#define LMS_FREQ_XB_200_FILTER_SW_SHIFT (4)
+
+/**
+ * This is the bit mask for the path configuration for the XB-200.
+ */
+#define LMS_FREQ_XB_200_PATH (3 << 2)
+
+/**
+ * Macro that indicates the number of bitshifts necessary to get to the path
+ * field
+ */
+#define LMS_FREQ_XB_200_PATH_SHIFT (2)
+
+/**
+ * Information about the frequency calculation for the LMS6002D PLL
+ * Calculation taken from the LMS6002D Programming and Calibration Guide
+ * version 1.1r1.
+ */
+struct lms_freq {
+ uint8_t freqsel; /**< Choice of VCO and dision ratio */
+ uint8_t vcocap; /**< VCOCAP hint */
+ uint16_t nint; /**< Integer portion of f_LO given f_REF */
+ uint32_t nfrac; /**< Fractional portion of f_LO given nint and f_REF */
+ uint8_t flags; /**< Additional parameters defining the tuning
+ configuration. See LMFS_FREQ_FLAGS_* values */
+ uint8_t xb_gpio; /**< Store XB-200 switch settings */
+
+#ifndef BLADERF_NIOS_BUILD
+ uint8_t x; /**< VCO division ratio */
+#endif
+
+ uint8_t vcocap_result; /**< Filled in by retune operation to denote
+ which VCOCAP value was used */
+};
+
+/* For >= 1.5 GHz uses the high band should be used. Otherwise, the low
+ * band should be selected */
+#define BLADERF1_BAND_HIGH 1500000000
+
+/**
+ * Internal low-pass filter bandwidth selection
+ */
+typedef enum {
+ BW_28MHz, /**< 28MHz bandwidth, 14MHz LPF */
+ BW_20MHz, /**< 20MHz bandwidth, 10MHz LPF */
+ BW_14MHz, /**< 14MHz bandwidth, 7MHz LPF */
+ BW_12MHz, /**< 12MHz bandwidth, 6MHz LPF */
+ BW_10MHz, /**< 10MHz bandwidth, 5MHz LPF */
+ BW_8p75MHz, /**< 8.75MHz bandwidth, 4.375MHz LPF */
+ BW_7MHz, /**< 7MHz bandwidth, 3.5MHz LPF */
+ BW_6MHz, /**< 6MHz bandwidth, 3MHz LPF */
+ BW_5p5MHz, /**< 5.5MHz bandwidth, 2.75MHz LPF */
+ BW_5MHz, /**< 5MHz bandwidth, 2.5MHz LPF */
+ BW_3p84MHz, /**< 3.84MHz bandwidth, 1.92MHz LPF */
+ BW_3MHz, /**< 3MHz bandwidth, 1.5MHz LPF */
+ BW_2p75MHz, /**< 2.75MHz bandwidth, 1.375MHz LPF */
+ BW_2p5MHz, /**< 2.5MHz bandwidth, 1.25MHz LPF */
+ BW_1p75MHz, /**< 1.75MHz bandwidth, 0.875MHz LPF */
+ BW_1p5MHz, /**< 1.5MHz bandwidth, 0.75MHz LPF */
+} lms_bw;
+
+
+/**
+ * LNA options
+ */
+typedef enum {
+ LNA_NONE, /**< Disable all LNAs */
+ LNA_1, /**< Enable LNA1 (300MHz - 2.8GHz) */
+ LNA_2, /**< Enable LNA2 (1.5GHz - 3.8GHz) */
+ LNA_3 /**< Enable LNA3 (Unused on the bladeRF) */
+} lms_lna;
+
+
+/**
+ * Loopback paths
+ */
+typedef enum {
+ LBP_BB, /**< Baseband loopback path */
+ LBP_RF /**< RF Loopback path */
+} lms_lbp;
+
+/**
+ * PA Selection
+ */
+typedef enum {
+ PA_AUX, /**< AUX PA Enable (for RF Loopback) */
+ PA_1, /**< PA1 Enable (300MHz - 2.8GHz) */
+ PA_2, /**< PA2 Enable (1.5GHz - 3.8GHz) */
+ PA_NONE, /**< All PAs disabled */
+} lms_pa;
+
+/**
+ * LMS6002D Transceiver configuration
+ */
+struct lms_xcvr_config {
+ uint32_t tx_freq_hz; /**< Transmit frequency in Hz */
+ uint32_t rx_freq_hz; /**< Receive frequency in Hz */
+ bladerf_loopback loopback_mode; /**< Loopback Mode */
+ lms_lna lna; /**< LNA Selection */
+ lms_pa pa; /**< PA Selection */
+ lms_bw tx_bw; /**< Transmit Bandwidth */
+ lms_bw rx_bw; /**< Receive Bandwidth */
+};
+
+/**
+ * Convert an integer to a bandwidth selection.
+ * If the actual bandwidth is not available, the closest
+ * bandwidth greater than the requested bandwidth is selected.
+ * If the provided value is greater than the maximum available bandwidth, the
+ * maximum available bandiwidth is returned.
+ *
+ * @param[in] req Requested bandwidth
+ *
+ * @return closest bandwidth
+ */
+lms_bw lms_uint2bw(unsigned int req);
+
+/**
+ * Convert a bandwidth seletion to an unsigned int.
+ *
+ * @param[in] bw Bandwidth enumeration
+ *
+ * @return bandwidth as an unsigned integer
+ */
+unsigned int lms_bw2uint(lms_bw bw);
+
+/**
+ * Wrapper for setting bits in an LMS6002 register via a RMW operation
+ *
+ * @param dev Device to operate on
+ * @param addr Register address
+ * @param mask Bits to set should be '1'
+ *
+ * @return BLADERF_ERR_* value
+ */
+static inline int lms_set(struct bladerf *dev, uint8_t addr, uint8_t mask)
+{
+ int status;
+ uint8_t regval;
+
+ status = LMS_READ(dev, addr, &regval);
+ if (status != 0) {
+ return status;
+ }
+
+ regval |= mask;
+
+ return LMS_WRITE(dev, addr, regval);
+}
+
+/*
+ * Wrapper for clearing bits in an LMS6002 register via a RMW operation
+ *
+ * @param dev Device to operate on
+ * @param addr Register address
+ * @param mask Bits to clear should be '1'
+ *
+ * @return BLADERF_ERR_* value
+ */
+static inline int lms_clear(struct bladerf *dev, uint8_t addr, uint8_t mask)
+{
+ int status;
+ uint8_t regval;
+
+ status = LMS_READ(dev, addr, &regval);
+ if (status != 0) {
+ return status;
+ }
+
+ regval &= ~mask;
+
+ return LMS_WRITE(dev, addr, regval);
+}
+
+/**
+ * Configure charge pump offset currents
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module to change
+ */
+int lms_config_charge_pumps(struct bladerf *dev, bladerf_module module);
+
+/**
+ * Enable or disable the low-pass filter on the specified module
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module to change
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_lpf_enable(struct bladerf *dev, bladerf_module mod, bool enable);
+
+/**
+ * Set the LPF mode
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module to change
+ * @param[in] mode Mode to set to
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_lpf_set_mode(struct bladerf *dev, bladerf_module mod,
+ bladerf_lpf_mode mode);
+
+/**
+ * Get the LPF mode
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module to change
+ * @param[out] mode Current LPF mode
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_lpf_get_mode(struct bladerf *dev, bladerf_module mod,
+ bladerf_lpf_mode *mode);
+
+/**
+ * Set the bandwidth for the specified module
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module to set bandwidth for
+ * @param[in] bw Desired bandwidth
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_set_bandwidth(struct bladerf *dev, bladerf_module mod, lms_bw bw);
+
+/**
+ * Get the bandwidth for the specified module
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module to read
+ * @param[out] bw Current bandwidth
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_get_bandwidth(struct bladerf *dev, bladerf_module mod, lms_bw *bw);
+
+/**
+ * Enable dithering on PLL in the module to help reduce any fractional spurs
+ * which might be occurring.
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module to change
+ * @param[in] nbits Number of bits to dither (1 to 8). Ignored when
+ * disabling dithering.
+ * @param[in] enable Set to `true` to enable, `false` to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_dither_enable(struct bladerf *dev, bladerf_module mod,
+ uint8_t nbits, bool enable);
+
+/**
+ * Perform a soft reset of the LMS6002D device
+ *
+ * @param[in] dev Device handle
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_soft_reset(struct bladerf *dev);
+
+/**
+ * Set the gain of the LNA
+ *
+ * The LNA gain can be one of three values: bypassed (0dB gain),
+ * mid (MAX-6dB) and max.
+ *
+ * @param[in] dev Device handle
+ * @param[in] gain Bypass, mid or max gain
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_lna_set_gain(struct bladerf *dev, bladerf_lna_gain gain);
+
+/**
+ * Get the gain of the LNA
+ *
+ * @param[in] dev Device handle
+ * @param[out] gain Bypass, mid or max gain
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_lna_get_gain(struct bladerf *dev, bladerf_lna_gain *gain);
+
+/**
+ * Select which LNA to enable
+ *
+ * LNA1 frequency range is from 300MHz to 2.8GHz
+ * LNA2 frequency range is from 1.5GHz to 3.8GHz
+ * LNA3 frequency range is from 300MHz to 3.0GHz
+ *
+ * @param[in] dev Device handle
+ * @param[in] lna LNA to enable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_select_lna(struct bladerf *dev, lms_lna lna);
+
+/**
+ * Get the currently selected LNA
+ *
+ * @param[in] dev Device handle
+ * @param[out] lna Currently selected LNA, according to device registers
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_get_lna(struct bladerf *dev, lms_lna *lna);
+
+/**
+ * Enable or disable RXVGA1
+ *
+ * @param[in] dev Device handle
+ * @param[in] enable Set to `true` to enable, `false` to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_rxvga1_enable(struct bladerf *dev, bool enable);
+
+/**
+ * Set the gain value of RXVGA1 (in dB)
+ *
+ * @param[in] dev Device handle
+ * @param[in] gain Gain in dB (range: 5 to 30)
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_rxvga1_set_gain(struct bladerf *dev, int gain);
+
+/**
+ * Get the RXVGA1 gain value (in dB)
+ *
+ * @param[in] dev Device handle
+ * @param[out] gain Gain in dB (range: 5 to 30)
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_rxvga1_get_gain(struct bladerf *dev, int *gain);
+
+/**
+ * Enable or disable RXVGA2
+ *
+ * @param[in] dev Device handle
+ * @param[in] enable Set to `true` to enable, `false` to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_rxvga2_enable(struct bladerf *dev, bool enable);
+
+/**
+ * Set the gain value of RXVGA2 (in dB)
+ *
+ * The range of gain values is from 0dB to 60dB.
+ * Anything above 30dB is not recommended as a gain setting and will be clamped.
+ *
+ * @param[in] dev Device handle
+ * @param[in] gain Gain in dB (range: 0 to 30)
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_rxvga2_set_gain(struct bladerf *dev, int gain);
+
+/**
+ * Get the RXVGA2 gain value (in dB)
+ *
+ * @param[in] dev Device handle
+ * @param[out] gain Gain in dB (range: 0 to 30)
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_rxvga2_get_gain(struct bladerf *dev, int *gain);
+
+/**
+ * Set the gain in dB of TXVGA2.
+ *
+ * The range of gain values is from 0dB to 25dB.
+ * Anything above 25 will be clamped at 25.
+ *
+ * @param[in] dev Device handle
+ * @param[in] gain Gain in dB (range: 0 to 25). Out of range values will
+ * be clamped.
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_txvga2_set_gain(struct bladerf *dev, int gain);
+
+/**
+ * Get the gain in dB of TXVGA2.
+ *
+ * @param[in] dev Device handle
+ * @param[out] gain Gain in dB (range: 0 to 25)
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_txvga2_get_gain(struct bladerf *dev, int *gain);
+
+/**
+ * Set the gain in dB of TXVGA1.
+ *
+ * The range of gain values is from -35dB to -4dB.
+ *
+ * @param[in] dev Device handle
+ * @param[in] gain Gain in dB (range: -4 to -35). Out of range values
+ * will be clamped.
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_txvga1_set_gain(struct bladerf *dev, int gain);
+
+/**
+ * Get the gain in dB of TXVGA1.
+ *
+ * The range of gain values is from -35dB to -4dB.
+ *
+ * @param[in] dev Device handle
+ * @param[out] gain Gain in dB (range: -4 to -35)
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_txvga1_get_gain(struct bladerf *dev, int *gain);
+
+/**
+ * Enable or disable a PA
+ *
+ * @note PA_ALL is NOT valid for enabling, only for disabling.
+ *
+ * @param[in] dev Device handle
+ * @param[in] pa PA to enable
+ * @param[in] enable Set to `true` to enable, `false` to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_pa_enable(struct bladerf *dev, lms_pa pa, bool enable);
+
+/**
+ * Enable or disable the peak detectors.
+ *
+ * This is used as a baseband feedback to the system during transmit for
+ * calibration purposes.
+ *
+ * @note You cannot actively receive RF when the peak detectors are enabled.
+ *
+ * @param[in] dev Device handle
+ * @param[in] enable Set to `true` to enable, `false` to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_peakdetect_enable(struct bladerf *dev, bool enable);
+
+/**
+ * Enable or disable the RF front end.
+ *
+ * @param[in] dev Device handle
+ * @param[in] module Module to enable or disable
+ * @param[in] enable Set to `true` to enable, `false` to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_enable_rffe(struct bladerf *dev, bladerf_module module, bool enable);
+
+/**
+ * Configure TX -> RX loopback mode
+ *
+ * @param[in] dev Device handle
+ * @param[in] mode Loopback mode. USE BLADERF_LB_NONE to disable
+ * loopback functionality.
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_set_loopback_mode(struct bladerf *dev, bladerf_loopback mode);
+
+/**
+ * Figure out what loopback mode we're in.
+ *
+ * @param[in] dev Device handle
+ * @param[out] mode Current loopback mode, or BLADERF_LB_NONE if
+ * loopback is not enabled.
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_get_loopback_mode(struct bladerf *dev, bladerf_loopback *mode);
+
+/**
+ * Top level power down of the LMS6002D
+ *
+ * @param[in] dev Device handle
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_power_down(struct bladerf *dev);
+
+/**
+ * Enable or disable the PLL of a module.
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module PLL to enable
+ * @param[in] enable Set to `true` to enable, `false` to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_pll_enable(struct bladerf *dev, bladerf_module mod, bool enable);
+
+/**
+ * Enable or disable the RX subsystem
+ *
+ * @param[in] dev Device handle
+ * @param[in] enable Set to `true` to enable, `false` to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_rx_enable(struct bladerf *dev, bool enable);
+
+/**
+ * Enable or disable the TX subsystem
+ *
+ * @param[in] dev Device handle
+ * @param[in] enable Set to `true` to enable, `false` to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_tx_enable(struct bladerf *dev, bool enable);
+
+/**
+ * Converts a frequency structure into the final frequency in Hz
+ *
+ * @param[in] f Frequency structure to convert
+ * @returns The closest frequency in Hz that `f` can be converted to
+ */
+uint32_t lms_frequency_to_hz(struct lms_freq *f);
+
+/**
+ * Pretty print a frequency structure
+ *
+ * @note This is intended only for debug purposes. The log level must
+ * be set to DEBUG for this output to be made visible.
+ *
+ * @param[in] freq Frequency structure to print out
+ */
+void lms_print_frequency(struct lms_freq *freq);
+
+/**
+ * Get the frequency structure of the module
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module to change
+ * @param[out] freq LMS frequency structure detailing VCO settings
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_get_frequency(struct bladerf *dev, bladerf_module mod,
+ struct lms_freq *freq);
+
+/**
+ * Fetch "Quick tune" parameters
+ *
+ * @param[in] dev Device handle
+ * @param[in] module Module to query
+ * @param[out] quick_tune Quick retune parameters
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_get_quick_tune(struct bladerf *dev,
+ bladerf_module module,
+ struct bladerf_quick_tune *quick_tune);
+
+/**
+ * Calculate the parameters to tune to a specified frequency
+ *
+ * @param[in] freq Desired frequency
+ * @param[out] f Computed tuning parameters
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_calculate_tuning_params(unsigned int freq, struct lms_freq *f);
+
+/**
+ * Set the frequency of a module, given the lms_freq structure
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module to tune
+ * @param[in] f lms_freq structure contaning desired tuning parameters
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_set_precalculated_frequency(struct bladerf *dev, bladerf_module mod,
+ struct lms_freq *f);
+
+/**
+ * Set the frequency of a module in Hz
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module to change
+ * @param[in] freq Frequency in Hz to tune
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+static inline int lms_set_frequency(struct bladerf *dev,
+ bladerf_module mod, uint32_t freq)
+{
+ struct lms_freq f;
+ int status;
+
+ status = lms_calculate_tuning_params(freq, &f);
+ if (status < 0) {
+ return status;
+ }
+
+ return lms_set_precalculated_frequency(dev, mod, &f);
+}
+
+/**
+ * Read back every register from the LMS6002D device.
+ *
+ * @note This is intended only for debug purposes.
+ *
+ * @param[in] dev Device handle
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_dump_registers(struct bladerf *dev);
+
+/**
+ * Calibrate the DC offset value for RX and TX modules for the
+ * direct conversion receiver.
+ *
+ * @param[in] dev Device handle
+ * @param[in] module Module to calibrate
+ *
+ * @return 0 on success, -1 on failure.
+ */
+int lms_calibrate_dc(struct bladerf *dev, bladerf_cal_module module);
+
+/**
+ * Load DC calibration values directly via device registers instead of
+ * running autocalibration routines.
+ *
+ * @param[in] dev Device handle
+ * @param[in] dc_cals Calibration values to load
+ */
+int lms_set_dc_cals(struct bladerf *dev,
+ const struct bladerf_lms_dc_cals *dc_cals);
+
+/**
+ * Retrieve the DC calibration values currently in use
+ *
+ * @param[in] dev Device handle
+ * @param[out] dc_cals Calibration values to load
+ */
+int lms_get_dc_cals(struct bladerf *dev, struct bladerf_lms_dc_cals *dc_cals);
+
+/**
+ * Initialize and configure the LMS6002D given the transceiver
+ * configuration passed in.
+ *
+ * @param[in] dev Device handle
+ * @param[in] config Transceiver configuration
+ *
+ * @return 0 on success, -1 on failure.
+ */
+int lms_config_init(struct bladerf *dev, struct lms_xcvr_config *config);
+
+/**
+ * Select the appropriate band fore the specified frequency
+ *
+ * @note This is band selection is specific to how the bladeRF is connected
+ * to the LNA and PA blocks.
+ *
+ * @param[in] dev Device handle
+ * @param[in] module Module to configure
+ * @parma[in] low_band Select the low band
+ *
+ * @return 0 on succes, BLADERF_ERR_* value on failure
+ */
+int lms_select_band(struct bladerf *dev, bladerf_module module, bool low_band);
+
+/**
+ * Select internal or external sampling
+ *
+ * @param[in] dev Device handle
+ * @param[in] sampling Desired sampling mode
+ *
+ * @return 0 on succes, BLADERF_ERR_* value on failure
+ */
+int lms_select_sampling(struct bladerf *dev, bladerf_sampling sampling);
+
+/**
+ * Get the current sampling type
+ *
+ * @param[in] dev Device handle
+ * @param[out] sampling Desired sampling mode
+ *
+ * @return 0 on succes, BLADERF_ERR_* value on failure
+ */
+int lms_get_sampling(struct bladerf *dev, bladerf_sampling *sampling);
+
+/**
+ * Set the DC offset value on the I channel
+ *
+ * For consistency with other bladeRF correction values,
+ * this value is scaled to [-2048, 2048].
+ *
+ * @param[in] dev Device handle
+ * @param[in] module Module to adjust
+ * @param[in] value DC offset adjustment value to write
+ *
+ * @return 0 on succes, BLADERF_ERR_* value on failure
+ */
+int lms_set_dc_offset_i(struct bladerf *dev,
+ bladerf_module module, uint16_t value);
+
+/**
+ * Get the DC offset value on the I channel
+ *
+ * For consistency with other bladeRF correction values,
+ * this value is scaled to [-2048, 2048].
+ *
+ * @param[in] dev Device handle
+ * @param[in] module Module to adjust
+ * @param[out] value On success, the DC offset value on the I channel
+ *
+ * @return 0 on succes, BLADERF_ERR_* value on failure
+ */
+int lms_get_dc_offset_i(struct bladerf *dev,
+ bladerf_module module, int16_t *value);
+
+/**
+ * Set the DC offset value on the Q channel.
+ *
+ * For consistency with other bladeRF correction values,
+ * this value is scaled to [-2048, 2048].
+ *
+ * @param[in] dev Device handle
+ * @param[in] module Module to adjust
+ * @param[in] value DC offset adjustment value to write
+ *
+ * @return 0 on succes, BLADERF_ERR_* value on failure
+ */
+int lms_set_dc_offset_q(struct bladerf *dev,
+ bladerf_module module, int16_t value);
+
+/**
+ * Get the DC offset value on the Q channel
+ *
+ * For consistency with other bladeRF correction values,
+ * this value is scaled to [-2048, 2048].
+ *
+ * @param[in] dev Device handle
+ * @param[in] module Module to adjust
+ * @param[out] value On success, the DC offset value on the I channel
+ *
+ * @return 0 on succes, BLADERF_ERR_* value on failure
+ */
+int lms_get_dc_offset_q(struct bladerf *dev,
+ bladerf_module module, int16_t *value);
+
+#endif /* LMS_H_ */
diff --git a/Radio/HW/BladeRF/fpga_common/include/nios_pkt_16x64.h b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_16x64.h
new file mode 100644
index 0000000..9e0b417
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_16x64.h
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLADERF_NIOS_PKT_16x64_H_
+#define BLADERF_NIOS_PKT_16x64_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+/*
+ * This file defines the Host <-> FPGA (NIOS II) packet formats for accesses
+ * to devices/blocks with 16-bit addresses and 64-bit data
+ *
+ *
+ * Request
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | Target ID (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 2 | Flags (Note 2) |
+ * +----------------+---------------------------------------------------------+
+ * | 3 | Reserved. Set to 0x00. |
+ * +----------------+---------------------------------------------------------+
+ * | 5:4 | 16-bit address, little-endian |
+ * +----------------+---------------------------------------------------------+
+ * | 13:6 | 64-bit data, little-endian |
+ * +----------------+---------------------------------------------------------+
+ * | 15:14 | Reserved. Set to 0. |
+ * +----------------+---------------------------------------------------------+
+ *
+ *
+ * Response
+ * ----------------------
+ *
+ * The response packet contains the same information as the request.
+ * A status flag will be set if the operation completed successfully.
+ *
+ * In the case of a read request, the data field will contain the read data, if
+ * the read succeeded.
+ *
+ * (Note 1)
+ * The "Target ID" refers to the peripheral, device, or block to access.
+ * See the NIOS_PKT_16x64_TARGET_* values.
+ *
+ * (Note 2)
+ * The flags are defined as follows:
+ *
+ * +================+========================+
+ * | Bit(s) | Value |
+ * +================+========================+
+ * | 7:2 | Reserved. Set to 0. |
+ * +----------------+------------------------+
+ * | | Status. Only used in |
+ * | | response packet. |
+ * | | Ignored in request. |
+ * | 1 | |
+ * | | 1 = Success |
+ * | | 0 = Failure |
+ * +----------------+------------------------+
+ * | 0 | 0 = Read operation |
+ * | | 1 = Write operation |
+ * +----------------+------------------------+
+ *
+ */
+
+#define NIOS_PKT_16x64_MAGIC ((uint8_t) 'E')
+
+/* Request packet indices */
+#define NIOS_PKT_16x64_IDX_MAGIC 0
+#define NIOS_PKT_16x64_IDX_TARGET_ID 1
+#define NIOS_PKT_16x64_IDX_FLAGS 2
+#define NIOS_PKT_16x64_IDX_RESV1 3
+#define NIOS_PKT_16x64_IDX_ADDR 4
+#define NIOS_PKT_16x64_IDX_DATA 6
+#define NIOS_PKT_16x64_IDX_RESV2 14
+
+/* Target IDs */
+#define NIOS_PKT_16x64_TARGET_AD9361 0x00
+#define NIOS_PKT_16x64_TARGET_RFIC 0x01 /* RFIC control */
+
+/* IDs 0x80 through 0xff will not be assigned by Nuand. These are reserved
+ * for user customizations */
+#define NIOS_PKT_16x64_TARGET_USR1 0x80
+#define NIOS_PKT_16x64_TARGET_USR128 0xff
+
+/* Flag bits */
+#define NIOS_PKT_16x64_FLAG_WRITE (1 << 0)
+#define NIOS_PKT_16x64_FLAG_SUCCESS (1 << 1)
+
+/**
+ * Sub-addresses for rfic target.
+ *
+ * +================+============================================+
+ * | Bit(s) | Value |
+ * +================+============================================+
+ * | 15:12 | Reserved. Set to 0. |
+ * +----------------+--------------------------------------------+
+ * | 11:8 | bladerf_channel & 0xf |
+ * | | 1111 = system-wide |
+ * +----------------+--------------------------------------------+
+ * | 7:0 | Subaddress. See bladerf_rfic_command enum. |
+ * +----------------+--------------------------------------------+
+ */
+
+/* Pack the request buffer */
+static inline void nios_pkt_16x64_pack(uint8_t *buf, uint8_t target, bool write,
+ uint16_t addr, uint64_t data)
+{
+ buf[NIOS_PKT_16x64_IDX_MAGIC] = NIOS_PKT_16x64_MAGIC;
+ buf[NIOS_PKT_16x64_IDX_TARGET_ID] = target;
+
+ if (write) {
+ buf[NIOS_PKT_16x64_IDX_FLAGS] = NIOS_PKT_16x64_FLAG_WRITE;
+ } else {
+ buf[NIOS_PKT_16x64_IDX_FLAGS] = 0x00;
+ }
+
+ buf[NIOS_PKT_16x64_IDX_RESV1] = 0x00;
+
+ buf[NIOS_PKT_16x64_IDX_ADDR + 0] = (addr >> 0);
+ buf[NIOS_PKT_16x64_IDX_ADDR + 1] = (addr >> 8);
+
+ buf[NIOS_PKT_16x64_IDX_DATA + 0] = (data >> 0) & 0xff;
+ buf[NIOS_PKT_16x64_IDX_DATA + 1] = (data >> 8) & 0xff;
+ buf[NIOS_PKT_16x64_IDX_DATA + 2] = (data >> 16) & 0xff;
+ buf[NIOS_PKT_16x64_IDX_DATA + 3] = (data >> 24) & 0xff;
+ buf[NIOS_PKT_16x64_IDX_DATA + 4] = (data >> 32) & 0xff;
+ buf[NIOS_PKT_16x64_IDX_DATA + 5] = (data >> 40) & 0xff;
+ buf[NIOS_PKT_16x64_IDX_DATA + 6] = (data >> 48) & 0xff;
+ buf[NIOS_PKT_16x64_IDX_DATA + 7] = (data >> 56) & 0xff;
+
+ buf[NIOS_PKT_16x64_IDX_RESV2 + 0] = 0x00;
+ buf[NIOS_PKT_16x64_IDX_RESV2 + 1] = 0x00;
+}
+
+/* Unpack the request buffer */
+static inline void nios_pkt_16x64_unpack(const uint8_t *buf, uint8_t *target,
+ bool *write, uint16_t *addr,
+ uint64_t *data)
+{
+ if (target != NULL) {
+ *target = buf[NIOS_PKT_16x64_IDX_TARGET_ID];
+ }
+
+ if (write != NULL) {
+ *write = (buf[NIOS_PKT_16x64_IDX_FLAGS] & NIOS_PKT_16x64_FLAG_WRITE) != 0;
+ }
+
+ if (addr != NULL) {
+ *addr = (buf[NIOS_PKT_16x64_IDX_ADDR + 0] << 0) |
+ (buf[NIOS_PKT_16x64_IDX_ADDR + 1] << 8);
+ }
+
+ if (data != NULL) {
+ *data = ((uint64_t) buf[NIOS_PKT_16x64_IDX_DATA + 0] << 0) |
+ ((uint64_t) buf[NIOS_PKT_16x64_IDX_DATA + 1] << 8) |
+ ((uint64_t) buf[NIOS_PKT_16x64_IDX_DATA + 2] << 16) |
+ ((uint64_t) buf[NIOS_PKT_16x64_IDX_DATA + 3] << 24) |
+ ((uint64_t) buf[NIOS_PKT_16x64_IDX_DATA + 4] << 32) |
+ ((uint64_t) buf[NIOS_PKT_16x64_IDX_DATA + 5] << 40) |
+ ((uint64_t) buf[NIOS_PKT_16x64_IDX_DATA + 6] << 48) |
+ ((uint64_t) buf[NIOS_PKT_16x64_IDX_DATA + 7] << 56);
+ }
+}
+
+/* Pack the response buffer */
+static inline void nios_pkt_16x64_resp_pack(uint8_t *buf, uint8_t target,
+ bool write, uint16_t addr,
+ uint64_t data, bool success)
+{
+ nios_pkt_16x64_pack(buf, target, write, addr, data);
+
+ if (success) {
+ buf[NIOS_PKT_16x64_IDX_FLAGS] |= NIOS_PKT_16x64_FLAG_SUCCESS;
+ }
+}
+
+/* Unpack the response buffer */
+static inline void nios_pkt_16x64_resp_unpack(const uint8_t *buf,
+ uint8_t *target, bool *write,
+ uint16_t *addr, uint64_t *data,
+ bool *success)
+{
+ nios_pkt_16x64_unpack(buf, target, write, addr, data);
+
+ if ((buf[NIOS_PKT_16x64_IDX_FLAGS] & NIOS_PKT_16x64_FLAG_SUCCESS) != 0) {
+ *success = true;
+ } else {
+ *success = false;
+ }
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/include/nios_pkt_32x32.h b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_32x32.h
new file mode 100644
index 0000000..e77cdee
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_32x32.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLADERF_NIOS_PKT_32x32_H_
+#define BLADERF_NIOS_PKT_32x32_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+/*
+ * This file defines the Host <-> FPGA (NIOS II) packet formats for accesses
+ * to devices/blocks with 32-bit addresses and 32-bit data
+ *
+ *
+ * Request
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | Target ID (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 2 | Flags (Note 2) |
+ * +----------------+---------------------------------------------------------+
+ * | 3 | Reserved. Set to 0x00. |
+ * +----------------+---------------------------------------------------------+
+ * | 7:4 | 32-bit address |
+ * +----------------+---------------------------------------------------------+
+ * | 11:8 | 32-bit data, little-endian |
+ * +----------------+---------------------------------------------------------+
+ * | 15:12 | Reserved. Set to 0. |
+ * +----------------+---------------------------------------------------------+
+ *
+ *
+ * Response
+ * ----------------------
+ *
+ * The response packet contains the same information as the request.
+ * A status flag will be set if the operation completed successfully.
+ *
+ * In the case of a read request, the data field will contain the read data, if
+ * the read succeeded.
+ *
+ * (Note 1)
+ * The "Target ID" refers to the peripheral, device, or block to access.
+ * See the NIOS_PKT_32x32_TARGET_* values.
+ *
+ * (Note 2)
+ * The flags are defined as follows:
+ *
+ * +================+========================+
+ * | Bit(s) | Value |
+ * +================+========================+
+ * | 7:2 | Reserved. Set to 0. |
+ * +----------------+------------------------+
+ * | | Status. Only used in |
+ * | | response packet. |
+ * | | Ignored in request. |
+ * | 1 | |
+ * | | 1 = Success |
+ * | | 0 = Failure |
+ * +----------------+------------------------+
+ * | 0 | 0 = Read operation |
+ * | | 1 = Write operation |
+ * +----------------+------------------------+
+ *
+ */
+
+#define NIOS_PKT_32x32_MAGIC ((uint8_t) 'K')
+
+/* Request packet indices */
+#define NIOS_PKT_32x32_IDX_MAGIC 0
+#define NIOS_PKT_32x32_IDX_TARGET_ID 1
+#define NIOS_PKT_32x32_IDX_FLAGS 2
+#define NIOS_PKT_32x32_IDX_RESV1 3
+#define NIOS_PKT_32x32_IDX_ADDR 4
+#define NIOS_PKT_32x32_IDX_DATA 8
+#define NIOS_PKT_32x32_IDX_RESV2 12
+
+/* Target IDs */
+
+/* For the EXP and EXP_DIR targets, the address is a bitmask of values
+ * to read/write */
+#define NIOS_PKT_32x32_TARGET_EXP 0x00 /* Expansion I/O */
+#define NIOS_PKT_32x32_TARGET_EXP_DIR 0x01 /* Expansion I/O Direction reg */
+#define NIOS_PKT_32x32_TARGET_ADI_AXI 0x02 /* ADI AXI Interface */
+#define NIOS_PKT_32x32_TARGET_WB_MSTR 0x03 /* Wishbone Master */
+
+/* IDs 0x80 through 0xff will not be assigned by Nuand. These are reserved
+ * for user customizations */
+#define NIOS_PKT_32x32_TARGET_USR1 0x80
+#define NIOS_PKT_32x32_TARGET_USR128 0xff
+
+/* Flag bits */
+#define NIOS_PKT_32x32_FLAG_WRITE (1 << 0)
+#define NIOS_PKT_32x32_FLAG_SUCCESS (1 << 1)
+
+
+/* Pack the request buffer */
+static inline void nios_pkt_32x32_pack(uint8_t *buf, uint8_t target, bool write,
+ uint32_t addr, uint32_t data)
+{
+ buf[NIOS_PKT_32x32_IDX_MAGIC] = NIOS_PKT_32x32_MAGIC;
+ buf[NIOS_PKT_32x32_IDX_TARGET_ID] = target;
+
+ if (write) {
+ buf[NIOS_PKT_32x32_IDX_FLAGS] = NIOS_PKT_32x32_FLAG_WRITE;
+ } else {
+ buf[NIOS_PKT_32x32_IDX_FLAGS] = 0x00;
+ }
+
+ buf[NIOS_PKT_32x32_IDX_RESV1] = 0x00;
+
+ buf[NIOS_PKT_32x32_IDX_ADDR + 0] = (addr >> 0);
+ buf[NIOS_PKT_32x32_IDX_ADDR + 1] = (addr >> 8);
+ buf[NIOS_PKT_32x32_IDX_ADDR + 2] = (addr >> 16);
+ buf[NIOS_PKT_32x32_IDX_ADDR + 3] = (addr >> 24);
+
+ buf[NIOS_PKT_32x32_IDX_DATA + 0] = (data >> 0);
+ buf[NIOS_PKT_32x32_IDX_DATA + 1] = (data >> 8);
+ buf[NIOS_PKT_32x32_IDX_DATA + 2] = (data >> 16);
+ buf[NIOS_PKT_32x32_IDX_DATA + 3] = (data >> 24);
+
+ buf[NIOS_PKT_32x32_IDX_RESV2 + 0] = 0x00;
+ buf[NIOS_PKT_32x32_IDX_RESV2 + 1] = 0x00;
+ buf[NIOS_PKT_32x32_IDX_RESV2 + 2] = 0x00;
+ buf[NIOS_PKT_32x32_IDX_RESV2 + 3] = 0x00;
+}
+
+/* Unpack the request buffer */
+static inline void nios_pkt_32x32_unpack(const uint8_t *buf, uint8_t *target,
+ bool *write, uint32_t *addr,
+ uint32_t *data)
+{
+ if (target != NULL) {
+ *target = buf[NIOS_PKT_32x32_IDX_TARGET_ID];
+ }
+
+ if (write != NULL) {
+ *write = (buf[NIOS_PKT_32x32_IDX_FLAGS] & NIOS_PKT_32x32_FLAG_WRITE) != 0;
+ }
+
+ if (addr != NULL) {
+ *addr = (buf[NIOS_PKT_32x32_IDX_ADDR + 0] << 0) |
+ (buf[NIOS_PKT_32x32_IDX_ADDR + 1] << 8) |
+ (buf[NIOS_PKT_32x32_IDX_ADDR + 2] << 16) |
+ (buf[NIOS_PKT_32x32_IDX_ADDR + 3] << 24);
+ }
+
+
+ if (data != NULL) {
+ *data = (buf[NIOS_PKT_32x32_IDX_DATA + 0] << 0) |
+ (buf[NIOS_PKT_32x32_IDX_DATA + 1] << 8) |
+ (buf[NIOS_PKT_32x32_IDX_DATA + 2] << 16) |
+ (buf[NIOS_PKT_32x32_IDX_DATA + 3] << 24);
+ }
+}
+
+/* Pack the response buffer */
+static inline void nios_pkt_32x32_resp_pack(uint8_t *buf, uint8_t target,
+ bool write, uint32_t addr,
+ uint32_t data, bool success)
+{
+ nios_pkt_32x32_pack(buf, target, write, addr, data);
+
+ if (success) {
+ buf[NIOS_PKT_32x32_IDX_FLAGS] |= NIOS_PKT_32x32_FLAG_SUCCESS;
+ }
+}
+
+/* Unpack the response buffer */
+static inline void nios_pkt_32x32_resp_unpack(const uint8_t *buf,
+ uint8_t *target, bool *write,
+ uint32_t *addr, uint32_t *data,
+ bool *success)
+{
+ nios_pkt_32x32_unpack(buf, target, write, addr, data);
+
+ if ((buf[NIOS_PKT_32x32_IDX_FLAGS] & NIOS_PKT_32x32_FLAG_SUCCESS) != 0) {
+ *success = true;
+ } else {
+ *success = false;
+ }
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x16.h b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x16.h
new file mode 100644
index 0000000..9ed1d45
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x16.h
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLADERF_NIOS_PKT_8x16_H_
+#define BLADERF_NIOS_PKT_8x16_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+/*
+ * This file defines the Host <-> FPGA (NIOS II) packet formats for accesses
+ * to devices/blocks with 8-bit addresses and 16-bit data
+ *
+ *
+ * Request
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | Target ID (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 2 | Flags (Note 2) |
+ * +----------------+---------------------------------------------------------+
+ * | 3 | Reserved. Set to 0x00. |
+ * +----------------+---------------------------------------------------------+
+ * | 4 | 8-bit address |
+ * +----------------+---------------------------------------------------------+
+ * | 5:6 | 16-bit data, little-endian |
+ * +----------------+---------------------------------------------------------+
+ * | 7-15 | Reserved. Set to 0. |
+ * +----------------+---------------------------------------------------------+
+ *
+ *
+ * Response
+ * ----------------------
+ *
+ * The response packet contains the same information as the request.
+ * A status flag will be set if the operation completed successfully.
+ *
+ * In the case of a read request, the data field will contain the read data, if
+ * the read succeeded.
+ *
+ * (Note 1)
+ * The "Target ID" refers to the peripheral, device, or block to access.
+ * See the NIOS_PKT_8x16_TARGET_* values.
+ *
+ * (Note 2)
+ * The flags are defined as follows:
+ *
+ * +================+========================+
+ * | Bit(s) | Value |
+ * +================+========================+
+ * | 7:2 | Reserved. Set to 0. |
+ * +----------------+------------------------+
+ * | | Status. Only used in |
+ * | | response packet. |
+ * | | Ignored in request. |
+ * | 1 | |
+ * | | 1 = Success |
+ * | | 0 = Failure |
+ * +----------------+------------------------+
+ * | 0 | 0 = Read operation |
+ * | | 1 = Write operation |
+ * +----------------+------------------------+
+ *
+ */
+
+#define NIOS_PKT_8x16_MAGIC ((uint8_t) 'B')
+
+/* Request packet indices */
+#define NIOS_PKT_8x16_IDX_MAGIC 0
+#define NIOS_PKT_8x16_IDX_TARGET_ID 1
+#define NIOS_PKT_8x16_IDX_FLAGS 2
+#define NIOS_PKT_8x16_IDX_RESV1 3
+#define NIOS_PKT_8x16_IDX_ADDR 4
+#define NIOS_PKT_8x16_IDX_DATA 5
+#define NIOS_PKT_8x16_IDX_RESV2 7
+
+/* Target IDs */
+#define NIOS_PKT_8x16_TARGET_VCTCXO_DAC 0x00
+#define NIOS_PKT_8x16_TARGET_IQ_CORR 0x01
+#define NIOS_PKT_8x16_TARGET_AGC_CORR 0x02
+#define NIOS_PKT_8x16_TARGET_AD56X1_DAC 0x03
+#define NIOS_PKT_8x16_TARGET_INA219 0x04
+
+/* IDs 0x80 through 0xff will not be assigned by Nuand. These are reserved
+ * for user customizations */
+#define NIOS_PKT_8x16_TARGET_USR1 0x80
+#define NIOS_PKT_8x16_TARGET_USR128 0xff
+
+/* Flag bits */
+#define NIOS_PKT_8x16_FLAG_WRITE (1 << 0)
+#define NIOS_PKT_8x16_FLAG_SUCCESS (1 << 1)
+
+/* Sub-addresses for the IQ Correction target block */
+#define NIOS_PKT_8x16_ADDR_IQ_CORR_RX_GAIN 0x00
+#define NIOS_PKT_8x16_ADDR_IQ_CORR_RX_PHASE 0x01
+#define NIOS_PKT_8x16_ADDR_IQ_CORR_TX_GAIN 0x02
+#define NIOS_PKT_8x16_ADDR_IQ_CORR_TX_PHASE 0x03
+
+/* Sub-addresses for the AGC DC Correction target block */
+#define NIOS_PKT_8x16_ADDR_AGC_DC_Q_MAX 0x00
+#define NIOS_PKT_8x16_ADDR_AGC_DC_I_MAX 0x01
+#define NIOS_PKT_8x16_ADDR_AGC_DC_Q_MID 0x02
+#define NIOS_PKT_8x16_ADDR_AGC_DC_I_MID 0x03
+#define NIOS_PKT_8x16_ADDR_AGC_DC_Q_MIN 0x04
+#define NIOS_PKT_8x16_ADDR_AGC_DC_I_MIN 0x05
+
+/* Pack the request buffer */
+static inline void nios_pkt_8x16_pack(uint8_t *buf, uint8_t target, bool write,
+ uint8_t addr, uint16_t data)
+{
+ buf[NIOS_PKT_8x16_IDX_MAGIC] = NIOS_PKT_8x16_MAGIC;
+ buf[NIOS_PKT_8x16_IDX_TARGET_ID] = target;
+
+ if (write) {
+ buf[NIOS_PKT_8x16_IDX_FLAGS] = NIOS_PKT_8x16_FLAG_WRITE;
+ } else {
+ buf[NIOS_PKT_8x16_IDX_FLAGS] = 0x00;
+ }
+
+ buf[NIOS_PKT_8x16_IDX_RESV1] = 0x00;
+
+ buf[NIOS_PKT_8x16_IDX_ADDR] = addr;
+
+ buf[NIOS_PKT_8x16_IDX_DATA] = data & 0xff;
+ buf[NIOS_PKT_8x16_IDX_DATA + 1] = (data >> 8);
+
+ buf[NIOS_PKT_8x16_IDX_RESV2 + 0] = 0x00;
+ buf[NIOS_PKT_8x16_IDX_RESV2 + 1] = 0x00;
+ buf[NIOS_PKT_8x16_IDX_RESV2 + 2] = 0x00;
+ buf[NIOS_PKT_8x16_IDX_RESV2 + 3] = 0x00;
+ buf[NIOS_PKT_8x16_IDX_RESV2 + 4] = 0x00;
+ buf[NIOS_PKT_8x16_IDX_RESV2 + 5] = 0x00;
+ buf[NIOS_PKT_8x16_IDX_RESV2 + 6] = 0x00;
+ buf[NIOS_PKT_8x16_IDX_RESV2 + 7] = 0x00;
+ buf[NIOS_PKT_8x16_IDX_RESV2 + 8] = 0x00;
+}
+
+/* Unpack the request buffer */
+static inline void nios_pkt_8x16_unpack(const uint8_t *buf, uint8_t *target,
+ bool *write, uint8_t *addr,
+ uint16_t *data)
+{
+ if (target != NULL) {
+ *target = buf[NIOS_PKT_8x16_IDX_TARGET_ID];
+ }
+
+ if (write != NULL) {
+ *write = (buf[NIOS_PKT_8x16_IDX_FLAGS] & NIOS_PKT_8x16_FLAG_WRITE) != 0;
+ }
+
+ if (addr != NULL) {
+ *addr = buf[NIOS_PKT_8x16_IDX_ADDR];
+ }
+
+ if (data != NULL) {
+ *data = (buf[NIOS_PKT_8x16_IDX_DATA + 0] << 0) |
+ (buf[NIOS_PKT_8x16_IDX_DATA + 1] << 8);
+ }
+}
+
+/* Pack the response buffer */
+static inline void nios_pkt_8x16_resp_pack(uint8_t *buf, uint8_t target,
+ bool write, uint8_t addr,
+ uint16_t data, bool success)
+{
+ nios_pkt_8x16_pack(buf, target, write, addr, data);
+
+ if (success) {
+ buf[NIOS_PKT_8x16_IDX_FLAGS] |= NIOS_PKT_8x16_FLAG_SUCCESS;
+ }
+}
+
+/* Unpack the response buffer */
+static inline void nios_pkt_8x16_resp_unpack(const uint8_t *buf,
+ uint8_t *target, bool *write,
+ uint8_t *addr, uint16_t *data,
+ bool *success)
+{
+ nios_pkt_8x16_unpack(buf, target, write, addr, data);
+
+ if ((buf[NIOS_PKT_8x16_IDX_FLAGS] & NIOS_PKT_8x16_FLAG_SUCCESS) != 0) {
+ *success = true;
+ } else {
+ *success = false;
+ }
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x32.h b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x32.h
new file mode 100644
index 0000000..27e6373
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x32.h
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLADERF_NIOS_PKT_8x32_H_
+#define BLADERF_NIOS_PKT_8x32_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+/*
+ * This file defines the Host <-> FPGA (NIOS II) packet formats for accesses
+ * to devices/blocks with 8-bit addresses and 32-bit data
+ *
+ *
+ * Request
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | Target ID (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 2 | Flags (Note 2) |
+ * +----------------+---------------------------------------------------------+
+ * | 3 | Reserved. Set to 0x00. |
+ * +----------------+---------------------------------------------------------+
+ * | 4 | 8-bit address |
+ * +----------------+---------------------------------------------------------+
+ * | 8:5 | 32-bit data, little-endian |
+ * +----------------+---------------------------------------------------------+
+ * | 15:9 | Reserved. Set to 0. |
+ * +----------------+---------------------------------------------------------+
+ *
+ *
+ * Response
+ * ----------------------
+ *
+ * The response packet contains the same information as the request.
+ * A status flag will be set if the operation completed successfully.
+ *
+ * In the case of a read request, the data field will contain the read data, if
+ * the read succeeded.
+ *
+ * (Note 1)
+ * The "Target ID" refers to the peripheral, device, or block to access.
+ * See the NIOS_PKT_8x32_TARGET_* values.
+ *
+ * (Note 2)
+ * The flags are defined as follows:
+ *
+ * +================+========================+
+ * | Bit(s) | Value |
+ * +================+========================+
+ * | 7:2 | Reserved. Set to 0. |
+ * +----------------+------------------------+
+ * | | Status. Only used in |
+ * | | response packet. |
+ * | | Ignored in request. |
+ * | 1 | |
+ * | | 1 = Success |
+ * | | 0 = Failure |
+ * +----------------+------------------------+
+ * | 0 | 0 = Read operation |
+ * | | 1 = Write operation |
+ * +----------------+------------------------+
+ *
+ */
+
+#define NIOS_PKT_8x32_MAGIC ((uint8_t) 'C')
+
+/* Request packet indices */
+#define NIOS_PKT_8x32_IDX_MAGIC 0
+#define NIOS_PKT_8x32_IDX_TARGET_ID 1
+#define NIOS_PKT_8x32_IDX_FLAGS 2
+#define NIOS_PKT_8x32_IDX_RESV1 3
+#define NIOS_PKT_8x32_IDX_ADDR 4
+#define NIOS_PKT_8x32_IDX_DATA 5
+#define NIOS_PKT_8x32_IDX_RESV2 9
+
+/* Target IDs */
+#define NIOS_PKT_8x32_TARGET_VERSION 0x00 /* FPGA version (read only) */
+#define NIOS_PKT_8x32_TARGET_CONTROL 0x01 /* FPGA control/config register */
+#define NIOS_PKT_8x32_TARGET_ADF4351 0x02 /* XB-200 ADF4351 register access
+ * (write-only) */
+#define NIOS_PKT_8x32_TARGET_RFFE_CSR 0x03 /* RFFE control & status GPIO */
+#define NIOS_PKT_8x32_TARGET_ADF400X 0x04 /* ADF400x config */
+#define NIOS_PKT_8x32_TARGET_FASTLOCK 0x05 /* Save AD9361 fast lock profile
+ * to Nios */
+
+/* IDs 0x80 through 0xff will not be assigned by Nuand. These are reserved
+ * for user customizations */
+#define NIOS_PKT_8x32_TARGET_USR1 0x80
+#define NIOS_PKT_8x32_TARGET_USR128 0xff
+
+/* Flag bits */
+#define NIOS_PKT_8x32_FLAG_WRITE (1 << 0)
+#define NIOS_PKT_8x32_FLAG_SUCCESS (1 << 1)
+
+/* Function to convert target ID to string */
+static inline const char* target2str(uint8_t target_id) {
+ switch (target_id) {
+ case NIOS_PKT_8x32_TARGET_VERSION:
+ return "FPGA Version";
+ case NIOS_PKT_8x32_TARGET_CONTROL:
+ return "FPGA Control/Config Register";
+ case NIOS_PKT_8x32_TARGET_ADF4351:
+ return "XB-200 ADF4351 Register (Write-Only)";
+ case NIOS_PKT_8x32_TARGET_RFFE_CSR:
+ return "RFFE Control & Status GPIO";
+ case NIOS_PKT_8x32_TARGET_ADF400X:
+ return "ADF400x Config";
+ case NIOS_PKT_8x32_TARGET_FASTLOCK:
+ return "AD9361 Fast Lock Profile";
+
+ /* Reserved for user customizations */
+ case NIOS_PKT_8x32_TARGET_USR1:
+ return "User Defined 1";
+ case NIOS_PKT_8x32_TARGET_USR128:
+ return "User Defined 128";
+
+ default:
+ return "Unknown Target ID";
+ }
+}
+
+/* Pack the request buffer */
+static inline void nios_pkt_8x32_pack(uint8_t *buf, uint8_t target, bool write,
+ uint8_t addr, uint32_t data)
+{
+ buf[NIOS_PKT_8x32_IDX_MAGIC] = NIOS_PKT_8x32_MAGIC;
+ buf[NIOS_PKT_8x32_IDX_TARGET_ID] = target;
+
+ if (write) {
+ buf[NIOS_PKT_8x32_IDX_FLAGS] = NIOS_PKT_8x32_FLAG_WRITE;
+ } else {
+ buf[NIOS_PKT_8x32_IDX_FLAGS] = 0x00;
+ }
+
+ buf[NIOS_PKT_8x32_IDX_RESV1] = 0x00;
+
+ buf[NIOS_PKT_8x32_IDX_ADDR] = addr;
+
+ buf[NIOS_PKT_8x32_IDX_DATA + 0] = data & 0xff;
+ buf[NIOS_PKT_8x32_IDX_DATA + 1] = (data >> 8);
+ buf[NIOS_PKT_8x32_IDX_DATA + 2] = (data >> 16);
+ buf[NIOS_PKT_8x32_IDX_DATA + 3] = (data >> 24);
+
+ buf[NIOS_PKT_8x32_IDX_RESV2 + 0] = 0x00;
+ buf[NIOS_PKT_8x32_IDX_RESV2 + 1] = 0x00;
+ buf[NIOS_PKT_8x32_IDX_RESV2 + 2] = 0x00;
+ buf[NIOS_PKT_8x32_IDX_RESV2 + 3] = 0x00;
+ buf[NIOS_PKT_8x32_IDX_RESV2 + 4] = 0x00;
+ buf[NIOS_PKT_8x32_IDX_RESV2 + 5] = 0x00;
+ buf[NIOS_PKT_8x32_IDX_RESV2 + 6] = 0x00;
+}
+
+/* Unpack the request buffer */
+static inline void nios_pkt_8x32_unpack(const uint8_t *buf, uint8_t *target,
+ bool *write, uint8_t *addr,
+ uint32_t *data)
+{
+ if (target != NULL) {
+ *target = buf[NIOS_PKT_8x32_IDX_TARGET_ID];
+ }
+
+ if (write != NULL) {
+ *write = (buf[NIOS_PKT_8x32_IDX_FLAGS] & NIOS_PKT_8x32_FLAG_WRITE) != 0;
+ }
+
+ if (addr != NULL) {
+ *addr = buf[NIOS_PKT_8x32_IDX_ADDR];
+ }
+
+ if (data != NULL) {
+ *data = (buf[NIOS_PKT_8x32_IDX_DATA + 0] << 0) |
+ (buf[NIOS_PKT_8x32_IDX_DATA + 1] << 8) |
+ (buf[NIOS_PKT_8x32_IDX_DATA + 2] << 16) |
+ (buf[NIOS_PKT_8x32_IDX_DATA + 3] << 24);
+ }
+}
+
+/* Pack the response buffer */
+static inline void nios_pkt_8x32_resp_pack(uint8_t *buf, uint8_t target,
+ bool write, uint8_t addr,
+ uint32_t data, bool success)
+{
+ nios_pkt_8x32_pack(buf, target, write, addr, data);
+
+ if (success) {
+ buf[NIOS_PKT_8x32_IDX_FLAGS] |= NIOS_PKT_8x32_FLAG_SUCCESS;
+ }
+}
+
+/* Unpack the response buffer */
+static inline void nios_pkt_8x32_resp_unpack(const uint8_t *buf,
+ uint8_t *target, bool *write,
+ uint8_t *addr, uint32_t *data,
+ bool *success)
+{
+ nios_pkt_8x32_unpack(buf, target, write, addr, data);
+
+ if ((buf[NIOS_PKT_8x32_IDX_FLAGS] & NIOS_PKT_8x32_FLAG_SUCCESS) != 0) {
+ *success = true;
+ } else {
+ *success = false;
+ }
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x64.h b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x64.h
new file mode 100644
index 0000000..420307c
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x64.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLADERF_NIOS_PKT_8x64_H_
+#define BLADERF_NIOS_PKT_8x64_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+/*
+ * This file defines the Host <-> FPGA (NIOS II) packet formats for accesses
+ * to devices/blocks with 8-bit addresses and 64-bit data
+ *
+ *
+ * Request
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | Target ID (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 2 | Flags (Note 2) |
+ * +----------------+---------------------------------------------------------+
+ * | 3 | Reserved. Set to 0x00. |
+ * +----------------+---------------------------------------------------------+
+ * | 4 | 8-bit address |
+ * +----------------+---------------------------------------------------------+
+ * | 12:5 | 64-bit data, little-endian |
+ * +----------------+---------------------------------------------------------+
+ * | 15:13 | Reserved. Set to 0. |
+ * +----------------+---------------------------------------------------------+
+ *
+ *
+ * Response
+ * ----------------------
+ *
+ * The response packet contains the same information as the request.
+ * A status flag will be set if the operation completed successfully.
+ *
+ * In the case of a read request, the data field will contain the read data, if
+ * the read succeeded.
+ *
+ * (Note 1)
+ * The "Target ID" refers to the peripheral, device, or block to access.
+ * See the NIOS_PKT_8x64_TARGET_* values.
+ *
+ * (Note 2)
+ * The flags are defined as follows:
+ *
+ * +================+========================+
+ * | Bit(s) | Value |
+ * +================+========================+
+ * | 7:2 | Reserved. Set to 0. |
+ * +----------------+------------------------+
+ * | | Status. Only used in |
+ * | | response packet. |
+ * | | Ignored in request. |
+ * | 1 | |
+ * | | 1 = Success |
+ * | | 0 = Failure |
+ * +----------------+------------------------+
+ * | 0 | 0 = Read operation |
+ * | | 1 = Write operation |
+ * +----------------+------------------------+
+ *
+ */
+
+#define NIOS_PKT_8x64_MAGIC ((uint8_t) 'D')
+
+/* Request packet indices */
+#define NIOS_PKT_8x64_IDX_MAGIC 0
+#define NIOS_PKT_8x64_IDX_TARGET_ID 1
+#define NIOS_PKT_8x64_IDX_FLAGS 2
+#define NIOS_PKT_8x64_IDX_RESV1 3
+#define NIOS_PKT_8x64_IDX_ADDR 4
+#define NIOS_PKT_8x64_IDX_DATA 5
+#define NIOS_PKT_8x64_IDX_RESV2 13
+
+/* Target IDs */
+
+#define NIOS_PKT_8x64_TARGET_TIMESTAMP 0x00 /* Timestamp readback (read only) */
+
+/* IDs 0x80 through 0xff will not be assigned by Nuand. These are reserved
+ * for user customizations */
+#define NIOS_PKT_8x64_TARGET_USR1 0x80
+#define NIOS_PKT_8x64_TARGET_USR128 0xff
+
+/* Flag bits */
+#define NIOS_PKT_8x64_FLAG_WRITE (1 << 0)
+#define NIOS_PKT_8x64_FLAG_SUCCESS (1 << 1)
+
+/* Sub-addresses for timestamp target */
+#define NIOS_PKT_8x64_TIMESTAMP_RX 0x00
+#define NIOS_PKT_8x64_TIMESTAMP_TX 0x01
+
+/* Pack the request buffer */
+static inline void nios_pkt_8x64_pack(uint8_t *buf, uint8_t target, bool write,
+ uint8_t addr, uint64_t data)
+{
+ buf[NIOS_PKT_8x64_IDX_MAGIC] = NIOS_PKT_8x64_MAGIC;
+ buf[NIOS_PKT_8x64_IDX_TARGET_ID] = target;
+
+ if (write) {
+ buf[NIOS_PKT_8x64_IDX_FLAGS] = NIOS_PKT_8x64_FLAG_WRITE;
+ } else {
+ buf[NIOS_PKT_8x64_IDX_FLAGS] = 0x00;
+ }
+
+ buf[NIOS_PKT_8x64_IDX_RESV1] = 0x00;
+
+ buf[NIOS_PKT_8x64_IDX_ADDR] = addr;
+
+ buf[NIOS_PKT_8x64_IDX_DATA + 0] = (data >> 0) & 0xff;
+ buf[NIOS_PKT_8x64_IDX_DATA + 1] = (data >> 8) & 0xff;
+ buf[NIOS_PKT_8x64_IDX_DATA + 2] = (data >> 16) & 0xff;
+ buf[NIOS_PKT_8x64_IDX_DATA + 3] = (data >> 24) & 0xff;
+ buf[NIOS_PKT_8x64_IDX_DATA + 4] = (data >> 32) & 0xff;
+ buf[NIOS_PKT_8x64_IDX_DATA + 5] = (data >> 40) & 0xff;
+ buf[NIOS_PKT_8x64_IDX_DATA + 6] = (data >> 48) & 0xff;
+ buf[NIOS_PKT_8x64_IDX_DATA + 7] = (data >> 56) & 0xff;
+
+ buf[NIOS_PKT_8x64_IDX_RESV2 + 0] = 0x00;
+ buf[NIOS_PKT_8x64_IDX_RESV2 + 1] = 0x00;
+ buf[NIOS_PKT_8x64_IDX_RESV2 + 2] = 0x00;
+}
+
+/* Unpack the request buffer */
+static inline void nios_pkt_8x64_unpack(const uint8_t *buf, uint8_t *target,
+ bool *write, uint8_t *addr,
+ uint64_t *data)
+{
+ if (target != NULL) {
+ *target = buf[NIOS_PKT_8x64_IDX_TARGET_ID];
+ }
+
+ if (write != NULL) {
+ *write = (buf[NIOS_PKT_8x64_IDX_FLAGS] & NIOS_PKT_8x64_FLAG_WRITE) != 0;
+ }
+
+ if (addr != NULL) {
+ *addr = buf[NIOS_PKT_8x64_IDX_ADDR];
+ }
+
+ if (data != NULL) {
+ *data = ((uint64_t) buf[NIOS_PKT_8x64_IDX_DATA + 0] << 0) |
+ ((uint64_t) buf[NIOS_PKT_8x64_IDX_DATA + 1] << 8) |
+ ((uint64_t) buf[NIOS_PKT_8x64_IDX_DATA + 2] << 16) |
+ ((uint64_t) buf[NIOS_PKT_8x64_IDX_DATA + 3] << 24) |
+ ((uint64_t) buf[NIOS_PKT_8x64_IDX_DATA + 4] << 32) |
+ ((uint64_t) buf[NIOS_PKT_8x64_IDX_DATA + 5] << 40) |
+ ((uint64_t) buf[NIOS_PKT_8x64_IDX_DATA + 6] << 48) |
+ ((uint64_t) buf[NIOS_PKT_8x64_IDX_DATA + 7] << 56);
+ }
+}
+
+/* Pack the response buffer */
+static inline void nios_pkt_8x64_resp_pack(uint8_t *buf, uint8_t target,
+ bool write, uint8_t addr,
+ uint64_t data, bool success)
+{
+ nios_pkt_8x64_pack(buf, target, write, addr, data);
+
+ if (success) {
+ buf[NIOS_PKT_8x64_IDX_FLAGS] |= NIOS_PKT_8x64_FLAG_SUCCESS;
+ }
+}
+
+/* Unpack the response buffer */
+static inline void nios_pkt_8x64_resp_unpack(const uint8_t *buf,
+ uint8_t *target, bool *write,
+ uint8_t *addr, uint64_t *data,
+ bool *success)
+{
+ nios_pkt_8x64_unpack(buf, target, write, addr, data);
+
+ if ((buf[NIOS_PKT_8x64_IDX_FLAGS] & NIOS_PKT_8x64_FLAG_SUCCESS) != 0) {
+ *success = true;
+ } else {
+ *success = false;
+ }
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x8.h b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x8.h
new file mode 100644
index 0000000..dd8072c
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x8.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLADERF_NIOS_PKT_8x8_H_
+#define BLADERF_NIOS_PKT_8x8_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+/*
+ * This file defines the Host <-> FPGA (NIOS II) packet formats for accesses
+ * to devices/blocks with 8-bit addresses and 8-bit data
+ *
+ *
+ * Request
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | Target ID (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 2 | Flags (Note 2) |
+ * +----------------+---------------------------------------------------------+
+ * | 3 | Reserved. Set to 0x00. |
+ * +----------------+---------------------------------------------------------+
+ * | 4 | 8-bit address |
+ * +----------------+---------------------------------------------------------+
+ * | 5 | 8-bit data |
+ * +----------------+---------------------------------------------------------+
+ * | 15:6 | Reserved. Set to 0. |
+ * +----------------+---------------------------------------------------------+
+ *
+ *
+ * Response
+ * ----------------------
+ *
+ * The response packet contains the same information as the request.
+ * A status flag will be set if the operation completed successfully.
+ *
+ * In the case of a read request, the data field will contain the read data, if
+ * the read succeeded.
+ *
+ * (Note 1)
+ * The "Target ID" refers to the peripheral, device, or block to access.
+ * See the NIOS_PKT_8x8_TARGET_* values.
+ *
+ * (Note 2)
+ * The flags are defined as follows:
+ *
+ * +================+========================+
+ * | Bit(s) | Value |
+ * +================+========================+
+ * | 7:2 | Reserved. Set to 0. |
+ * +----------------+------------------------+
+ * | | Status. Only used in |
+ * | | response packet. |
+ * | | Ignored in request. |
+ * | 1 | |
+ * | | 1 = Success |
+ * | | 0 = Failure |
+ * +----------------+------------------------+
+ * | 0 | 0 = Read operation |
+ * | | 1 = Write operation |
+ * +----------------+------------------------+
+ *
+ */
+
+#define NIOS_PKT_8x8_MAGIC ((uint8_t) 'A')
+
+/* Request packet indices */
+#define NIOS_PKT_8x8_IDX_MAGIC 0
+#define NIOS_PKT_8x8_IDX_TARGET_ID 1
+#define NIOS_PKT_8x8_IDX_FLAGS 2
+#define NIOS_PKT_8x8_IDX_RESV1 3
+#define NIOS_PKT_8x8_IDX_ADDR 4
+#define NIOS_PKT_8x8_IDX_DATA 5
+#define NIOS_PKT_8x8_IDX_RESV2 6
+
+/* Target IDs */
+#define NIOS_PKT_8x8_TARGET_LMS6 0x00 /* LMS6002D register access */
+#define NIOS_PKT_8x8_TARGET_SI5338 0x01 /* Si5338 register access */
+#define NIOS_PKT_8x8_TARGET_VCTCXO_TAMER 0x02 /* VCTCXO Tamer control */
+#define NIOS_PKT_8x8_TX_TRIGGER_CTL 0x03 /* TX trigger control */
+#define NIOS_PKT_8x8_RX_TRIGGER_CTL 0x04 /* RX trigger control */
+
+/* IDs 0x80 through 0xff will not be assigned by Nuand. These are reserved
+ * for user customizations */
+#define NIOS_PKT_8x8_TARGET_USR1 0x80
+#define NIOS_PKT_8x8_TARGET_USR128 0xff
+
+/* Flag bits */
+#define NIOS_PKT_8x8_FLAG_WRITE (1 << 0)
+#define NIOS_PKT_8x8_FLAG_SUCCESS (1 << 1)
+
+
+/* Pack the request buffer */
+static inline void nios_pkt_8x8_pack(uint8_t *buf, uint8_t target, bool write,
+ uint8_t addr, uint8_t data)
+{
+ buf[NIOS_PKT_8x8_IDX_MAGIC] = NIOS_PKT_8x8_MAGIC;
+ buf[NIOS_PKT_8x8_IDX_TARGET_ID] = target;
+
+ if (write) {
+ buf[NIOS_PKT_8x8_IDX_FLAGS] = NIOS_PKT_8x8_FLAG_WRITE;
+ } else {
+ buf[NIOS_PKT_8x8_IDX_FLAGS] = 0x00;
+ }
+
+ buf[NIOS_PKT_8x8_IDX_RESV1] = 0x00;
+
+ buf[NIOS_PKT_8x8_IDX_ADDR] = addr;
+ buf[NIOS_PKT_8x8_IDX_DATA] = data;
+
+ buf[NIOS_PKT_8x8_IDX_RESV2 + 0] = 0x00;
+ buf[NIOS_PKT_8x8_IDX_RESV2 + 1] = 0x00;
+ buf[NIOS_PKT_8x8_IDX_RESV2 + 2] = 0x00;
+ buf[NIOS_PKT_8x8_IDX_RESV2 + 3] = 0x00;
+ buf[NIOS_PKT_8x8_IDX_RESV2 + 4] = 0x00;
+ buf[NIOS_PKT_8x8_IDX_RESV2 + 5] = 0x00;
+ buf[NIOS_PKT_8x8_IDX_RESV2 + 6] = 0x00;
+ buf[NIOS_PKT_8x8_IDX_RESV2 + 7] = 0x00;
+ buf[NIOS_PKT_8x8_IDX_RESV2 + 8] = 0x00;
+ buf[NIOS_PKT_8x8_IDX_RESV2 + 9] = 0x00;
+}
+
+/* Unpack the request buffer */
+static inline void nios_pkt_8x8_unpack(const uint8_t *buf, uint8_t *target,
+ bool *write, uint8_t *addr,
+ uint8_t *data)
+{
+ if (target != NULL) {
+ *target = buf[NIOS_PKT_8x8_IDX_TARGET_ID];
+ }
+
+ if (write != NULL) {
+ *write = (buf[NIOS_PKT_8x8_IDX_FLAGS] & NIOS_PKT_8x8_FLAG_WRITE) != 0;
+ }
+
+ if (addr != NULL) {
+ *addr = buf[NIOS_PKT_8x8_IDX_ADDR];
+ }
+
+ if (data != NULL) {
+ *data = buf[NIOS_PKT_8x8_IDX_DATA];
+ }
+}
+
+/* Pack the response buffer */
+static inline void nios_pkt_8x8_resp_pack(uint8_t *buf, uint8_t target,
+ bool write, uint8_t addr,
+ uint8_t data, bool success)
+{
+ nios_pkt_8x8_pack(buf, target, write, addr, data);
+
+ if (success) {
+ buf[NIOS_PKT_8x8_IDX_FLAGS] |= NIOS_PKT_8x8_FLAG_SUCCESS;
+ }
+}
+
+/* Unpack the response buffer */
+static inline void nios_pkt_8x8_resp_unpack(const uint8_t *buf, uint8_t *target,
+ bool *write, uint8_t *addr,
+ uint8_t *data, bool *success)
+{
+ nios_pkt_8x8_unpack(buf, target, write, addr, data);
+
+ if ((buf[NIOS_PKT_8x8_IDX_FLAGS] & NIOS_PKT_8x8_FLAG_SUCCESS) != 0) {
+ *success = true;
+ } else {
+ *success = false;
+ }
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/include/nios_pkt_formats.h b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_formats.h
new file mode 100644
index 0000000..7ab5901
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_formats.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef BLADERF_NIOS_PKT_FORMATS_H_
+#define BLADERF_NIOS_PKT_FORMATS_H_
+
+#include "nios_pkt_legacy.h"
+#include "nios_pkt_retune.h"
+#include "nios_pkt_retune2.h"
+#include "nios_pkt_8x8.h"
+#include "nios_pkt_8x16.h"
+#include "nios_pkt_8x32.h"
+#include "nios_pkt_8x64.h"
+#include "nios_pkt_32x32.h"
+#include "nios_pkt_16x64.h"
+
+#define NIOS_PKT_LEN 16
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/include/nios_pkt_legacy.h b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_legacy.h
new file mode 100644
index 0000000..9912ce9
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_legacy.h
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2013-2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLADERF_NIOS_PKT_LEGACY_H_
+#define BLADERF_NIOS_PKT_LEGACY_H_
+
+/* This is the original packet format used to issue requests from the
+ * host to the FPGA via the FX3 UART.
+ *
+ * This format remains supported for backwards compatibility, but should no
+ * longer be added to.
+ *
+ * If you're looking to customize the FPGA, consider using
+ * one of the "pkt_AxB" packet formats and handlers, or implementing a new
+ * format and handler.
+ *
+ * Request
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | Configuration byte (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 2 - 15 | Pairs of 8-bit addr, 8-bit data |
+ * +----------------+---------------------------------------------------------+
+ *
+ *
+ *
+ * Note 1: Configuration byte:
+ *
+ * +================+============================+
+ * | Bit(s) | Value |
+ * +================+============================+
+ * | 7 | 1 = Read operation |
+ * +----------------+----------------------------+
+ * | 6 | 1 = Write operation |
+ * +----------------+----------------------------+
+ * | 5:4 | Device: |
+ * | | 00 - Config PIO (Note 2) |
+ * | | 01 - LMS register |
+ * | | 10 - VCTCXO Trim DAC |
+ * | | 11 - SI5338 register |
+ * +----------------+----------------------------+
+ * | 3 | Unused |
+ * +----------------+----------------------------+
+ * | 2:0 | Addr/Data pair count |
+ * | | (Note 2) |
+ * +----------------+----------------------------+
+ *
+ * Note 2: Config PIO addresses
+ *
+ * The NIOS II core and modules in the FPGA's programmable fabric are connected
+ * via parallel IO (PIO). See the NIOS_PKT_LEGACY_PIO_ADDR_* definitions
+ * in this file contain a virtual "register map" for these modules.
+ *
+ * Note 3: "Count" field
+ *
+ * The original intent of this field was to allow multiple register
+ * accesses to be requested at once.
+ *
+ * However, this feature was not leveraged by the host code for the LMS and
+ * SI5338 accesses, so revised legacy packet handler only processes the
+ * first addr/data pair.
+ *
+ * Readback of the time tamer values is the only case where this field
+ * is set to a count greater than 1.
+ *
+ * Although config PIO values are larger than one byte, the host code
+ * accessed these byte by byte through multiple requests. For example,
+ * 4 accesses would be required to fully read/write the configuration PIO.
+ *
+ * The above inefficiency is the motivation behind adding packet handlers
+ * that can read/write 32 or 64 bits in a single request (e.g., pkt_8x32,
+ * pkt_8x64).
+ *
+ *
+ *
+ * Response
+ * ----------------------
+ *
+ * The response for the legacy packet is essentially just the device
+ * echoing the request.
+ *
+ * On a read request, the number of requested items will be populated
+ * in bytes 2:15.
+ *
+ * The remaining bytes, or all of bytes 2:15 on a write request, should
+ * be regarded as "undefined" values and not used.
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | Configuration byte |
+ * +----------------+---------------------------------------------------------+
+ * | 2 - 15 | Pairs of 8-bit addr, 8-bit data |
+ * +----------------+---------------------------------------------------------+
+ *
+ */
+
+#define NIOS_PKT_LEGACY_MAGIC 'N'
+
+#define NIOS_PKT_LEGACY_DEV_GPIO_ADDR 0
+#define NIOS_PKT_LEGACY_DEV_RX_GAIN_ADDR 4
+#define NIOS_PKT_LEGACY_DEV_RX_PHASE_ADDR 6
+#define NIOS_PKT_LEGACY_DEV_TX_GAIN_ADDR 8
+#define NIOS_PKT_LEGACY_DEV_TX_PHASE_ADDR 10
+#define NIOS_PKT_LEGACY_DEV_FPGA_VERSION_ID 12
+
+#define NIOS_PKT_LEGACY_MODE_CNT_MASK 0x7
+#define NIOS_PKT_LEGACY_MODE_CNT_SHIFT 0
+#define NIOS_PKT_LEGACY_MODE_DEV_MASK 0x30
+#define NIOS_PKT_LEGACY_MODE_DEV_SHIFT 4
+
+#define NIOS_PKT_LEGACY_DEV_CONFIG (0 << NIOS_PKT_LEGACY_MODE_DEV_SHIFT)
+#define NIOS_PKT_LEGACY_DEV_LMS (1 << NIOS_PKT_LEGACY_MODE_DEV_SHIFT)
+#define NIOS_PKT_LEGACY_DEV_VCTCXO (2 << NIOS_PKT_LEGACY_MODE_DEV_SHIFT)
+#define NIOS_PKT_LEGACY_DEV_SI5338 (3 << NIOS_PKT_LEGACY_MODE_DEV_SHIFT)
+
+#define NIOS_PKT_LEGACY_MODE_DIR_MASK 0xC0
+#define NIOS_PKT_LEGACY_MODE_DIR_SHIFT 6
+#define NIOS_PKT_LEGACY_MODE_DIR_READ (2 << NIOS_PKT_LEGACY_MODE_DIR_SHIFT)
+#define NIOS_PKT_LEGACY_MODE_DIR_WRITE (1 << NIOS_PKT_LEGACY_MODE_DIR_SHIFT)
+
+
+/* PIO address space */
+
+/*
+ * 32-bit Device control register.
+ *
+ * This is register accessed via the libbladeRF functions,
+ * bladerf_config_gpio_write() and bladerf_config_gpio_read().
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_CONTROL 0
+#define NIOS_PKT_LEGACY_PIO_LEN_CONTROL 4
+
+/*
+ * IQ Correction: 16-bit RX Gain value
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_IQ_RX_GAIN 4
+#define NIOS_PKT_LEGACY_PIO_LEN_IQ_RX_GAIN 2
+
+/*
+ * IQ Correction: 16-bit RX Phase value
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_IQ_RX_PHASE 6
+#define NIOS_PKT_LEGACY_PIO_LEN_IQ_RX_PHASE 2
+
+/*
+ * IQ Correction: 16-bit TX Gain value
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_IQ_TX_GAIN 8
+#define NIOS_PKT_LEGACY_PIO_LEN_IQ_TX_GAIN 2
+
+/*
+ * IQ Correction: 16-bit TX Phase value
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_IQ_TX_PHASE 10
+#define NIOS_PKT_LEGACY_PIO_LEN_IQ_TX_PHASE 2
+
+/*
+ * 32-bit FPGA Version (read-only)
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_FPGA_VERSION 12
+#define NIOS_PKT_LEGACY_PIO_LEN_FPGA_VERSION 4
+
+/*
+ * 64-bit RX timestamp
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_RX_TIMESTAMP 16
+#define NIOS_PKT_LEGACY_PIO_LEN_RX_TIMESTAMP 8
+
+/*
+ * 64-bit TX timestamp
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_TX_TIMESTAMP 24
+#define NIOS_PKT_LEGACY_PIO_LEN_TX_TIMESTAMP 8
+
+/*
+ * VCTCXO Trim DAC value
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_VCTCXO 34
+#define NIOS_PKT_LEGACY_PIO_LEN_VCTCXO 2
+
+/*
+ * XB-200 ADF4351 Synthesizer
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_XB200_SYNTH 36
+#define NIOS_PKT_LEGACY_PIO_LEN_XB200_SYNTH 4
+
+/*
+ * Expansion IO
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_EXP 40
+#define NIOS_PKT_LEGACY_PIO_LEN_EXP 4
+
+/*
+ * Expansion IO Direction
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_EXP_DIR 44
+#define NIOS_PKT_LEGACY_PIO_LEN_EXP_DIR 4
+
+struct uart_cmd {
+ unsigned char addr;
+ unsigned char data;
+};
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/include/nios_pkt_retune.h b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_retune.h
new file mode 100644
index 0000000..9bedcb0
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_retune.h
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLADERF_NIOS_PKT_RETUNE_H_
+#define BLADERF_NIOS_PKT_RETUNE_H_
+
+#ifndef BLADERF_NIOS_BUILD
+# include <libbladeRF.h>
+#else
+# include "libbladeRF_nios_compat.h"
+#endif
+
+#include <stdint.h>
+
+/* Specify this value instead of a timestamp to clear the retune queue */
+#define NIOS_PKT_RETUNE_CLEAR_QUEUE ((uint64_t) -1)
+
+/* This file defines the Host <-> FPGA (NIOS II) packet formats for
+ * retune messages. This packet is formatted, as follows. All values are
+ * little-endian.
+ *
+ * Request
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | 64-bit timestamp denoting when to retune. (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 9 | 32-bit LMS6002D n_int & n_frac register values (Note 2) |
+ * +----------------+---------------------------------------------------------+
+ * | 13 | RX/TX bit, FREQSEL LMS6002D reg value (Note 3) |
+ * +----------------+---------------------------------------------------------+
+ * | 14 | Bit 7: Band-selection (Note 4) |
+ * | | Bit 6: 1=Quick tune, 0=Normal tune |
+ * | | Bits [5:0] VCOCAP[5:0] Hint |
+ * +----------------+---------------------------------------------------------+
+ * | 15 | 8-bit reserved word. Should be set to 0x00. |
+ * +----------------+---------------------------------------------------------+
+ *
+ * (Note 1) Special Timestamp Values:
+ *
+ * Tune "Now": 0x0000000000000000
+ * Clear Retune Queue: 0xffffffffffffffff
+ *
+ * When the "Clear Retune Queue" value is used, all of the other tuning
+ * parameters are ignored.
+ *
+ * (Note 2) Packed as follows:
+ *
+ * +================+=======================+
+ * | Byte offset | (MSB) Value (LSB)|
+ * +================+=======================+
+ * | 0 | NINT[8:1] |
+ * +----------------+-----------------------+
+ * | 1 | NINT[0], NFRAC[22:16] |
+ * +----------------+-----------------------+
+ * | 2 | NFRAC[15:8] |
+ * +----------------+-----------------------+
+ * | 3 | NFRAC[7:0] |
+ * +----------------+-----------------------+
+ *
+ * (Note 3) Packed as follows:
+ *
+ * +================+=======================+
+ * | Bit(s) | Value |
+ * +================+=======================+
+ * | 7 | TX |
+ * +----------------+-----------------------+
+ * | 6 | RX |
+ * +----------------+-----------------------+
+ * | [5:0] | FREQSEL |
+ * +----------------+-----------------------+
+ *
+ * (Notes 4) Band-selection bit = 1 implies "Low band". 0 = "High band"
+ */
+
+#define NIOS_PKT_RETUNE_IDX_MAGIC 0
+#define NIOS_PKT_RETUNE_IDX_TIME 1
+#define NIOS_PKT_RETUNE_IDX_INTFRAC 9
+#define NIOS_PKT_RETUNE_IDX_FREQSEL 13
+#define NIOS_PKT_RETUNE_IDX_BANDSEL 14
+#define NIOS_PKT_RETUNE_IDX_RESV 15
+
+#define NIOS_PKT_RETUNE_MAGIC 'T'
+
+
+#define FLAG_QUICK_TUNE (1 << 6)
+#define FLAG_RX (1 << 6)
+#define FLAG_TX (1 << 7)
+#define FLAG_LOW_BAND (1 << 7)
+
+
+/* Denotes no tune word is supplied. */
+#define NIOS_PKT_RETUNE_NO_HINT 0xff
+
+/* Denotes that the retune should not be scheduled - it should occur "now" */
+#define NIOS_PKT_RETUNE_NOW ((uint64_t) 0x00)
+
+#define PACK_TXRX_FREQSEL(module_, freqsel_) \
+ (freqsel_ & 0x3f)
+
+/* Pack the retune request buffer with the provided parameters */
+static inline void nios_pkt_retune_pack(uint8_t *buf,
+ bladerf_module module,
+ uint64_t timestamp,
+ uint16_t nint,
+ uint32_t nfrac,
+ uint8_t freqsel,
+ uint8_t vcocap,
+ bool low_band,
+ uint8_t xb_gpio,
+ bool quick_tune)
+{
+ buf[NIOS_PKT_RETUNE_IDX_MAGIC] = NIOS_PKT_RETUNE_MAGIC;
+
+ buf[NIOS_PKT_RETUNE_IDX_TIME + 0] = timestamp & 0xff;
+ buf[NIOS_PKT_RETUNE_IDX_TIME + 1] = (timestamp >> 8) & 0xff;
+ buf[NIOS_PKT_RETUNE_IDX_TIME + 2] = (timestamp >> 16) & 0xff;
+ buf[NIOS_PKT_RETUNE_IDX_TIME + 3] = (timestamp >> 24) & 0xff;
+ buf[NIOS_PKT_RETUNE_IDX_TIME + 4] = (timestamp >> 32) & 0xff;
+ buf[NIOS_PKT_RETUNE_IDX_TIME + 5] = (timestamp >> 40) & 0xff;
+ buf[NIOS_PKT_RETUNE_IDX_TIME + 6] = (timestamp >> 48) & 0xff;
+ buf[NIOS_PKT_RETUNE_IDX_TIME + 7] = (timestamp >> 56) & 0xff;
+
+ buf[NIOS_PKT_RETUNE_IDX_INTFRAC + 0] = (nint >> 1) & 0xff;
+ buf[NIOS_PKT_RETUNE_IDX_INTFRAC + 1] = (nint & 0x1) << 7;
+ buf[NIOS_PKT_RETUNE_IDX_INTFRAC + 1] |= ((nfrac >> 16) & 0x7f);
+ buf[NIOS_PKT_RETUNE_IDX_INTFRAC + 2] = (nfrac >> 8) & 0xff;
+ buf[NIOS_PKT_RETUNE_IDX_INTFRAC + 3] = nfrac & 0xff;
+
+ buf[NIOS_PKT_RETUNE_IDX_FREQSEL] = freqsel & 0xff;
+
+ switch (module) {
+ case BLADERF_MODULE_TX:
+ buf[NIOS_PKT_RETUNE_IDX_FREQSEL] |= FLAG_TX;
+ break;
+
+ case BLADERF_MODULE_RX:
+ buf[NIOS_PKT_RETUNE_IDX_FREQSEL] |= FLAG_RX;
+ break;
+
+ default:
+ /* Erroneous case - should not occur */
+ break;
+ }
+
+ if (low_band) {
+ buf[NIOS_PKT_RETUNE_IDX_BANDSEL] = FLAG_LOW_BAND;
+ } else {
+ buf[NIOS_PKT_RETUNE_IDX_BANDSEL] = 0x00;
+ }
+
+ if (quick_tune) {
+ buf[NIOS_PKT_RETUNE_IDX_BANDSEL] |= FLAG_QUICK_TUNE;
+ }
+
+ buf[NIOS_PKT_RETUNE_IDX_BANDSEL] |= vcocap;
+
+ buf[NIOS_PKT_RETUNE_IDX_RESV] = xb_gpio;
+}
+
+/* Unpack a retune request */
+static inline void nios_pkt_retune_unpack(const uint8_t *buf,
+ bladerf_module *module,
+ uint64_t *timestamp,
+ uint16_t *nint,
+ uint32_t *nfrac,
+ uint8_t *freqsel,
+ uint8_t *vcocap,
+ bool *low_band,
+ uint8_t *xb_gpio,
+ bool *quick_tune)
+{
+ *timestamp = ( ((uint64_t) buf[NIOS_PKT_RETUNE_IDX_TIME + 0]) << 0);
+ *timestamp |= ( ((uint64_t) buf[NIOS_PKT_RETUNE_IDX_TIME + 1]) << 8);
+ *timestamp |= ( ((uint64_t) buf[NIOS_PKT_RETUNE_IDX_TIME + 2]) << 16);
+ *timestamp |= ( ((uint64_t) buf[NIOS_PKT_RETUNE_IDX_TIME + 3]) << 24);
+ *timestamp |= ( ((uint64_t) buf[NIOS_PKT_RETUNE_IDX_TIME + 4]) << 32);
+ *timestamp |= ( ((uint64_t) buf[NIOS_PKT_RETUNE_IDX_TIME + 5]) << 40);
+ *timestamp |= ( ((uint64_t) buf[NIOS_PKT_RETUNE_IDX_TIME + 6]) << 48);
+ *timestamp |= ( ((uint64_t) buf[NIOS_PKT_RETUNE_IDX_TIME + 7]) << 56);
+
+ *nint = buf[NIOS_PKT_RETUNE_IDX_INTFRAC + 0] << 1;
+ *nint |= buf[NIOS_PKT_RETUNE_IDX_INTFRAC + 1] >> 7;
+
+ *nfrac = (buf[NIOS_PKT_RETUNE_IDX_INTFRAC + 1] & 0x7f) << 16;
+ *nfrac |= buf[NIOS_PKT_RETUNE_IDX_INTFRAC + 2] << 8;
+ *nfrac |= buf[NIOS_PKT_RETUNE_IDX_INTFRAC + 3];
+
+ *freqsel = buf[NIOS_PKT_RETUNE_IDX_FREQSEL] & 0x3f;
+
+ *module = -1;
+
+ if (buf[NIOS_PKT_RETUNE_IDX_FREQSEL] & FLAG_TX) {
+ *module = BLADERF_MODULE_TX;
+ } else if (buf[NIOS_PKT_RETUNE_IDX_FREQSEL] & FLAG_RX) {
+ *module = BLADERF_MODULE_RX;
+ }
+
+ *low_band = (buf[NIOS_PKT_RETUNE_IDX_BANDSEL] & FLAG_LOW_BAND) != 0;
+ *quick_tune = (buf[NIOS_PKT_RETUNE_IDX_BANDSEL] & FLAG_QUICK_TUNE) != 0;
+ *vcocap = buf[NIOS_PKT_RETUNE_IDX_BANDSEL] & 0x3f;
+ *xb_gpio = buf[NIOS_PKT_RETUNE_IDX_RESV];
+}
+
+
+/*
+ * Response
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | 64-bit duration denoting how long the operation took to |
+ * | | complete, in units of timestamp ticks. (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 9 | Bits [7:6] Reserved, set to 0. |
+ * | | Bits [5:0] VCOCAP value used. (Note 2) |
+ * +----------------+---------------------------------------------------------+
+ * | 10 | Status Flags (Note 3) |
+ * +----------------+---------------------------------------------------------+
+ * | 11-15 | Reserved. All bits set to 0. |
+ * +----------------+---------------------------------------------------------+
+ *
+ * (Note 1) This value will be zero if timestamps are not running for the
+ * associated module.
+ *
+ * (Note 2) This field's value should be ignored when reading a response for
+ * a request to clear the retune queue.
+ *
+ * (Note 3) Description of Status Flags:
+ *
+ * flags[0]: 1 = Timestamp and VCOCAP are valid. This is only the case for
+ * "Tune NOW" requests. It is not possible to return this
+ * information for scheduled retunes, as the event generally
+ * does not occur before the response is set.
+ *
+ * 0 = This was a scheduled retune. Timestamp and VCOCAP Fields
+ * should be ignored.
+ *
+ *
+ * flags[1]: 1 = Operation completed successfully.
+ * 0 = Operation failed.
+ *
+ * For "Tune NOW" requests, a failure may occur as the result
+ * of the tuning algorithm failing to occur, and such other
+ * unexpected failurs.
+ *
+ * The scheduled tune request will failure if the retune queue
+ * is full.
+ *
+ * flags[7:2] Reserved. Set to 0.
+ */
+
+#define NIOS_PKT_RETUNERESP_IDX_MAGIC 0
+#define NIOS_PKT_RETUNERESP_IDX_TIME 1
+#define NIOS_PKT_RETUNERESP_IDX_VCOCAP 9
+#define NIOS_PKT_RETUNERESP_IDX_FLAGS 10
+#define NIOS_PKT_RETUNERESP_IDX_RESV 11
+
+#define NIOS_PKT_RETUNERESP_FLAG_TSVTUNE_VALID (1 << 0)
+#define NIOS_PKT_RETUNERESP_FLAG_SUCCESS (1 << 1)
+
+static inline void nios_pkt_retune_resp_pack(uint8_t *buf,
+ uint64_t duration,
+ uint8_t vcocap,
+ uint8_t flags)
+{
+ buf[NIOS_PKT_RETUNERESP_IDX_MAGIC] = NIOS_PKT_RETUNE_MAGIC;
+
+ buf[NIOS_PKT_RETUNERESP_IDX_TIME + 0] = duration & 0xff;
+ buf[NIOS_PKT_RETUNERESP_IDX_TIME + 1] = (duration >> 8) & 0xff;
+ buf[NIOS_PKT_RETUNERESP_IDX_TIME + 2] = (duration >> 16) & 0xff;
+ buf[NIOS_PKT_RETUNERESP_IDX_TIME + 3] = (duration >> 24) & 0xff;
+ buf[NIOS_PKT_RETUNERESP_IDX_TIME + 4] = (duration >> 32) & 0xff;
+ buf[NIOS_PKT_RETUNERESP_IDX_TIME + 5] = (duration >> 40) & 0xff;
+ buf[NIOS_PKT_RETUNERESP_IDX_TIME + 6] = (duration >> 48) & 0xff;
+ buf[NIOS_PKT_RETUNERESP_IDX_TIME + 7] = (duration >> 56) & 0xff;
+
+ buf[NIOS_PKT_RETUNERESP_IDX_VCOCAP] = vcocap;
+
+ buf[NIOS_PKT_RETUNERESP_IDX_FLAGS] = flags;
+
+ buf[NIOS_PKT_RETUNERESP_IDX_RESV + 0] = 0x00;
+ buf[NIOS_PKT_RETUNERESP_IDX_RESV + 1] = 0x00;
+ buf[NIOS_PKT_RETUNERESP_IDX_RESV + 2] = 0x00;
+ buf[NIOS_PKT_RETUNERESP_IDX_RESV + 3] = 0x00;
+ buf[NIOS_PKT_RETUNERESP_IDX_RESV + 4] = 0x00;
+}
+
+static inline void nios_pkt_retune_resp_unpack(const uint8_t *buf,
+ uint64_t *duration,
+ uint8_t *vcocap,
+ uint8_t *flags)
+{
+ *duration = buf[NIOS_PKT_RETUNERESP_IDX_TIME + 0];
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNERESP_IDX_TIME + 1]) << 8;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNERESP_IDX_TIME + 2]) << 16;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNERESP_IDX_TIME + 3]) << 24;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNERESP_IDX_TIME + 4]) << 32;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNERESP_IDX_TIME + 5]) << 40;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNERESP_IDX_TIME + 6]) << 48;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNERESP_IDX_TIME + 7]) << 56;
+
+ *vcocap = buf[NIOS_PKT_RETUNERESP_IDX_VCOCAP];
+
+ *flags = buf[NIOS_PKT_RETUNERESP_IDX_FLAGS];
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/include/nios_pkt_retune2.h b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_retune2.h
new file mode 100644
index 0000000..e58b51b
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_retune2.h
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2018 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLADERF_NIOS_PKT_RETUNE2_H_
+#define BLADERF_NIOS_PKT_RETUNE2_H_
+
+#ifndef BLADERF_NIOS_BUILD
+# include <libbladeRF.h>
+#else
+# include "libbladeRF_nios_compat.h"
+#endif
+
+#include <stdint.h>
+
+/* This file defines the Host <-> FPGA (NIOS II) packet formats for
+ * retune2 messages. This packet is formatted, as follows. All values are
+ * little-endian.
+ *
+ * Request
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | 64-bit timestamp denoting when to retune. (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 9 | 16-bit Nios fast lock profile number to load (Note 2) |
+ * +----------------+---------------------------------------------------------+
+ * | 11 | 8-bit RFFE fast lock profile slot to use |
+ * +----------------+---------------------------------------------------------+
+ * | 12 | Bit 7: RX bit (set if this is an RX profile |
+ * | | Bits 6: TX output port selection |
+ * | | Bits [5:0]: RX input port selection |
+ * +----------------+---------------------------------------------------------+
+ * | 13 | Bits [7:6]: External TX2 SPDT switch setting |
+ * | | Bits [5:4]: External TX1 SPDT switch setting |
+ * | | Bits [3:2]: External RX2 SPDT switch setting |
+ * | | Bits [1:0]: External RX1 SPDT switch setting |
+ * +----------------+---------------------------------------------------------+
+ * | 14-15 | 8-bit reserved words. Should be set to 0x00. |
+ * +----------------+---------------------------------------------------------+
+ *
+ * (Note 1) Special Timestamp Values:
+ *
+ * Tune "Now": 0x0000000000000000
+ * Clear Retune Queue: 0xffffffffffffffff
+ *
+ * When the "Clear Retune Queue" value is used, all of the other tuning
+ * parameters are ignored.
+ *
+ * (Note 2) Packed as follows:
+ *
+ * +================+=======================+
+ * | Byte offset | (MSB) Value (LSB)|
+ * +================+=======================+
+ * | 0 | NIOS_PROFILE[7:0] |
+ * +----------------+-----------------------+
+ * | 1 | NIOS_PROFILE[15:8] |
+ * +----------------+-----------------------+
+ *
+ */
+
+#define NIOS_PKT_RETUNE2_IDX_MAGIC 0
+#define NIOS_PKT_RETUNE2_IDX_TIME 1
+#define NIOS_PKT_RETUNE2_IDX_NIOS_PROFILE 9
+#define NIOS_PKT_RETUNE2_IDX_RFFE_PROFILE 11
+#define NIOS_PKT_RETUNE2_IDX_RFFE_PORT 12
+#define NIOS_PKT_RETUNE2_IDX_SPDT 13
+#define NIOS_PKT_RETUNE2_IDX_RESV 14
+
+#define NIOS_PKT_RETUNE2_MAGIC 'U'
+
+/* Specify this value instead of a timestamp to clear the retune2 queue */
+#define NIOS_PKT_RETUNE2_CLEAR_QUEUE ((uint64_t) -1)
+
+/* Denotes that the retune2 should not be scheduled - it should occur "now" */
+#define NIOS_PKT_RETUNE2_NOW ((uint64_t) 0x00)
+
+/* The IS_RX bit embedded in the 'port' parameter of the retune2 packet */
+#define NIOS_PKT_RETUNE2_PORT_IS_RX_MASK (0x1 << 7)
+
+/* Pack the retune2 request buffer with the provided parameters */
+static inline void nios_pkt_retune2_pack(uint8_t *buf,
+ bladerf_module module,
+ uint64_t timestamp,
+ uint16_t nios_profile,
+ uint8_t rffe_profile,
+ uint8_t port,
+ uint8_t spdt)
+{
+ uint8_t pkt_port;
+
+ /* Clear the IS_RX bit of the port parameter */
+ pkt_port = (port & (~NIOS_PKT_RETUNE2_PORT_IS_RX_MASK));
+
+ /* Set the IS_RX bit (if needed) */
+ pkt_port = (pkt_port | (BLADERF_CHANNEL_IS_TX(module) ? 0x0 :
+ NIOS_PKT_RETUNE2_PORT_IS_RX_MASK)) & 0xff;
+
+ buf[NIOS_PKT_RETUNE2_IDX_MAGIC] = NIOS_PKT_RETUNE2_MAGIC;
+
+ buf[NIOS_PKT_RETUNE2_IDX_TIME + 0] = (timestamp >> 0) & 0xff;
+ buf[NIOS_PKT_RETUNE2_IDX_TIME + 1] = (timestamp >> 8) & 0xff;
+ buf[NIOS_PKT_RETUNE2_IDX_TIME + 2] = (timestamp >> 16) & 0xff;
+ buf[NIOS_PKT_RETUNE2_IDX_TIME + 3] = (timestamp >> 24) & 0xff;
+ buf[NIOS_PKT_RETUNE2_IDX_TIME + 4] = (timestamp >> 32) & 0xff;
+ buf[NIOS_PKT_RETUNE2_IDX_TIME + 5] = (timestamp >> 40) & 0xff;
+ buf[NIOS_PKT_RETUNE2_IDX_TIME + 6] = (timestamp >> 48) & 0xff;
+ buf[NIOS_PKT_RETUNE2_IDX_TIME + 7] = (timestamp >> 56) & 0xff;
+
+ buf[NIOS_PKT_RETUNE2_IDX_NIOS_PROFILE + 0] = (nios_profile >> 0) & 0xff;
+ buf[NIOS_PKT_RETUNE2_IDX_NIOS_PROFILE + 1] = (nios_profile >> 8) & 0xff;
+
+ buf[NIOS_PKT_RETUNE2_IDX_RFFE_PROFILE] = rffe_profile & 0xff;
+
+ buf[NIOS_PKT_RETUNE2_IDX_RFFE_PORT] = pkt_port;
+
+ buf[NIOS_PKT_RETUNE2_IDX_SPDT] = spdt & 0xff;
+
+ buf[NIOS_PKT_RETUNE2_IDX_RESV + 0] = 0x00;
+ buf[NIOS_PKT_RETUNE2_IDX_RESV + 1] = 0x00;
+}
+
+/* Unpack a retune request */
+static inline void nios_pkt_retune2_unpack(const uint8_t *buf,
+ bladerf_module *module,
+ uint64_t *timestamp,
+ uint16_t *nios_profile,
+ uint8_t *rffe_profile,
+ uint8_t *port,
+ uint8_t *spdt)
+{
+ *timestamp = ( ((uint64_t)buf[NIOS_PKT_RETUNE2_IDX_TIME + 0]) << 0 );
+ *timestamp |= ( ((uint64_t)buf[NIOS_PKT_RETUNE2_IDX_TIME + 1]) << 8 );
+ *timestamp |= ( ((uint64_t)buf[NIOS_PKT_RETUNE2_IDX_TIME + 2]) << 16 );
+ *timestamp |= ( ((uint64_t)buf[NIOS_PKT_RETUNE2_IDX_TIME + 3]) << 24 );
+ *timestamp |= ( ((uint64_t)buf[NIOS_PKT_RETUNE2_IDX_TIME + 4]) << 32 );
+ *timestamp |= ( ((uint64_t)buf[NIOS_PKT_RETUNE2_IDX_TIME + 5]) << 40 );
+ *timestamp |= ( ((uint64_t)buf[NIOS_PKT_RETUNE2_IDX_TIME + 6]) << 48 );
+ *timestamp |= ( ((uint64_t)buf[NIOS_PKT_RETUNE2_IDX_TIME + 7]) << 56 );
+
+ *nios_profile = ( ((uint16_t)buf[NIOS_PKT_RETUNE2_IDX_NIOS_PROFILE + 0])
+ << 0 );
+ *nios_profile |= ( ((uint16_t)buf[NIOS_PKT_RETUNE2_IDX_NIOS_PROFILE + 1])
+ << 8 );
+
+ *rffe_profile = buf[NIOS_PKT_RETUNE2_IDX_RFFE_PROFILE];
+
+ *port = buf[NIOS_PKT_RETUNE2_IDX_RFFE_PORT];
+
+ *spdt = buf[NIOS_PKT_RETUNE2_IDX_SPDT];
+
+ *module = ( (buf[NIOS_PKT_RETUNE2_IDX_RFFE_PORT] &
+ NIOS_PKT_RETUNE2_PORT_IS_RX_MASK) ? BLADERF_MODULE_RX :
+ BLADERF_MODULE_TX );
+
+}
+
+
+/*
+ * Response
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | 64-bit duration denoting how long the operation took to |
+ * | | complete, in units of timestamp ticks. (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 9 | Status Flags (Note 2) |
+ * +----------------+---------------------------------------------------------+
+ * | 10-15 | Reserved. All bits set to 0. |
+ * +----------------+---------------------------------------------------------+
+ *
+ * (Note 1) This value will be zero if timestamps are not running for the
+ * associated module.
+ *
+ * (Note 2) Description of Status Flags:
+ *
+ * flags[0]: 1 = Timestamp is valid. This is only the case for "Tune NOW"
+ * requests. It is not possible to return this information
+ * for scheduled retunes, as the event generally does not
+ * occur before the response is set.
+ *
+ * 0 = This was a scheduled retune. Timestamp fields should be
+ * ignored.
+ *
+ * flags[1]: 1 = Operation completed successfully.
+ * 0 = Operation failed.
+ *
+ * For "Tune NOW" requests, a failure may occur as the result
+ * of the tuning algorithm failing to occur, and such other
+ * unexpected failurs.
+ *
+ * The scheduled tune request will failure if the retune queue
+ * is full.
+ *
+ * flags[7:2] Reserved. Set to 0.
+ */
+
+#define NIOS_PKT_RETUNE2_RESP_IDX_MAGIC 0
+#define NIOS_PKT_RETUNE2_RESP_IDX_TIME 1
+#define NIOS_PKT_RETUNE2_RESP_IDX_FLAGS 9
+#define NIOS_PKT_RETUNE2_RESP_IDX_RESV 10
+
+#define NIOS_PKT_RETUNE2_RESP_FLAG_TSVTUNE_VALID (1 << 0)
+#define NIOS_PKT_RETUNE2_RESP_FLAG_SUCCESS (1 << 1)
+
+static inline void nios_pkt_retune2_resp_pack(uint8_t *buf,
+ uint64_t duration,
+ uint8_t flags)
+{
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_MAGIC] = NIOS_PKT_RETUNE2_MAGIC;
+
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 0] = duration & 0xff;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 1] = (duration >> 8) & 0xff;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 2] = (duration >> 16) & 0xff;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 3] = (duration >> 24) & 0xff;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 4] = (duration >> 32) & 0xff;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 5] = (duration >> 40) & 0xff;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 6] = (duration >> 48) & 0xff;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 7] = (duration >> 56) & 0xff;
+
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_FLAGS] = flags;
+
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_RESV + 0] = 0x00;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_RESV + 1] = 0x00;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_RESV + 2] = 0x00;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_RESV + 3] = 0x00;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_RESV + 4] = 0x00;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_RESV + 5] = 0x00;
+}
+
+static inline void nios_pkt_retune2_resp_unpack(const uint8_t *buf,
+ uint64_t *duration,
+ uint8_t *flags)
+{
+ *duration = buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 0];
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 1]) << 8;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 2]) << 16;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 3]) << 24;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 4]) << 32;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 5]) << 40;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 6]) << 48;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 7]) << 56;
+
+ *flags = buf[NIOS_PKT_RETUNE2_RESP_IDX_FLAGS];
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/src/ad936x_helpers.c b/Radio/HW/BladeRF/fpga_common/src/ad936x_helpers.c
new file mode 100644
index 0000000..15982fa
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/src/ad936x_helpers.c
@@ -0,0 +1,196 @@
+/*
+ * 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
+ */
+
+#ifdef BLADERF_NIOS_BUILD
+#include "devices.h"
+#endif // BLADERF_NIOS_BUILD
+
+/* Avoid building this in low-memory situations */
+#if !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X)
+
+#if !defined(BLADERF_NIOS_BUILD) && !defined(BLADERF_NIOS_PC_SIMULATION)
+#include "log.h"
+#endif
+
+#include "ad936x_helpers.h"
+#include "bladerf2_common.h"
+
+static bool tx_mute_state[2] = { false };
+
+uint32_t txmute_get_cached(struct ad9361_rf_phy *phy, bladerf_channel ch)
+{
+ switch (ch) {
+ case BLADERF_CHANNEL_TX(0):
+ return phy->tx1_atten_cached;
+ case BLADERF_CHANNEL_TX(1):
+ return phy->tx2_atten_cached;
+ default:
+ return 0;
+ }
+}
+
+int txmute_set_cached(struct ad9361_rf_phy *phy,
+ bladerf_channel ch,
+ uint32_t atten)
+{
+ switch (ch) {
+ case BLADERF_CHANNEL_TX(0):
+ phy->tx1_atten_cached = atten;
+ return 0;
+ case BLADERF_CHANNEL_TX(1):
+ phy->tx2_atten_cached = atten;
+ return 0;
+ default:
+ return BLADERF_ERR_INVAL;
+ }
+}
+
+int txmute_get(struct ad9361_rf_phy *phy, bladerf_channel ch, bool *state)
+{
+ int rfic_ch = (ch >> 1);
+
+ *state = tx_mute_state[rfic_ch];
+
+ return 0;
+}
+
+int txmute_set(struct ad9361_rf_phy *phy, bladerf_channel ch, bool state)
+{
+ int rfic_ch = (ch >> 1);
+ uint32_t const MUTED_ATTEN = 89750;
+ uint32_t atten, cached;
+ int status;
+
+ if (tx_mute_state[rfic_ch] == state) {
+ // short circuit if there's no change
+ return 0;
+ }
+
+ if (state) {
+ // mute: save the existing value before muting
+ uint32_t readval;
+
+ status = ad9361_get_tx_attenuation(phy, rfic_ch, &readval);
+ if (status < 0) {
+ return errno_ad9361_to_bladerf(status);
+ }
+
+ cached = readval;
+ atten = MUTED_ATTEN;
+ } else {
+ // unmute: restore the saved value
+ cached = txmute_get_cached(phy, ch);
+ atten = cached;
+ }
+
+ status = ad9361_set_tx_attenuation(phy, rfic_ch, atten);
+ if (status < 0) {
+ return errno_ad9361_to_bladerf(status);
+ }
+
+ status = txmute_set_cached(phy, ch, cached);
+ if (status < 0) {
+ return status;
+ }
+
+ tx_mute_state[rfic_ch] = state;
+
+ return 0;
+}
+
+int set_ad9361_port_by_freq(struct ad9361_rf_phy *phy,
+ bladerf_channel ch,
+ bool enabled,
+ bladerf_frequency freq)
+{
+ struct band_port_map const *port_map = NULL;
+ int status;
+
+ /* Look up the port configuration for this frequency */
+ port_map = _get_band_port_map_by_freq(ch, enabled ? freq : 0);
+
+ if (NULL == port_map) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Set the AD9361 port accordingly */
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ status = ad9361_set_tx_rf_port_output(phy, port_map->rfic_port);
+ } else {
+ status = ad9361_set_rx_rf_port_input(phy, port_map->rfic_port);
+ }
+
+ return errno_ad9361_to_bladerf(status);
+}
+
+enum rf_gain_ctrl_mode gainmode_bladerf_to_ad9361(bladerf_gain_mode gainmode,
+ bool *ok)
+{
+ struct bladerf_rfic_gain_mode_map const *mode_map;
+ size_t mode_map_len;
+ size_t i;
+
+ mode_map = bladerf2_rx_gain_mode_map;
+ mode_map_len = ARRAY_SIZE(bladerf2_rx_gain_mode_map);
+
+ if (NULL != ok) {
+ *ok = false;
+ }
+
+ for (i = 0; i < mode_map_len; ++i) {
+ if (mode_map[i].brf_mode == gainmode) {
+ if (NULL != ok) {
+ *ok = true;
+ }
+ return mode_map[i].rfic_mode;
+ }
+ }
+
+ return 0;
+};
+
+bladerf_gain_mode gainmode_ad9361_to_bladerf(enum rf_gain_ctrl_mode gainmode,
+ bool *ok)
+{
+ struct bladerf_rfic_gain_mode_map const *mode_map;
+ size_t mode_map_len;
+ size_t i;
+
+ mode_map = bladerf2_rx_gain_mode_map;
+ mode_map_len = ARRAY_SIZE(bladerf2_rx_gain_mode_map);
+
+ if (NULL != ok) {
+ *ok = false;
+ }
+
+ for (i = 0; i < mode_map_len; ++i) {
+ if (mode_map[i].rfic_mode == gainmode) {
+ if (NULL != ok) {
+ *ok = true;
+ }
+ return mode_map[i].brf_mode;
+ }
+ }
+
+ return 0;
+}
+
+#endif // !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X)
diff --git a/Radio/HW/BladeRF/fpga_common/src/ad936x_params.c b/Radio/HW/BladeRF/fpga_common/src/ad936x_params.c
new file mode 100644
index 0000000..aa7bebe
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/src/ad936x_params.c
@@ -0,0 +1,1024 @@
+#ifdef BLADERF_NIOS_BUILD
+#include "devices.h"
+#endif // BLADERF_NIOS_BUILD
+
+/* Avoid building this in low-memory situations */
+#if !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X)
+
+#include "ad9361_api.h"
+#include "platform.h"
+
+/**
+ * Reference:
+ * https://wiki.analog.com/resources/tools-software/linux-drivers/iio-transceiver/ad9361-customization
+ *
+ * N/A = not applicable due to other setting; changes may unmask these
+ * DEFAULT = changed during device initialization
+ */
+
+// clang-format off
+AD9361_InitParam bladerf2_rfic_init_params = {
+ /* Device selection */
+ ID_AD9361, // AD9361 RF Agile Transceiver // dev_sel
+
+ /* Identification number */
+ 0, // Chip ID 0 // id_no
+
+ /* Reference Clock */
+ 38400000UL, // RefClk = 38.4 MHz // reference_clk_rate
+
+ /* Base Configuration */
+ 1, // use 2Rx2Tx mode // two_rx_two_tx_mode_enable *** adi,2rx-2tx-mode-enable
+ 1, // N/A when two_rx_two_tx_mode_enable = 1 // one_rx_one_tx_mode_use_rx_num *** adi,1rx-1tx-mode-use-rx-num
+ 1, // N/A when two_rx_two_tx_mode_enable = 1 // one_rx_one_tx_mode_use_tx_num *** adi,1rx-1tx-mode-use-tx-num
+ 1, // use FDD mode // frequency_division_duplex_mode_enable *** adi,frequency-division-duplex-mode-enable
+ 1, // use independent FDD mode // frequency_division_duplex_independent_mode_enable *** adi,frequency-division-duplex-independent-mode-enable
+ 0, // N/A when frequency_division_duplex_mode_enable = 1 // tdd_use_dual_synth_mode_enable *** adi,tdd-use-dual-synth-mode-enable
+ 0, // N/A when frequency_division_duplex_mode_enable = 1 // tdd_skip_vco_cal_enable *** adi,tdd-skip-vco-cal-enable
+ 0, // TX fastlock delay = 0 ns // tx_fastlock_delay_ns *** adi,tx-fastlock-delay-ns
+ 0, // RX fastlock delay = 0 ns // rx_fastlock_delay_ns *** adi,rx-fastlock-delay-ns
+ 0, // RX fastlock pin control disabled // rx_fastlock_pincontrol_enable *** adi,rx-fastlock-pincontrol-enable
+ 0, // TX fastlock pin control disabled // tx_fastlock_pincontrol_enable *** adi,tx-fastlock-pincontrol-enable
+ 0, // use internal RX LO // external_rx_lo_enable *** adi,external-rx-lo-enable
+ 0, // use internal TX LO // external_tx_lo_enable *** adi,external-tx-lo-enable
+ 5, // apply new tracking word: on gain change, after exiting RX state // dc_offset_tracking_update_event_mask *** adi,dc-offset-tracking-update-event-mask
+ 6, // atten value for DC tracking, RX LO > 4 GHz // dc_offset_attenuation_high_range *** adi,dc-offset-attenuation-high-range
+ 5, // atten value for DC tracking, RX LO < 4 GHz // dc_offset_attenuation_low_range *** adi,dc-offset-attenuation-low-range
+ 0x28, // loop gain for DC tracking, RX LO > 4 GHz // dc_offset_count_high_range *** adi,dc-offset-count-high-range
+ 0x32, // loop gain for DC tracking, RX LO < 4 GHz // dc_offset_count_low_range *** adi,dc-offset-count-low-range
+ 0, // use full gain table // split_gain_table_mode_enable *** adi,split-gain-table-mode-enable
+ MAX_SYNTH_FREF, // f_ref window 80 MHz // trx_synthesizer_target_fref_overwrite_hz *** adi,trx-synthesizer-target-fref-overwrite-hz
+ 0, // don't use improved RX QEC tracking // qec_tracking_slow_mode_enable *** adi,qec-tracking-slow-mode-enable
+
+ /* ENSM Control */
+ 0, // use level mode on ENABLE and TXNRX pins // ensm_enable_pin_pulse_mode_enable *** adi,ensm-enable-pin-pulse-mode-enable
+ 0, // use SPI writes for ENSM state, not ENABLE/TXNRX pins // ensm_enable_txnrx_control_enable *** adi,ensm-enable-txnrx-control-enable
+
+ /* LO Control */
+ 2400000000UL, // DEFAULT // rx_synthesizer_frequency_hz *** adi,rx-synthesizer-frequency-hz
+ 2400000000UL, // DEFAULT // tx_synthesizer_frequency_hz *** adi,tx-synthesizer-frequency-hz
+
+ /* Rate & BW Control */
+ { 983040000, 245760000, 122880000, 61440000, 30720000, 30720000 }, // DEFAULT // uint32_t rx_path_clock_frequencies[6] *** adi,rx-path-clock-frequencies
+ { 983040000, 122880000, 122880000, 61440000, 30720000, 30720000 }, // DEFAULT // uint32_t tx_path_clock_frequencies[6] *** adi,tx-path-clock-frequencies
+ 18000000, // DEFAULT // rf_rx_bandwidth_hz *** adi,rf-rx-bandwidth-hz
+ 18000000, // DEFAULT // rf_tx_bandwidth_hz *** adi,rf-tx-bandwidth-hz
+
+ /* RF Port Control */
+ 0, // DEFAULT // rx_rf_port_input_select *** adi,rx-rf-port-input-select
+ 0, // DEFAULT // tx_rf_port_input_select *** adi,tx-rf-port-input-select
+
+ /* TX Attenuation Control */
+ 10000, // DEFAULT // tx_attenuation_mdB *** adi,tx-attenuation-mdB
+ 0, // N/A when frequency_division_duplex_mode_enable = 1 // update_tx_gain_in_alert_enable *** adi,update-tx-gain-in-alert-enable
+
+ /* Reference Clock Control */
+ 1, // Expect external clock into XTALN // xo_disable_use_ext_refclk_enable *** adi,xo-disable-use-ext-refclk-enable
+ {3, 5920}, // ~0 ppm DCXO trim (N/A if ext clk) // dcxo_coarse_and_fine_tune[2] *** adi,dcxo-coarse-and-fine-tune
+ CLKOUT_DISABLE, // disable clkout pin (see enum ad9361_clkout) // clk_output_mode_select *** adi,clk-output-mode-select
+
+ /* Gain Control */
+ RF_GAIN_SLOWATTACK_AGC, // RX1 BLADERF_GAIN_DEFAULT = slow attack AGC // gc_rx1_mode *** adi,gc-rx1-mode
+ RF_GAIN_SLOWATTACK_AGC, // RX2 BLADERF_GAIN_DEFAULT = slow attack AGC // gc_rx2_mode *** adi,gc-rx2-mode
+ 58, // magic AGC setting, see AD9361 docs // gc_adc_large_overload_thresh *** adi,gc-adc-large-overload-thresh
+ 4, // magic AGC setting, see AD9361 docs // gc_adc_ovr_sample_size *** adi,gc-adc-ovr-sample-size
+ 47, // magic AGC setting, see AD9361 docs // gc_adc_small_overload_thresh *** adi,gc-adc-small-overload-thresh
+ 8192, // magic AGC setting, see AD9361 docs // gc_dec_pow_measurement_duration *** adi,gc-dec-pow-measurement-duration
+ 0, // magic AGC setting, see AD9361 docs // gc_dig_gain_enable *** adi,gc-dig-gain-enable
+ 800, // magic AGC setting, see AD9361 docs // gc_lmt_overload_high_thresh *** adi,gc-lmt-overload-high-thresh
+ 704, // magic AGC setting, see AD9361 docs // gc_lmt_overload_low_thresh *** adi,gc-lmt-overload-low-thresh
+ 24, // magic AGC setting, see AD9361 docs // gc_low_power_thresh *** adi,gc-low-power-thresh
+ 15, // magic AGC setting, see AD9361 docs // gc_max_dig_gain *** adi,gc-max-dig-gain
+
+ /* Gain MGC Control */
+ 2, // N/A when mgc_rx(1,2)_ctrl_inp_enable = 0 // mgc_dec_gain_step *** adi,mgc-dec-gain-step
+ 2, // N/A when mgc_rx(1,2)_ctrl_inp_enable = 0 // mgc_inc_gain_step *** adi,mgc-inc-gain-step
+ 0, // don't use CTRL_IN for RX1 MGC stepping // mgc_rx1_ctrl_inp_enable *** adi,mgc-rx1-ctrl-inp-enable
+ 0, // don't use CTRL_IN for RX2 MGC stepping // mgc_rx2_ctrl_inp_enable *** adi,mgc-rx2-ctrl-inp-enable
+ 0, // N/A when mgc_rx(1,2)_ctrl_inp_enable = 0 // mgc_split_table_ctrl_inp_gain_mode *** adi,mgc-split-table-ctrl-inp-gain-mode
+
+ /* Gain AGC Control */
+ 10, // magic AGC setting, see AD9361 docs // agc_adc_large_overload_exceed_counter *** adi,agc-adc-large-overload-exceed-counter
+ 2, // magic AGC setting, see AD9361 docs // agc_adc_large_overload_inc_steps *** adi,agc-adc-large-overload-inc-steps
+ 0, // magic AGC setting, see AD9361 docs // agc_adc_lmt_small_overload_prevent_gain_inc_enable *** adi,agc-adc-lmt-small-overload-prevent-gain-inc-enable
+ 10, // magic AGC setting, see AD9361 docs // agc_adc_small_overload_exceed_counter *** adi,agc-adc-small-overload-exceed-counter
+ 4, // magic AGC setting, see AD9361 docs // agc_dig_gain_step_size *** adi,agc-dig-gain-step-size
+ 3, // magic AGC setting, see AD9361 docs // agc_dig_saturation_exceed_counter *** adi,agc-dig-saturation-exceed-counter
+ 1000, // magic AGC setting, see AD9361 docs // agc_gain_update_interval_us *** adi,agc-gain-update-interval-us
+ 0, // magic AGC setting, see AD9361 docs // agc_immed_gain_change_if_large_adc_overload_enable *** adi,agc-immed-gain-change-if-large-adc-overload-enable
+ 0, // magic AGC setting, see AD9361 docs // agc_immed_gain_change_if_large_lmt_overload_enable *** adi,agc-immed-gain-change-if-large-lmt-overload-enable
+ 10, // magic AGC setting, see AD9361 docs // agc_inner_thresh_high *** adi,agc-inner-thresh-high
+ 1, // magic AGC setting, see AD9361 docs // agc_inner_thresh_high_dec_steps *** adi,agc-inner-thresh-high-dec-steps
+ 12, // magic AGC setting, see AD9361 docs // agc_inner_thresh_low *** adi,agc-inner-thresh-low
+ 1, // magic AGC setting, see AD9361 docs // agc_inner_thresh_low_inc_steps *** adi,agc-inner-thresh-low-inc-steps
+ 10, // magic AGC setting, see AD9361 docs // agc_lmt_overload_large_exceed_counter *** adi,agc-lmt-overload-large-exceed-counter
+ 2, // magic AGC setting, see AD9361 docs // agc_lmt_overload_large_inc_steps *** adi,agc-lmt-overload-large-inc-steps
+ 10, // magic AGC setting, see AD9361 docs // agc_lmt_overload_small_exceed_counter *** adi,agc-lmt-overload-small-exceed-counter
+ 5, // magic AGC setting, see AD9361 docs // agc_outer_thresh_high *** adi,agc-outer-thresh-high
+ 2, // magic AGC setting, see AD9361 docs // agc_outer_thresh_high_dec_steps *** adi,agc-outer-thresh-high-dec-steps
+ 18, // magic AGC setting, see AD9361 docs // agc_outer_thresh_low *** adi,agc-outer-thresh-low
+ 2, // magic AGC setting, see AD9361 docs // agc_outer_thresh_low_inc_steps *** adi,agc-outer-thresh-low-inc-steps
+ 1, // magic AGC setting, see AD9361 docs // agc_attack_delay_extra_margin_us; *** adi,agc-attack-delay-extra-margin-us
+ 0, // magic AGC setting, see AD9361 docs // agc_sync_for_gain_counter_enable *** adi,agc-sync-for-gain-counter-enable
+
+ /* Fast AGC */
+ 64, // magic AGC setting, see AD9361 docs // fagc_dec_pow_measuremnt_duration *** adi,fagc-dec-pow-measurement-duration
+ 260, // magic AGC setting, see AD9361 docs // fagc_state_wait_time_ns *** adi,fagc-state-wait-time-ns
+
+ /* Fast AGC - Low Power */
+ 0, // magic AGC setting, see AD9361 docs // fagc_allow_agc_gain_increase *** adi,fagc-allow-agc-gain-increase-enable
+ 5, // magic AGC setting, see AD9361 docs // fagc_lp_thresh_increment_time *** adi,fagc-lp-thresh-increment-time
+ 1, // magic AGC setting, see AD9361 docs // fagc_lp_thresh_increment_steps *** adi,fagc-lp-thresh-increment-steps
+
+ /* Fast AGC - Lock Level */
+ 10, // magic AGC setting, see AD9361 docs // fagc_lock_level *** adi,fagc-lock-level
+ 1, // magic AGC setting, see AD9361 docs // fagc_lock_level_lmt_gain_increase_en *** adi,fagc-lock-level-lmt-gain-increase-enable
+ 5, // magic AGC setting, see AD9361 docs // fagc_lock_level_gain_increase_upper_limit *** adi,fagc-lock-level-gain-increase-upper-limit
+
+ /* Fast AGC - Peak Detectors and Final Settling */
+ 1, // magic AGC setting, see AD9361 docs // fagc_lpf_final_settling_steps *** adi,fagc-lpf-final-settling-steps
+ 1, // magic AGC setting, see AD9361 docs // fagc_lmt_final_settling_steps *** adi,fagc-lmt-final-settling-steps
+ 3, // magic AGC setting, see AD9361 docs // fagc_final_overrange_count *** adi,fagc-final-overrange-count
+
+ /* Fast AGC - Final Power Test */
+ 0, // magic AGC setting, see AD9361 docs // fagc_gain_increase_after_gain_lock_en *** adi,fagc-gain-increase-after-gain-lock-enable
+
+ /* Fast AGC - Unlocking the Gain */
+ 0, // magic AGC setting, see AD9361 docs // fagc_gain_index_type_after_exit_rx_mode *** adi,fagc-gain-index-type-after-exit-rx-mode
+ 1, // magic AGC setting, see AD9361 docs // fagc_use_last_lock_level_for_set_gain_en *** adi,fagc-use-last-lock-level-for-set-gain-enable
+ 1, // magic AGC setting, see AD9361 docs // fagc_rst_gla_stronger_sig_thresh_exceeded_en *** adi,fagc-rst-gla-stronger-sig-thresh-exceeded-enable
+ 5, // magic AGC setting, see AD9361 docs // fagc_optimized_gain_offset *** adi,fagc-optimized-gain-offset
+ 10, // magic AGC setting, see AD9361 docs // fagc_rst_gla_stronger_sig_thresh_above_ll *** adi,fagc-rst-gla-stronger-sig-thresh-above-ll
+ 1, // magic AGC setting, see AD9361 docs // fagc_rst_gla_engergy_lost_sig_thresh_exceeded_en *** adi,fagc-rst-gla-engergy-lost-sig-thresh-exceeded-enable
+ 1, // magic AGC setting, see AD9361 docs // fagc_rst_gla_engergy_lost_goto_optim_gain_en *** adi,fagc-rst-gla-engergy-lost-goto-optim-gain-enable
+ 10, // magic AGC setting, see AD9361 docs // fagc_rst_gla_engergy_lost_sig_thresh_below_ll *** adi,fagc-rst-gla-engergy-lost-sig-thresh-below-ll
+ 8, // magic AGC setting, see AD9361 docs // fagc_energy_lost_stronger_sig_gain_lock_exit_cnt *** adi,fagc-energy-lost-stronger-sig-gain-lock-exit-cnt
+ 1, // magic AGC setting, see AD9361 docs // fagc_rst_gla_large_adc_overload_en *** adi,fagc-rst-gla-large-adc-overload-enable
+ 1, // magic AGC setting, see AD9361 docs // fagc_rst_gla_large_lmt_overload_en *** adi,fagc-rst-gla-large-lmt-overload-enable
+ 0, // magic AGC setting, see AD9361 docs // fagc_rst_gla_en_agc_pulled_high_en *** adi,fagc-rst-gla-en-agc-pulled-high-enable
+ 0, // magic AGC setting, see AD9361 docs // fagc_rst_gla_if_en_agc_pulled_high_mode *** adi,fagc-rst-gla-if-en-agc-pulled-high-mode
+ 64, // magic AGC setting, see AD9361 docs // fagc_power_measurement_duration_in_state5 *** adi,fagc-power-measurement-duration-in-state5
+
+ /* RSSI Control */
+ 1, // settling delay on RSSI algo restart = 1 μs // rssi_delay *** adi,rssi-delay
+ 1000, // total RSSI measurement duration = 1000 μs // rssi_duration *** adi,rssi-duration
+ GAIN_CHANGE_OCCURS, // reset RSSI accumulator on gain change event // rssi_restart_mode *** adi,rssi-restart-mode
+ 0, // RSSI control values are in microseconds // rssi_unit_is_rx_samples_enable *** adi,rssi-unit-is-rx-samples-enable
+ 1, // wait 1 μs between RSSI measurements // rssi_wait *** adi,rssi-wait
+
+ /* Aux ADC Control */
+ /* bladeRF Micro: N/A, pin tied to GND */
+ 256, // AuxADC decimate by 256 // aux_adc_decimation *** adi,aux-adc-decimation
+ 40000000UL, // AuxADC sample rate 40 MHz // aux_adc_rate *** adi,aux-adc-rate
+
+ /* AuxDAC Control */
+ /* bladeRF Micro: AuxDAC1 is TP7 and AUXDAC_TRIM, AuxDAC2 is TP8 */
+ 1, // AuxDAC does not slave the ENSM // aux_dac_manual_mode_enable *** adi,aux-dac-manual-mode-enable
+ 0, // AuxDAC1 default value = 0 mV // aux_dac1_default_value_mV *** adi,aux-dac1-default-value-mV
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac1_active_in_rx_enable *** adi,aux-dac1-active-in-rx-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac1_active_in_tx_enable *** adi,aux-dac1-active-in-tx-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac1_active_in_alert_enable *** adi,aux-dac1-active-in-alert-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac1_rx_delay_us *** adi,aux-dac1-rx-delay-us
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac1_tx_delay_us *** adi,aux-dac1-tx-delay-us
+ 0, // AuxDAC2 default value = 0 mV // aux_dac2_default_value_mV *** adi,aux-dac2-default-value-mV
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac2_active_in_rx_enable *** adi,aux-dac2-active-in-rx-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac2_active_in_tx_enable *** adi,aux-dac2-active-in-tx-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac2_active_in_alert_enable *** adi,aux-dac2-active-in-alert-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac2_rx_delay_us *** adi,aux-dac2-rx-delay-us
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac2_tx_delay_us *** adi,aux-dac2-tx-delay-us
+
+ /* Temperature Sensor Control */
+ 256, // Temperature sensor decimate by 256 // temp_sense_decimation *** adi,temp-sense-decimation
+ 1000, // Measure temperature every 1000 ms // temp_sense_measurement_interval_ms *** adi,temp-sense-measurement-interval-ms
+ 206, // Offset = +206 degrees C // temp_sense_offset_signed *** adi,temp-sense-offset-signed
+ 1, // Periodic temperature measurements enabled // temp_sense_periodic_measurement_enable *** adi,temp-sense-periodic-measurement-enable
+
+ /* Control Out Setup */
+ /* See https://wiki.analog.com/resources/tools-software/linux-drivers/iio-transceiver/ad9361-customization#control_output_setup */
+ 0xFF, // Enable all CTRL_OUT bits // ctrl_outs_enable_mask *** adi,ctrl-outs-enable-mask
+ 0, // CTRL_OUT index is 0 // ctrl_outs_index *** adi,ctrl-outs-index
+
+ /* External LNA Control */
+ /* bladeRF Micro: GPO_0 is TP3, GPO_1 is TP4 */
+ 0, // N/A when elna_rx(1,2)_gpo(0,1)_control_enable = 0 // elna_settling_delay_ns *** adi,elna-settling-delay-ns
+ 0, // MUST be 0 when elna_rx(1,2)_gpo(0,1)_control_enable = 0 // elna_gain_mdB *** adi,elna-gain-mdB
+ 0, // MUST be 0 when elna_rx(1,2)_gpo(0,1)_control_enable = 0 // elna_bypass_loss_mdB *** adi,elna-bypass-loss-mdB
+ 0, // Ext LNA Ctrl bit in Rx1 gain table does NOT set GPO0 state // elna_rx1_gpo0_control_enable *** adi,elna-rx1-gpo0-control-enable
+ 0, // Ext LNA Ctrl bit in Rx2 gain table does NOT set GPO1 state // elna_rx2_gpo1_control_enable *** adi,elna-rx2-gpo1-control-enable
+ 0, // N/A when elna_rx(1,2)_gpo(0,1)_control_enable = 0 // elna_gaintable_all_index_enable *** adi,elna-gaintable-all-index-enable
+
+ /* Digital Interface Control */
+#ifdef ENABLE_AD9361_DIGITAL_INTERFACE_TIMING_VERIFICATION
+ /* Calibrate the digital interface delay (hardware validation) */
+ 0, // Don't skip digital interface tuning // digital_interface_tune_skip_mode *** adi,digital-interface-tune-skip-mode
+#else
+ /* Use hardcoded digital interface delay values (production) */
+ 2, // Skip RX and TX tuning; use hardcoded values below // digital_interface_tune_skip_mode *** adi,digital-interface-tune-skip-mode
+#endif // ENABLE_AD9361_DIGITAL_INTERFACE_TIMING_VERIFICATION
+ 0, // ?? UNDOCUMENTED ?? // digital_interface_tune_fir_disable *** adi,digital-interface-tune-fir-disable
+ 1, // Swap I and Q (spectral inversion) // pp_tx_swap_enable *** adi,pp-tx-swap-enable
+ 1, // Swap I and Q (spectral inversion) // pp_rx_swap_enable *** adi,pp-rx-swap-enable
+ 0, // Don't swap TX1 and TX2 // tx_channel_swap_enable *** adi,tx-channel-swap-enable
+ 0, // Don't swap RX1 and RX2 // rx_channel_swap_enable *** adi,rx-channel-swap-enable
+ 1, // Toggle RX_FRAME with 50% duty cycle // rx_frame_pulse_mode_enable *** adi,rx-frame-pulse-mode-enable
+ 0, // Data port timing reflects # of enabled signal paths // two_t_two_r_timing_enable *** adi,2t2r-timing-enable
+ 0, // Don't invert data bus // invert_data_bus_enable *** adi,invert-data-bus-enable
+ 0, // Don't invert data clock // invert_data_clk_enable *** adi,invert-data-clk-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // fdd_alt_word_order_enable *** adi,fdd-alt-word-order-enable
+ 0, // Don't invert RX_FRAME // invert_rx_frame_enable *** adi,invert-rx-frame-enable
+ 0, // Don't make RX sample rate 2x the TX sample rate // fdd_rx_rate_2tx_enable *** adi,fdd-rx-rate-2tx-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // swap_ports_enable *** adi,swap-ports-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // single_data_rate_enable *** adi,single-data-rate-enable
+ 1, // Use LVDS mode on data port // lvds_mode_enable *** adi,lvds-mode-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // half_duplex_mode_enable *** adi,half-duplex-mode-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // single_port_mode_enable *** adi,single-port-mode-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // full_port_enable *** adi,full-port-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // full_duplex_swap_bits_enable *** adi,full-duplex-swap-bits-enable
+ 0, // RX_DATA delay rel to RX_FRAME = 0 DATA_CLK/2 cycles // delay_rx_data *** adi,delay-rx-data
+ // Approx 0.3 ns/LSB on next 4 values
+ 5, // DATA_CLK delay = 1.5 ns // rx_data_clock_delay *** adi,rx-data-clock-delay
+ 0, // RX_DATA/RX_FRAME delay = 0 ns // rx_data_delay *** adi,rx-data-delay
+ 0, // FB_CLK delay = 0 ns // tx_fb_clock_delay *** adi,tx-fb-clock-delay
+ 5, // TX_DATA/TX_FRAME delay = 1.5 ns // tx_data_delay *** adi,tx-data-delay
+ 300, // LVDS driver bias 300 mV // lvds_bias_mV *** adi,lvds-bias-mV
+ 1, // Enable LVDS on-chip termination // lvds_rx_onchip_termination_enable *** adi,lvds-rx-onchip-termination-enable
+ 1, // RX1 and RX2 are not phase-aligned // rx1rx2_phase_inversion_en *** adi,rx1-rx2-phase-inversion-enable
+ 0xFF, // Default signal inversion mappings // lvds_invert1_control *** adi,lvds-invert1-control
+ 0x0F, // Default signal inversion mappings // lvds_invert2_control *** adi,lvds-invert2-control
+ 1, // CLK_OUT drive increased by ~20% // clk_out_drive
+ 1, // DATA_CLK drive increased by ~20% // dataclk_drive
+ 1, // Data port drive increased by ~20% // data_port_drive
+ 0, // CLK_OUT minimum slew (fastest rise/fall) // clk_out_slew
+ 0, // DATA_CLK minimum slew (fastest rise/fall) // dataclk_slew
+ 0, // Data port minimum slew (fastest rise/fall) // data_port_slew
+
+ /* GPO Control */
+ 0, // GPO0 is LOW in Sleep/Wait/Alert states // gpo0_inactive_state_high_enable *** adi,gpo0-inactive-state-high-enable
+ 0, // GPO1 is LOW in Sleep/Wait/Alert states // gpo1_inactive_state_high_enable *** adi,gpo1-inactive-state-high-enable
+ 0, // GPO2 is LOW in Sleep/Wait/Alert states // gpo2_inactive_state_high_enable *** adi,gpo2-inactive-state-high-enable
+ 0, // GPO3 is LOW in Sleep/Wait/Alert states // gpo3_inactive_state_high_enable *** adi,gpo3-inactive-state-high-enable
+ 0, // GPO0 does not change state when entering RX state // gpo0_slave_rx_enable *** adi,gpo0-slave-rx-enable
+ 0, // GPO0 does not change state when entering TX state // gpo0_slave_tx_enable *** adi,gpo0-slave-tx-enable
+ 0, // GPO1 does not change state when entering RX state // gpo1_slave_rx_enable *** adi,gpo1-slave-rx-enable
+ 0, // GPO1 does not change state when entering TX state // gpo1_slave_tx_enable *** adi,gpo1-slave-tx-enable
+ 0, // GPO2 does not change state when entering RX state // gpo2_slave_rx_enable *** adi,gpo2-slave-rx-enable
+ 0, // GPO2 does not change state when entering TX state // gpo2_slave_tx_enable *** adi,gpo2-slave-tx-enable
+ 0, // GPO3 does not change state when entering RX state // gpo3_slave_rx_enable *** adi,gpo3-slave-rx-enable
+ 0, // GPO3 does not change state when entering TX state // gpo3_slave_tx_enable *** adi,gpo3-slave-tx-enable
+ 0, // N/A when gpo0_slave_rx_enable = 0 // gpo0_rx_delay_us *** adi,gpo0-rx-delay-us
+ 0, // N/A when gpo0_slave_tx_enable = 0 // gpo0_tx_delay_us *** adi,gpo0-tx-delay-us
+ 0, // N/A when gpo1_slave_rx_enable = 0 // gpo1_rx_delay_us *** adi,gpo1-rx-delay-us
+ 0, // N/A when gpo1_slave_tx_enable = 0 // gpo1_tx_delay_us *** adi,gpo1-tx-delay-us
+ 0, // N/A when gpo2_slave_rx_enable = 0 // gpo2_rx_delay_us *** adi,gpo2-rx-delay-us
+ 0, // N/A when gpo2_slave_tx_enable = 0 // gpo2_tx_delay_us *** adi,gpo2-tx-delay-us
+ 0, // N/A when gpo3_slave_rx_enable = 0 // gpo3_rx_delay_us *** adi,gpo3-rx-delay-us
+ 0, // N/A when gpo3_slave_tx_enable = 0 // gpo3_tx_delay_us *** adi,gpo3-tx-delay-us
+
+ /* Tx Monitor Control */
+ /* bladeRF Micro: N/A, TX_MON1 and TX_MON2 tied to GND */
+ 37000, // N/A // low_high_gain_threshold_mdB *** adi,txmon-low-high-thresh
+ 0, // N/A // low_gain_dB *** adi,txmon-low-gain
+ 24, // N/A // high_gain_dB *** adi,txmon-high-gain
+ 0, // N/A // tx_mon_track_en *** adi,txmon-dc-tracking-enable
+ 0, // N/A // one_shot_mode_en *** adi,txmon-one-shot-mode-enable
+ 511, // N/A // tx_mon_delay *** adi,txmon-delay
+ 8192, // N/A // tx_mon_duration *** adi,txmon-duration
+ 2, // N/A // tx1_mon_front_end_gain *** adi,txmon-1-front-end-gain
+ 2, // N/A // tx2_mon_front_end_gain *** adi,txmon-2-front-end-gain
+ 48, // N/A // tx1_mon_lo_cm *** adi,txmon-1-lo-cm
+ 48, // N/A // tx2_mon_lo_cm *** adi,txmon-2-lo-cm
+
+ /* GPIO definitions */
+ RFFE_CONTROL_RESET_N, // Reset using RFFE bit 0 // gpio_resetb *** reset-gpios
+
+ /* MCS Sync */
+ -1, // Future use (MCS Sync) // gpio_sync *** sync-gpios
+ -1, // Future use (MCS Sync) // gpio_cal_sw1 *** cal-sw1-gpios
+ -1, // Future use (MCS Sync) // gpio_cal_sw2 *** cal-sw2-gpios
+
+ /* External LO clocks */
+ NULL, // Future use (RX_EXT_LO, TX_EXT_LO control) // (*ad9361_rfpll_ext_recalc_rate)()
+ NULL, // Future use (RX_EXT_LO, TX_EXT_LO control) // (*ad9361_rfpll_ext_round_rate)()
+ NULL // Future use (RX_EXT_LO, TX_EXT_LO control) // (*ad9361_rfpll_ext_set_rate)()
+};
+// clang-format on
+
+
+// clang-format off
+AD9361_InitParam bladerf2_rfic_init_params_fastagc_burst = {
+ /* Device selection */
+ ID_AD9361, // AD9361 RF Agile Transceiver // dev_sel
+
+ /* Identification number */
+ 0, // Chip ID 0 // id_no
+
+ /* Reference Clock */
+ 38400000UL, // RefClk = 38.4 MHz // reference_clk_rate
+
+ /* Base Configuration */
+ 1, // use 2Rx2Tx mode // two_rx_two_tx_mode_enable *** adi,2rx-2tx-mode-enable
+ 1, // N/A when two_rx_two_tx_mode_enable = 1 // one_rx_one_tx_mode_use_rx_num *** adi,1rx-1tx-mode-use-rx-num
+ 1, // N/A when two_rx_two_tx_mode_enable = 1 // one_rx_one_tx_mode_use_tx_num *** adi,1rx-1tx-mode-use-tx-num
+ 1, // use FDD mode // frequency_division_duplex_mode_enable *** adi,frequency-division-duplex-mode-enable
+ 1, // use independent FDD mode // frequency_division_duplex_independent_mode_enable *** adi,frequency-division-duplex-independent-mode-enable
+ 0, // N/A when frequency_division_duplex_mode_enable = 1 // tdd_use_dual_synth_mode_enable *** adi,tdd-use-dual-synth-mode-enable
+ 0, // N/A when frequency_division_duplex_mode_enable = 1 // tdd_skip_vco_cal_enable *** adi,tdd-skip-vco-cal-enable
+ 0, // TX fastlock delay = 0 ns // tx_fastlock_delay_ns *** adi,tx-fastlock-delay-ns
+ 0, // RX fastlock delay = 0 ns // rx_fastlock_delay_ns *** adi,rx-fastlock-delay-ns
+ 0, // RX fastlock pin control disabled // rx_fastlock_pincontrol_enable *** adi,rx-fastlock-pincontrol-enable
+ 0, // TX fastlock pin control disabled // tx_fastlock_pincontrol_enable *** adi,tx-fastlock-pincontrol-enable
+ 0, // use internal RX LO // external_rx_lo_enable *** adi,external-rx-lo-enable
+ 0, // use internal TX LO // external_tx_lo_enable *** adi,external-tx-lo-enable
+ 5, // apply new tracking word: on gain change, after exiting RX state // dc_offset_tracking_update_event_mask *** adi,dc-offset-tracking-update-event-mask
+ 6, // atten value for DC tracking, RX LO > 4 GHz // dc_offset_attenuation_high_range *** adi,dc-offset-attenuation-high-range
+ 5, // atten value for DC tracking, RX LO < 4 GHz // dc_offset_attenuation_low_range *** adi,dc-offset-attenuation-low-range
+ 0x28, // loop gain for DC tracking, RX LO > 4 GHz // dc_offset_count_high_range *** adi,dc-offset-count-high-range
+ 0x32, // loop gain for DC tracking, RX LO < 4 GHz // dc_offset_count_low_range *** adi,dc-offset-count-low-range
+ 0, // use full gain table // split_gain_table_mode_enable *** adi,split-gain-table-mode-enable
+ MAX_SYNTH_FREF, // f_ref window 80 MHz // trx_synthesizer_target_fref_overwrite_hz *** adi,trx-synthesizer-target-fref-overwrite-hz
+ 0, // don't use improved RX QEC tracking // qec_tracking_slow_mode_enable *** adi,qec-tracking-slow-mode-enable
+
+ /* ENSM Control */
+ 0, // use level mode on ENABLE and TXNRX pins // ensm_enable_pin_pulse_mode_enable *** adi,ensm-enable-pin-pulse-mode-enable
+ 0, // use SPI writes for ENSM state, not ENABLE/TXNRX pins // ensm_enable_txnrx_control_enable *** adi,ensm-enable-txnrx-control-enable
+
+ /* LO Control */
+ 2400000000UL, // DEFAULT // rx_synthesizer_frequency_hz *** adi,rx-synthesizer-frequency-hz
+ 2400000000UL, // DEFAULT // tx_synthesizer_frequency_hz *** adi,tx-synthesizer-frequency-hz
+
+ /* Rate & BW Control */
+ { 983040000, 245760000, 122880000, 61440000, 30720000, 30720000 }, // DEFAULT // uint32_t rx_path_clock_frequencies[6] *** adi,rx-path-clock-frequencies
+ { 983040000, 122880000, 122880000, 61440000, 30720000, 30720000 }, // DEFAULT // uint32_t tx_path_clock_frequencies[6] *** adi,tx-path-clock-frequencies
+ 18000000, // DEFAULT // rf_rx_bandwidth_hz *** adi,rf-rx-bandwidth-hz
+ 18000000, // DEFAULT // rf_tx_bandwidth_hz *** adi,rf-tx-bandwidth-hz
+
+ /* RF Port Control */
+ 0, // DEFAULT // rx_rf_port_input_select *** adi,rx-rf-port-input-select
+ 0, // DEFAULT // tx_rf_port_input_select *** adi,tx-rf-port-input-select
+
+ /* TX Attenuation Control */
+ 10000, // DEFAULT // tx_attenuation_mdB *** adi,tx-attenuation-mdB
+ 0, // N/A when frequency_division_duplex_mode_enable = 1 // update_tx_gain_in_alert_enable *** adi,update-tx-gain-in-alert-enable
+
+ /* Reference Clock Control */
+ 1, // Expect external clock into XTALN // xo_disable_use_ext_refclk_enable *** adi,xo-disable-use-ext-refclk-enable
+ {3, 5920}, // ~0 ppm DCXO trim (N/A if ext clk) // dcxo_coarse_and_fine_tune[2] *** adi,dcxo-coarse-and-fine-tune
+ CLKOUT_DISABLE, // disable clkout pin (see enum ad9361_clkout) // clk_output_mode_select *** adi,clk-output-mode-select
+
+ /* Gain Control */
+ RF_GAIN_FASTATTACK_AGC, // RX1 BLADERF_GAIN_DEFAULT = slow attack AGC // gc_rx1_mode *** adi,gc-rx1-mode
+ RF_GAIN_FASTATTACK_AGC, // RX2 BLADERF_GAIN_DEFAULT = slow attack AGC // gc_rx2_mode *** adi,gc-rx2-mode
+ 58, // magic AGC setting, see AD9361 docs // gc_adc_large_overload_thresh *** adi,gc-adc-large-overload-thresh
+ 4, // magic AGC setting, see AD9361 docs // gc_adc_ovr_sample_size *** adi,gc-adc-ovr-sample-size
+ 47, // magic AGC setting, see AD9361 docs // gc_adc_small_overload_thresh *** adi,gc-adc-small-overload-thresh
+ 2, // magic AGC setting, see AD9361 docs // gc_dec_pow_measurement_duration *** adi,gc-dec-pow-measurement-duration
+ 0, // magic AGC setting, see AD9361 docs // gc_dig_gain_enable *** adi,gc-dig-gain-enable
+ 480, // magic AGC setting, see AD9361 docs // gc_lmt_overload_high_thresh *** adi,gc-lmt-overload-high-thresh
+ 400, // magic AGC setting, see AD9361 docs // gc_lmt_overload_low_thresh *** adi,gc-lmt-overload-low-thresh
+ 40, // magic AGC setting, see AD9361 docs // gc_low_power_thresh *** adi,gc-low-power-thresh
+ 15, // magic AGC setting, see AD9361 docs // gc_max_dig_gain *** adi,gc-max-dig-gain
+
+ /* Gain MGC Control */
+ 2, // N/A when mgc_rx(1,2)_ctrl_inp_enable = 0 // mgc_dec_gain_step *** adi,mgc-dec-gain-step
+ 2, // N/A when mgc_rx(1,2)_ctrl_inp_enable = 0 // mgc_inc_gain_step *** adi,mgc-inc-gain-step
+ 0, // don't use CTRL_IN for RX1 MGC stepping // mgc_rx1_ctrl_inp_enable *** adi,mgc-rx1-ctrl-inp-enable
+ 0, // don't use CTRL_IN for RX2 MGC stepping // mgc_rx2_ctrl_inp_enable *** adi,mgc-rx2-ctrl-inp-enable
+ 0, // N/A when mgc_rx(1,2)_ctrl_inp_enable = 0 // mgc_split_table_ctrl_inp_gain_mode *** adi,mgc-split-table-ctrl-inp-gain-mode
+
+ /* Gain AGC Control */
+ 10, // magic AGC setting, see AD9361 docs // agc_adc_large_overload_exceed_counter *** adi,agc-adc-large-overload-exceed-counter
+ 2, // magic AGC setting, see AD9361 docs // agc_adc_large_overload_inc_steps *** adi,agc-adc-large-overload-inc-steps
+ 0, // magic AGC setting, see AD9361 docs // agc_adc_lmt_small_overload_prevent_gain_inc_enable *** adi,agc-adc-lmt-small-overload-prevent-gain-inc-enable
+ 10, // magic AGC setting, see AD9361 docs // agc_adc_small_overload_exceed_counter *** adi,agc-adc-small-overload-exceed-counter
+ 4, // magic AGC setting, see AD9361 docs // agc_dig_gain_step_size *** adi,agc-dig-gain-step-size
+ 3, // magic AGC setting, see AD9361 docs // agc_dig_saturation_exceed_counter *** adi,agc-dig-saturation-exceed-counter
+ 1, // magic AGC setting, see AD9361 docs // agc_gain_update_interval_us *** adi,agc-gain-update-interval-us
+ 0, // magic AGC setting, see AD9361 docs // agc_immed_gain_change_if_large_adc_overload_enable *** adi,agc-immed-gain-change-if-large-adc-overload-enable
+ 0, // magic AGC setting, see AD9361 docs // agc_immed_gain_change_if_large_lmt_overload_enable *** adi,agc-immed-gain-change-if-large-lmt-overload-enable
+ 10, // magic AGC setting, see AD9361 docs // agc_inner_thresh_high *** adi,agc-inner-thresh-high
+ 1, // magic AGC setting, see AD9361 docs // agc_inner_thresh_high_dec_steps *** adi,agc-inner-thresh-high-dec-steps
+ 12, // magic AGC setting, see AD9361 docs // agc_inner_thresh_low *** adi,agc-inner-thresh-low
+ 1, // magic AGC setting, see AD9361 docs // agc_inner_thresh_low_inc_steps *** adi,agc-inner-thresh-low-inc-steps
+ 10, // magic AGC setting, see AD9361 docs // agc_lmt_overload_large_exceed_counter *** adi,agc-lmt-overload-large-exceed-counter
+ 2, // magic AGC setting, see AD9361 docs // agc_lmt_overload_large_inc_steps *** adi,agc-lmt-overload-large-inc-steps
+ 10, // magic AGC setting, see AD9361 docs // agc_lmt_overload_small_exceed_counter *** adi,agc-lmt-overload-small-exceed-counter
+ 5, // magic AGC setting, see AD9361 docs // agc_outer_thresh_high *** adi,agc-outer-thresh-high
+ 2, // magic AGC setting, see AD9361 docs // agc_outer_thresh_high_dec_steps *** adi,agc-outer-thresh-high-dec-steps
+ 18, // magic AGC setting, see AD9361 docs // agc_outer_thresh_low *** adi,agc-outer-thresh-low
+ 2, // magic AGC setting, see AD9361 docs // agc_outer_thresh_low_inc_steps *** adi,agc-outer-thresh-low-inc-steps
+ 1, // magic AGC setting, see AD9361 docs // agc_attack_delay_extra_margin_us; *** adi,agc-attack-delay-extra-margin-us
+ 0, // magic AGC setting, see AD9361 docs // agc_sync_for_gain_counter_enable *** adi,agc-sync-for-gain-counter-enable
+
+ /* Fast AGC */
+ 16, // magic AGC setting, see AD9361 docs // fagc_dec_pow_measuremnt_duration *** adi,fagc-dec-pow-measurement-duration
+ 260, // magic AGC setting, see AD9361 docs // fagc_state_wait_time_ns *** adi,fagc-state-wait-time-ns
+
+ /* Fast AGC - Low Power */
+ 0, // magic AGC setting, see AD9361 docs // fagc_allow_agc_gain_increase *** adi,fagc-allow-agc-gain-increase-enable
+ 5, // magic AGC setting, see AD9361 docs // fagc_lp_thresh_increment_time *** adi,fagc-lp-thresh-increment-time
+ 7, // magic AGC setting, see AD9361 docs // fagc.lp_thresh_increment_steps *** adi,fagc-lp-thresh-increment-steps
+
+ /* Fast AGC - Lock Level */
+ 10, // magic AGC setting, see AD9361 docs // fagc_lock_level *** adi,fagc-lock-level
+ 1, // magic AGC setting, see AD9361 docs // fagc_lock_level_lmt_gain_increase_en *** adi,fagc-lock-level-lmt-gain-increase-enable
+ 63, // magic AGC setting, see AD9361 docs // fagc_lock_level_gain_increase_upper_limit *** adi,fagc-lock-level-gain-increase-upper-limit
+
+ /* Fast AGC - Peak Detectors and Final Settling */
+ 1, // magic AGC setting, see AD9361 docs // fagc_lpf_final_settling_steps *** adi,fagc-lpf-final-settling-steps
+ 1, // magic AGC setting, see AD9361 docs // fagc_lmt_final_settling_steps *** adi,fagc-lmt-final-settling-steps
+ 3, // magic AGC setting, see AD9361 docs // fagc_final_overrange_count *** adi,fagc-final-overrange-count
+
+ /* Fast AGC - Final Power Test */
+ 0, // magic AGC setting, see AD9361 docs // fagc_gain_increase_after_gain_lock_en *** adi,fagc-gain-increase-after-gain-lock-enable
+
+ /* Fast AGC - Unlocking the Gain */
+ 0, // magic AGC setting, see AD9361 docs // fagc_gain_index_type_after_exit_rx_mode *** adi,fagc-gain-index-type-after-exit-rx-mode
+ 1, // magic AGC setting, see AD9361 docs // fagc_use_last_lock_level_for_set_gain_en *** adi,fagc-use-last-lock-level-for-set-gain-enable
+ 1, // magic AGC setting, see AD9361 docs // fagc_rst_gla_stronger_sig_thresh_exceeded_en *** adi,fagc-rst-gla-stronger-sig-thresh-exceeded-enable
+ 5, // magic AGC setting, see AD9361 docs // fagc_optimized_gain_offset *** adi,fagc-optimized-gain-offset
+ 10, // magic AGC setting, see AD9361 docs // fagc_rst_gla_stronger_sig_thresh_above_ll *** adi,fagc-rst-gla-stronger-sig-thresh-above-ll
+ 1, // magic AGC setting, see AD9361 docs // fagc_rst_gla_engergy_lost_sig_thresh_exceeded_en *** adi,fagc-rst-gla-engergy-lost-sig-thresh-exceeded-enable
+ 0, // magic AGC setting, see AD9361 docs // fagc_rst_gla_engergy_lost_goto_optim_gain_en *** adi,fagc-rst-gla-engergy-lost-goto-optim-gain-enable
+ 10, // magic AGC setting, see AD9361 docs // fagc_rst_gla_engergy_lost_sig_thresh_below_ll *** adi,fagc-rst-gla-engergy-lost-sig-thresh-below-ll
+ 3, // magic AGC setting, see AD9361 docs // fagc_energy_lost_stronger_sig_gain_lock_exit_cnt *** adi,fagc-energy-lost-stronger-sig-gain-lock-exit-cnt
+ 1, // magic AGC setting, see AD9361 docs // fagc_rst_gla_large_adc_overload_en *** adi,fagc-rst-gla-large-adc-overload-enable
+ 1, // magic AGC setting, see AD9361 docs // fagc_rst_gla_large_lmt_overload_en *** adi,fagc-rst-gla-large-lmt-overload-enable
+ 0, // magic AGC setting, see AD9361 docs // fagc_rst_gla_en_agc_pulled_high_en *** adi,fagc-rst-gla-en-agc-pulled-high-enable
+ 0, // magic AGC setting, see AD9361 docs // fagc_rst_gla_if_en_agc_pulled_high_mode *** adi,fagc-rst-gla-if-en-agc-pulled-high-mode
+ 64, // magic AGC setting, see AD9361 docs // fagc_power_measurement_duration_in_state5 *** adi,fagc-power-measurement-duration-in-state5
+
+ /* RSSI Control */
+ 1, // settling delay on RSSI algo restart = 1 μs // rssi_delay *** adi,rssi-delay
+ 1000, // total RSSI measurement duration = 1000 μs // rssi_duration *** adi,rssi-duration
+ GAIN_CHANGE_OCCURS, // reset RSSI accumulator on gain change event // rssi_restart_mode *** adi,rssi-restart-mode
+ 0, // RSSI control values are in microseconds // rssi_unit_is_rx_samples_enable *** adi,rssi-unit-is-rx-samples-enable
+ 1, // wait 1 μs between RSSI measurements // rssi_wait *** adi,rssi-wait
+
+ /* Aux ADC Control */
+ /* bladeRF Micro: N/A, pin tied to GND */
+ 256, // AuxADC decimate by 256 // aux_adc_decimation *** adi,aux-adc-decimation
+ 40000000UL, // AuxADC sample rate 40 MHz // aux_adc_rate *** adi,aux-adc-rate
+
+ /* AuxDAC Control */
+ /* bladeRF Micro: AuxDAC1 is TP7 and AUXDAC_TRIM, AuxDAC2 is TP8 */
+ 1, // AuxDAC does not slave the ENSM // aux_dac_manual_mode_enable *** adi,aux-dac-manual-mode-enable
+ 0, // AuxDAC1 default value = 0 mV // aux_dac1_default_value_mV *** adi,aux-dac1-default-value-mV
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac1_active_in_rx_enable *** adi,aux-dac1-active-in-rx-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac1_active_in_tx_enable *** adi,aux-dac1-active-in-tx-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac1_active_in_alert_enable *** adi,aux-dac1-active-in-alert-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac1_rx_delay_us *** adi,aux-dac1-rx-delay-us
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac1_tx_delay_us *** adi,aux-dac1-tx-delay-us
+ 0, // AuxDAC2 default value = 0 mV // aux_dac2_default_value_mV *** adi,aux-dac2-default-value-mV
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac2_active_in_rx_enable *** adi,aux-dac2-active-in-rx-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac2_active_in_tx_enable *** adi,aux-dac2-active-in-tx-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac2_active_in_alert_enable *** adi,aux-dac2-active-in-alert-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac2_rx_delay_us *** adi,aux-dac2-rx-delay-us
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac2_tx_delay_us *** adi,aux-dac2-tx-delay-us
+
+ /* Temperature Sensor Control */
+ 256, // Temperature sensor decimate by 256 // temp_sense_decimation *** adi,temp-sense-decimation
+ 1000, // Measure temperature every 1000 ms // temp_sense_measurement_interval_ms *** adi,temp-sense-measurement-interval-ms
+ 206, // Offset = +206 degrees C // temp_sense_offset_signed *** adi,temp-sense-offset-signed
+ 1, // Periodic temperature measurements enabled // temp_sense_periodic_measurement_enable *** adi,temp-sense-periodic-measurement-enable
+
+ /* Control Out Setup */
+ /* See https://wiki.analog.com/resources/tools-software/linux-drivers/iio-transceiver/ad9361-customization#control_output_setup */
+ 0xFF, // Enable all CTRL_OUT bits // ctrl_outs_enable_mask *** adi,ctrl-outs-enable-mask
+ 7, // CTRL_OUT index is 0 // ctrl_outs_index *** adi,ctrl-outs-index
+
+ /* External LNA Control */
+ /* bladeRF Micro: GPO_0 is TP3, GPO_1 is TP4 */
+ 0, // N/A when elna_rx(1,2)_gpo(0,1)_control_enable = 0 // elna_settling_delay_ns *** adi,elna-settling-delay-ns
+ 0, // MUST be 0 when elna_rx(1,2)_gpo(0,1)_control_enable = 0 // elna_gain_mdB *** adi,elna-gain-mdB
+ 0, // MUST be 0 when elna_rx(1,2)_gpo(0,1)_control_enable = 0 // elna_bypass_loss_mdB *** adi,elna-bypass-loss-mdB
+ 0, // Ext LNA Ctrl bit in Rx1 gain table does NOT set GPO0 state // elna_rx1_gpo0_control_enable *** adi,elna-rx1-gpo0-control-enable
+ 0, // Ext LNA Ctrl bit in Rx2 gain table does NOT set GPO1 state // elna_rx2_gpo1_control_enable *** adi,elna-rx2-gpo1-control-enable
+ 0, // N/A when elna_rx(1,2)_gpo(0,1)_control_enable = 0 // elna_gaintable_all_index_enable *** adi,elna-gaintable-all-index-enable
+
+ /* Digital Interface Control */
+#ifdef ENABLE_AD9361_DIGITAL_INTERFACE_TIMING_VERIFICATION
+ /* Calibrate the digital interface delay (hardware validation) */
+ 0, // Don't skip digital interface tuning // digital_interface_tune_skip_mode *** adi,digital-interface-tune-skip-mode
+#else
+ /* Use hardcoded digital interface delay values (production) */
+ 2, // Skip RX and TX tuning; use hardcoded values below // digital_interface_tune_skip_mode *** adi,digital-interface-tune-skip-mode
+#endif // ENABLE_AD9361_DIGITAL_INTERFACE_TIMING_VERIFICATION
+ 0, // ?? UNDOCUMENTED ?? // digital_interface_tune_fir_disable *** adi,digital-interface-tune-fir-disable
+ 1, // Swap I and Q (spectral inversion) // pp_tx_swap_enable *** adi,pp-tx-swap-enable
+ 1, // Swap I and Q (spectral inversion) // pp_rx_swap_enable *** adi,pp-rx-swap-enable
+ 0, // Don't swap TX1 and TX2 // tx_channel_swap_enable *** adi,tx-channel-swap-enable
+ 0, // Don't swap RX1 and RX2 // rx_channel_swap_enable *** adi,rx-channel-swap-enable
+ 1, // Toggle RX_FRAME with 50% duty cycle // rx_frame_pulse_mode_enable *** adi,rx-frame-pulse-mode-enable
+ 0, // Data port timing reflects # of enabled signal paths // two_t_two_r_timing_enable *** adi,2t2r-timing-enable
+ 0, // Don't invert data bus // invert_data_bus_enable *** adi,invert-data-bus-enable
+ 0, // Don't invert data clock // invert_data_clk_enable *** adi,invert-data-clk-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // fdd_alt_word_order_enable *** adi,fdd-alt-word-order-enable
+ 0, // Don't invert RX_FRAME // invert_rx_frame_enable *** adi,invert-rx-frame-enable
+ 0, // Don't make RX sample rate 2x the TX sample rate // fdd_rx_rate_2tx_enable *** adi,fdd-rx-rate-2tx-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // swap_ports_enable *** adi,swap-ports-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // single_data_rate_enable *** adi,single-data-rate-enable
+ 1, // Use LVDS mode on data port // lvds_mode_enable *** adi,lvds-mode-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // half_duplex_mode_enable *** adi,half-duplex-mode-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // single_port_mode_enable *** adi,single-port-mode-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // full_port_enable *** adi,full-port-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // full_duplex_swap_bits_enable *** adi,full-duplex-swap-bits-enable
+ 0, // RX_DATA delay rel to RX_FRAME = 0 DATA_CLK/2 cycles // delay_rx_data *** adi,delay-rx-data
+ // Approx 0.3 ns/LSB on next 4 values
+ 5, // DATA_CLK delay = 1.5 ns // rx_data_clock_delay *** adi,rx-data-clock-delay
+ 0, // RX_DATA/RX_FRAME delay = 0 ns // rx_data_delay *** adi,rx-data-delay
+ 0, // FB_CLK delay = 0 ns // tx_fb_clock_delay *** adi,tx-fb-clock-delay
+ 5, // TX_DATA/TX_FRAME delay = 1.5 ns // tx_data_delay *** adi,tx-data-delay
+ 300, // LVDS driver bias 300 mV // lvds_bias_mV *** adi,lvds-bias-mV
+ 1, // Enable LVDS on-chip termination // lvds_rx_onchip_termination_enable *** adi,lvds-rx-onchip-termination-enable
+ 1, // RX1 and RX2 are not phase-aligned // rx1rx2_phase_inversion_en *** adi,rx1-rx2-phase-inversion-enable
+ 0xFF, // Default signal inversion mappings // lvds_invert1_control *** adi,lvds-invert1-control
+ 0x0F, // Default signal inversion mappings // lvds_invert2_control *** adi,lvds-invert2-control
+ 1, // CLK_OUT drive increased by ~20% // clk_out_drive
+ 1, // DATA_CLK drive increased by ~20% // dataclk_drive
+ 1, // Data port drive increased by ~20% // data_port_drive
+ 0, // CLK_OUT minimum slew (fastest rise/fall) // clk_out_slew
+ 0, // DATA_CLK minimum slew (fastest rise/fall) // dataclk_slew
+ 0, // Data port minimum slew (fastest rise/fall) // data_port_slew
+
+ /* GPO Control */
+ 0, // GPO0 is LOW in Sleep/Wait/Alert states // gpo0_inactive_state_high_enable *** adi,gpo0-inactive-state-high-enable
+ 0, // GPO1 is LOW in Sleep/Wait/Alert states // gpo1_inactive_state_high_enable *** adi,gpo1-inactive-state-high-enable
+ 0, // GPO2 is LOW in Sleep/Wait/Alert states // gpo2_inactive_state_high_enable *** adi,gpo2-inactive-state-high-enable
+ 0, // GPO3 is LOW in Sleep/Wait/Alert states // gpo3_inactive_state_high_enable *** adi,gpo3-inactive-state-high-enable
+ 0, // GPO0 does not change state when entering RX state // gpo0_slave_rx_enable *** adi,gpo0-slave-rx-enable
+ 0, // GPO0 does not change state when entering TX state // gpo0_slave_tx_enable *** adi,gpo0-slave-tx-enable
+ 0, // GPO1 does not change state when entering RX state // gpo1_slave_rx_enable *** adi,gpo1-slave-rx-enable
+ 0, // GPO1 does not change state when entering TX state // gpo1_slave_tx_enable *** adi,gpo1-slave-tx-enable
+ 0, // GPO2 does not change state when entering RX state // gpo2_slave_rx_enable *** adi,gpo2-slave-rx-enable
+ 0, // GPO2 does not change state when entering TX state // gpo2_slave_tx_enable *** adi,gpo2-slave-tx-enable
+ 0, // GPO3 does not change state when entering RX state // gpo3_slave_rx_enable *** adi,gpo3-slave-rx-enable
+ 0, // GPO3 does not change state when entering TX state // gpo3_slave_tx_enable *** adi,gpo3-slave-tx-enable
+ 0, // N/A when gpo0_slave_rx_enable = 0 // gpo0_rx_delay_us *** adi,gpo0-rx-delay-us
+ 0, // N/A when gpo0_slave_tx_enable = 0 // gpo0_tx_delay_us *** adi,gpo0-tx-delay-us
+ 0, // N/A when gpo1_slave_rx_enable = 0 // gpo1_rx_delay_us *** adi,gpo1-rx-delay-us
+ 0, // N/A when gpo1_slave_tx_enable = 0 // gpo1_tx_delay_us *** adi,gpo1-tx-delay-us
+ 0, // N/A when gpo2_slave_rx_enable = 0 // gpo2_rx_delay_us *** adi,gpo2-rx-delay-us
+ 0, // N/A when gpo2_slave_tx_enable = 0 // gpo2_tx_delay_us *** adi,gpo2-tx-delay-us
+ 0, // N/A when gpo3_slave_rx_enable = 0 // gpo3_rx_delay_us *** adi,gpo3-rx-delay-us
+ 0, // N/A when gpo3_slave_tx_enable = 0 // gpo3_tx_delay_us *** adi,gpo3-tx-delay-us
+
+ /* Tx Monitor Control */
+ /* bladeRF Micro: N/A, TX_MON1 and TX_MON2 tied to GND */
+ 37000, // N/A // low_high_gain_threshold_mdB *** adi,txmon-low-high-thresh
+ 0, // N/A // low_gain_dB *** adi,txmon-low-gain
+ 24, // N/A // high_gain_dB *** adi,txmon-high-gain
+ 0, // N/A // tx_mon_track_en *** adi,txmon-dc-tracking-enable
+ 0, // N/A // one_shot_mode_en *** adi,txmon-one-shot-mode-enable
+ 511, // N/A // tx_mon_delay *** adi,txmon-delay
+ 8192, // N/A // tx_mon_duration *** adi,txmon-duration
+ 2, // N/A // tx1_mon_front_end_gain *** adi,txmon-1-front-end-gain
+ 2, // N/A // tx2_mon_front_end_gain *** adi,txmon-2-front-end-gain
+ 48, // N/A // tx1_mon_lo_cm *** adi,txmon-1-lo-cm
+ 48, // N/A // tx2_mon_lo_cm *** adi,txmon-2-lo-cm
+
+ /* GPIO definitions */
+ RFFE_CONTROL_RESET_N, // Reset using RFFE bit 0 // gpio_resetb *** reset-gpios
+
+ /* MCS Sync */
+ -1, // Future use (MCS Sync) // gpio_sync *** sync-gpios
+ -1, // Future use (MCS Sync) // gpio_cal_sw1 *** cal-sw1-gpios
+ -1, // Future use (MCS Sync) // gpio_cal_sw2 *** cal-sw2-gpios
+
+ /* External LO clocks */
+ NULL, // Future use (RX_EXT_LO, TX_EXT_LO control) // (*ad9361_rfpll_ext_recalc_rate)()
+ NULL, // Future use (RX_EXT_LO, TX_EXT_LO control) // (*ad9361_rfpll_ext_round_rate)()
+ NULL // Future use (RX_EXT_LO, TX_EXT_LO control) // (*ad9361_rfpll_ext_set_rate)()
+};
+// clang-format on
+
+/**
+ * AD9361 FIR Filters
+ *
+ * The AD9361 RFIC provides programmable FIR filters on both the RX and TX
+ * paths.
+ *
+ * On TX, the signal path is:
+ *
+ * DIGITAL:
+ * [ Programmable TX FIR ] -> [ HB1 ] -> [ HB2 ] -> [ HB3/INT3 ] -> [ DAC ]
+ * ANALOG:
+ * [ DAC ] -> [ BB LPF ] -> [ Secondary LPF ] -> ...
+ *
+ * The Programmable TX FIR is a programmable polyphase FIR filter, which can
+ * interpolate by 1, 2, 4, or be bypassed. Taps are stored in 16-bit
+ * twos-complement. If interpolating by 1, there is a limit of 64 taps;
+ * otherwise, the limit is 128 taps.
+ *
+ * HB1 and HB2 are fixed-coefficient half-band interpolating filters, and can
+ * interpolate by 2 or be bypassed. HB3/INT3 is a fixed-coefficient
+ * interpolating filter, and can interpolate by 2 or 3, or be bypassed.
+ *
+ * BB LPF is a third-order Butterworth LPF, and the Secondary LPF is a
+ * single-pole low-pass filter. Both have programmable corner frequencies.
+ *
+ * On RX, the signal path is:
+ *
+ * ANALOG:
+ * ... -> [ TIA LPF ] -> [ BB LPF ] -> [ ADC ]
+ * DIGITAL:
+ * [ ADC ] -> [ HB3/DEC3 ] -> [ HB2 ] -> [ HB1 ] -> [ Programmable RX FIR ]
+ *
+ * The TIA LPF is a transimpedance amplifier which applies a single-pole
+ * low-pass filter, and the BB LPF is a third-order Butterworth low-pass filter.
+ * Both have programmable corner frequencies.
+ *
+ * HB3/DEC3 is a fixed-coefficient decimating filter, and can decimate by a
+ * factor of 2 or 3, or be bypassed. HB2 is a fixed-coefficient half-band
+ * decimating filter, and can decimate by a factor of 2 or be bypassed. HB1 is a
+ * fixed-coefficient half-band decimating filter, and can also decimate by a
+ * factor of 2 or be bypassed.
+ *
+ * The Programmable RX FIR filter is a programmable polyphase filter, which can
+ * decimate by a factor of 1, 2, or 4, or be bypassed. Similar to the TX FIR,
+ * taps are stored in 16-bit twos-complement. The maximum number of taps is
+ * limited to the ratio of the sample clock to the filter's output rate,
+ * multiplied by 16, up to a maximum of 128 taps. There is a fixed +6 dB gain,
+ * so the below RX filters are configured for a -6 dB gain to effect a net gain
+ * of 0 dB.
+ *
+ * In practice, the decimation/interpolation settings must match for both the RX
+ * and TX FIR filters. If they differ, TX quadrature calibration (and likely
+ * other calibrations) will fail.
+ *
+ *
+ * This file specifies four filters:
+ * bladerf2_rfic_rx_fir_config = decimate by 1 RX FIR
+ * bladerf2_rfic_tx_fir_config = interpolate by 1 TX FIR
+ * bladerf2_rfic_rx_fir_config_dec2 = decimate by 2 RX FIR
+ * bladerf2_rfic_tx_fir_config_int2 = interpolate by 2 TX FIR
+ *
+ * The first two (the 1x filters) are the default, and should provide reasonable
+ * performance under most circumstances. The other two filters are primarily
+ * intended for situations requiring a flatter TX spectrum, particularly when
+ * the ratio of sample rate to signal bandwidth is low.
+ */
+
+AD9361_RXFIRConfig bladerf2_rfic_rx_fir_config = {
+ 3, // rx (RX1 = 1, RX2 = 2, both = 3)
+ -6, // rx_gain (-12, -6, 0, or 6 dB)
+ 1, // rx_dec (decimate by 1, 2, or 4)
+
+ /**
+ * RX FIR Filter
+ * Built using https://github.com/analogdevicesinc/libad9361-iio
+ * Branch: filter_generation
+ * Commit: f749cef974f687f696226455dc7684277886cf3b
+ *
+ * This filter is intended to improve the flatness of the RX spectrum. It is
+ * a 64-tap, decimate-by-1 filter.
+ *
+ * Design parameters:
+ *
+ * fdp.Rdata = 30720000;
+ * fdp.RxTx = "Rx";
+ * fdp.Type = "Lowpass";
+ * fdp.DAC_div = 1;
+ * fdp.HB3 = 2;
+ * fdp.HB2 = 2;
+ * fdp.HB1 = 2;
+ * fdp.FIR = 1;
+ * fdp.PLL_mult = 4;
+ * fdp.converter_rate = 245760000;
+ * fdp.PLL_rate = 983040000;
+ * fdp.Fpass = fdp.Rdata*0.42;
+ * fdp.Fstop = fdp.Rdata*0.50;
+ * fdp.Fcenter = 0;
+ * fdp.Apass = 0.125;
+ * fdp.Astop = 85;
+ * fdp.phEQ = -1;
+ * fdp.wnom = 17920000;
+ * fdp.caldiv = 7;
+ * fdp.RFbw = 22132002;
+ * fdp.FIRdBmin = 0;
+ * fdp.int_FIR = 1;
+ */
+ // clang-format off
+ {
+ 0, 0, 0, 1, -1, 3, -6, 11,
+ -19, 33, -53, 84, -129, 193, -282, 404,
+ -565, 777, -1052, 1401, -1841, 2390, -3071, 3911,
+ -4947, 6230, -7833, 9888, -12416, 15624, -21140, 32767,
+ 32767, -21140, 15624, -12416, 9888, -7833, 6230, -4947,
+ 3911, -3071, 2390, -1841, 1401, -1052, 777, -565,
+ 404, -282, 193, -129, 84, -53, 33, -19,
+ 11, -6, 3, -1, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ }, // rx_coef[128]
+ // clang-format on
+ 64, // rx_coef_size
+ { 0, 0, 0, 0, 0, 0 }, // rx_path_clks[6]
+ 0 // rx_bandwidth
+};
+
+AD9361_TXFIRConfig bladerf2_rfic_tx_fir_config = {
+ 3, // tx (TX1 = 1, TX2 = 2, both = 3)
+ 0, // tx_gain (-6 or 0 dB)
+ 1, // tx_int (interpolate by 1, 2, or 4)
+
+ /**
+ * TX FIR Filter
+ *
+ * This filter literally does nothing, but it is here as a placeholder.
+ */
+ // clang-format off
+ {
+ 32767, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ }, // tx_coef[128]
+ // clang-format on
+ 64, // tx_coef_size
+ { 0, 0, 0, 0, 0, 0 }, // tx_path_clks[6]
+ 0 // tx_bandwidth
+};
+
+AD9361_RXFIRConfig bladerf2_rfic_rx_fir_config_dec2 = {
+ 3, // rx (RX1 = 1, RX2 = 2, both = 3)
+ -6, // rx_gain (-12, -6, 0, or 6 dB)
+ 2, // rx_dec (decimate by 1, 2, or 4)
+
+ /**
+ * RX FIR Filter
+ * Built using https://github.com/analogdevicesinc/libad9361-iio
+ * Branch: filter_generation
+ * Commit: f749cef974f687f696226455dc7684277886cf3b
+ *
+ * This filter is intended to improve the flatness of the RX spectrum.
+ *
+ * It is a 128-tap, decimate-by-2 filter. Note that you MUST use a
+ * interpolate-by-2 filter on TX if you are using this filter.
+ *
+ * Design parameters:
+ *
+ * fdp.Rdata = 15360000;
+ * fdp.RxTx = "Rx";
+ * fdp.Type = "Lowpass";
+ * fdp.DAC_div = 1;
+ * fdp.HB3 = 2;
+ * fdp.HB2 = 2;
+ * fdp.HB1 = 2;
+ * fdp.FIR = 2;
+ * fdp.PLL_mult = 4;
+ * fdp.converter_rate = 245760000;
+ * fdp.PLL_rate = 983040000;
+ * fdp.Fpass = fdp.Rdata*0.45;
+ * fdp.Fstop = fdp.Rdata*0.50;
+ * fdp.Fcenter = 0;
+ * fdp.Apass = 0.1250;
+ * fdp.Astop = 85;
+ * fdp.phEQ = 217;
+ * fdp.wnom = 8800000;
+ * fdp.caldiv = 19;
+ * fdp.RFbw = 8472407;
+ * fdp.FIRdBmin = 0;
+ * fdp.int_FIR = 1;
+ */
+ // clang-format off
+ {
+ 22, 125, 207, 190, 15, -98, -45, 91,
+ 60, -76, -90, 69, 115, -47, -147, 22,
+ 173, 18, -198, -66, 211, 127, -214, -194,
+ 200, 269, -168, -345, 113, 419, -36, -484,
+ -66, 536, 193, -566, -343, 568, 513, -535,
+ -699, 458, 897, -329, -1099, 140, 1296, 120,
+ -1479, -464, 1636, 912, -1750, -1496, 1797, 2275,
+ -1734, -3378, 1464, 5120, -659, -8461, -2238, 18338,
+ 32689, 24727, 4100, -7107, -2663, 4128, 2513, -2578,
+ -2378, 1567, 2184, -861, -1953, 351, 1703, 22,
+ -1446, -290, 1190, 474, -942, -590, 710, 650,
+ -498, -664, 311, 642, -150, -593, 18, 524,
+ 86, -444, -162, 357, 211, -271, -238, 189,
+ 245, -116, -237, 53, 216, -2, -187, -37,
+ 154, 64, -119, -82, 87, 89, -56, -96,
+ 30, 99, 3, -120, -107, 0, 56, 45,
+ }, // rx_coef[128]
+ // clang-format on
+ 128, // rx_coef_size
+ { 0, 0, 0, 0, 0, 0 }, // rx_path_clks[6]
+ 0 // rx_bandwidth
+};
+
+AD9361_TXFIRConfig bladerf2_rfic_tx_fir_config_int2 = {
+ 3, // tx (TX1 = 1, TX2 = 2, both = 3)
+ 0, // tx_gain (-6 or 0 dB)
+ 2, // tx_int (interpolate by 1, 2, or 4)
+
+ /**
+ * TX FIR Filter
+ * Built using https://github.com/analogdevicesinc/libad9361-iio
+ * Branch: filter_generation
+ * Commit: f749cef974f687f696226455dc7684277886cf3b
+ *
+ * This filter is intended to improve the flatness of the TX spectrum.
+ *
+ * It is a 128-tap, interpolate-by-2 filter. Note that you MUST use a
+ * decimate-by-2 filter on RX if you are using this filter.
+ *
+ * Design parameters:
+ *
+ * fdp.Rdata = 15360000;
+ * fdp.RxTx = "Tx";
+ * fdp.Type = "Lowpass";
+ * fdp.DAC_div = 1;
+ * fdp.HB3 = 2;
+ * fdp.HB2 = 2;
+ * fdp.HB1 = 2;
+ * fdp.FIR = 2;
+ * fdp.PLL_mult = 4;
+ * fdp.converter_rate = 245760000;
+ * fdp.PLL_rate = 983040000;
+ * fdp.Fpass = fdp.Rdata*0.45;
+ * fdp.Fstop = fdp.Rdata*0.50;
+ * fdp.Fcenter = 0;
+ * fdp.Apass = 0.1250;
+ * fdp.Astop = 85;
+ * fdp.phEQ = 217;
+ * fdp.wnom = 8800000;
+ * fdp.caldiv = 19;
+ * fdp.RFbw = 8472407;
+ * fdp.FIRdBmin = 0;
+ * fdp.int_FIR = 1;
+ */
+ // clang-format off
+ {
+ 20, 104, 183, 161, 0, -129, -82, 61,
+ 69, -65, -108, 31, 117, -15, -145, -23,
+ 155, 61, -167, -113, 163, 167, -149, -227,
+ 116, 286, -67, -342, -3, 388, 91, -421,
+ -197, 433, 321, -420, -457, 376, 602, -294,
+ -749, 171, 891, 1, -1019, -225, 1123, 507,
+ -1190, -855, 1205, 1279, -1148, -1800, 984, 2456,
+ -656, -3329, 31, 4619, 1275, -6897, -4889, 12679,
+ 29822, 27710, 9244, -5193, -4330, 2732, 3367, -1405,
+ -2793, 571, 2318, -25, -1901, -338, 1527, 574,
+ -1189, -716, 885, 787, -617, -802, 383, 774,
+ -187, -714, 25, 632, 101, -535, -193, 432,
+ 254, -330, -289, 232, 299, -143, -291, 66,
+ 267, -3, -234, -46, 192, 79, -153, -103,
+ 107, 109, -76, -117, 32, 103, -19, -115,
+ -35, 83, 34, -120, -204, -134, -42, 12,
+ }, // tx_coef[128]
+ // clang-format on
+ 128, // tx_coef_size
+ { 0, 0, 0, 0, 0, 0 }, // tx_path_clks[6]
+ 0 // tx_bandwidth
+};
+
+AD9361_RXFIRConfig bladerf2_rfic_rx_fir_config_dec4 = {
+ 3, // rx (RX1 = 1, RX2 = 2, both = 3)
+ -6, // rx_gain (-12, -6, 0, or 6 dB)
+ 4, // rx_dec (decimate by 1, 2, or 4)
+
+ /**
+ * RX FIR Filter
+ * Built using https://github.com/analogdevicesinc/libad9361-iio
+ * Branch: filter_generation
+ * Commit: f749cef974f687f696226455dc7684277886cf3b
+ *
+ * This filter is intended to allow sample rates down to 520834 sps.
+ *
+ * It is a 128-tap, decimate-by-4 filter. Note that you MUST use a
+ * interpolate-by-4 filter on TX if you are using this filter.
+ *
+ * Design parameters:
+ *
+ * fdp.Rdata = 520834;
+ * fdp.RxTx = "Rx";
+ * fdp.Type = "Lowpass";
+ * fdp.DAC_div = 1;
+ * fdp.HB3 = 3;
+ * fdp.HB2 = 2;
+ * fdp.HB1 = 2;
+ * fdp.FIR = 4;
+ * fdp.PLL_mult = 32;
+ * fdp.converter_rate = 25000000;
+ * fdp.PLL_rate = 800000000;
+ * fdp.Fpass = fdp.Rdata*0.375;
+ * fdp.Fstop = fdp.Rdata*0.50;
+ * fdp.Fcenter = 0;
+ * fdp.Apass = 0.125;
+ * fdp.Astop = 85;
+ * fdp.phEQ = 217;
+ * fdp.wnom = 347220;
+ * fdp.caldiv = 309;
+ * fdp.RFbw = 433256;
+ * fdp.FIRdBmin = 0;
+ * fdp.int_FIR = 1;
+ */
+ // clang-format off
+ {
+ -30, -24, -46, -54, -28, -6, 50, 82,
+ 108, 84, 28, -60, -136, -172, -132, -24,
+ 122, 244, 280, 192, -2, -238, -410, -428,
+ -250, 80, 436, 656, 614, 276, -252, -760,
+ -1006, -830, -234, 580, 1272, 1494, 1060, 48,
+ -1178, -2086, -2192, -1284, 420, 2296, 3504, 3324,
+ 1502, -1544, -4746, -6664, -5978, -1984, 5054, 13852,
+ 22416, 28606, 30790, 28370, 21974, 13264, 4422, -2522,
+ -6282, -6630, -4358, -892, 2236, 3914, 3762, 2144,
+ -80, -1948, -2774, -2376, -1078, 486, 1652, 2008,
+ 1510, 466, -640, -1352, -1434, -928, -110, 652,
+ 1060, 990, 532, -82, -586, -792, -654, -274,
+ 164, 480, 562, 410, 118, -178, -362, -378,
+ -244, -34, 154, 256, 240, 136, -4, -116,
+ -168, -144, -74, 14, 74, 102, 76, 38,
+ -28, -54, -96, -68, -82, -26, -34, -2,
+ }, // rx_coef[128]
+ // clang-format on
+ 128, // rx_coef_size
+ { 0, 0, 0, 0, 0, 0 }, // rx_path_clks[6]
+ 0 // rx_bandwidth
+};
+
+AD9361_TXFIRConfig bladerf2_rfic_tx_fir_config_int4 = {
+ 3, // tx (TX1 = 1, TX2 = 2, both = 3)
+ 0, // tx_gain (-6 or 0 dB)
+ 4, // tx_int (interpolate by 1, 2, or 4)
+
+ /**
+ * TX FIR Filter
+ * Built using https://github.com/analogdevicesinc/libad9361-iio
+ * Branch: filter_generation
+ * Commit: f749cef974f687f696226455dc7684277886cf3b
+ *
+ * This filter is intended to allow sample rates down to 520834 sps.
+ *
+ * It is a 128-tap, interpolate-by-4 filter. Note that you MUST use a
+ * decimate-by-4 filter on RX if you are using this filter.
+ *
+ * Design parameters:
+ *
+ * fdp.Rdata = 520834;
+ * fdp.RxTx = "Tx";
+ * fdp.Type = "Lowpass";
+ * fdp.DAC_div = 1;
+ * fdp.HB3 = 3;
+ * fdp.HB2 = 2;
+ * fdp.HB1 = 2;
+ * fdp.FIR = 4;
+ * fdp.PLL_mult = 32;
+ * fdp.converter_rate = 25000000;
+ * fdp.PLL_rate = 800000000;
+ * fdp.Fpass = fdp.Rdata*0.375;
+ * fdp.Fstop = fdp.Rdata*0.50;
+ * fdp.Fcenter = 0;
+ * fdp.Apass = 0.125;
+ * fdp.Astop = 85;
+ * fdp.phEQ = 217;
+ * fdp.wnom = 347220;
+ * fdp.caldiv = 309;
+ * fdp.RFbw = 1253611;
+ * fdp.FIRdBmin = 0;
+ * fdp.int_FIR = 1;
+ */
+ // clang-format off
+ {
+ -18, 2, -14, 16, 34, 76, 104, 124,
+ 108, 62, -12, -86, -136, -128, -58, 58,
+ 174, 242, 214, 84, -110, -294, -382, -310,
+ -84, 226, 494, 586, 426, 40, -434, -796,
+ -860, -538, 92, 792, 1258, 1226, 622, -386,
+ -1406, -1972, -1730, -628, 1002, 2522, 3200, 2526,
+ 466, -2400, -4986, -6012, -4444, 90, 7064, 15112,
+ 22370, 27018, 27836, 24590, 18120, 10072, 2400, -3220,
+ -5868, -5578, -3214, -106, 2446, 3584, 3126, 1508,
+ -464, -1970, -2484, -1940, -696, 666, 1590, 1764,
+ 1212, 244, -706, -1258, -1242, -732, 10, 656,
+ 964, 850, 412, -134, -560, -710, -560, -208,
+ 178, 444, 500, 352, 88, -172, -330, -336,
+ -212, -24, 144, 230, 218, 124, 2, -100,
+ -146, -126, -62, 18, 82, 110, 98, 60,
+ 12, -28, -52, -56, -50, -32, -18, -8,
+ }, // tx_coef[128]
+ // clang-format on
+ 128, // tx_coef_size
+ { 0, 0, 0, 0, 0, 0 }, // tx_path_clks[6]
+ 0 // tx_bandwidth
+};
+
+#endif // !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X)
diff --git a/Radio/HW/BladeRF/fpga_common/src/band_select.c b/Radio/HW/BladeRF/fpga_common/src/band_select.c
new file mode 100644
index 0000000..3d20e8f
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/src/band_select.c
@@ -0,0 +1,59 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdint.h>
+#include "band_select.h"
+#include "lms.h"
+
+int band_select(struct bladerf *dev, bladerf_module module, bool low_band)
+{
+ int status;
+ uint32_t gpio;
+ const uint32_t band = low_band ? 2 : 1;
+
+ log_debug("Selecting %s band.\n", low_band ? "low" : "high");
+
+ status = lms_select_band(dev, module, low_band);
+ if (status != 0) {
+ return status;
+ }
+
+#ifndef BLADERF_NIOS_BUILD
+ status = dev->backend->config_gpio_read(dev, &gpio);
+#else
+ status = CONFIG_GPIO_READ(dev, &gpio);
+#endif
+ if (status != 0) {
+ return status;
+ }
+
+ gpio &= ~(module == BLADERF_MODULE_TX ? (3 << 3) : (3 << 5));
+ gpio |= (module == BLADERF_MODULE_TX ? (band << 3) : (band << 5));
+
+#ifndef BLADERF_NIOS_BUILD
+ return dev->backend->config_gpio_write(dev, gpio);
+#else
+ return CONFIG_GPIO_WRITE(dev, gpio);
+#endif
+}
diff --git a/Radio/HW/BladeRF/fpga_common/src/bladerf2_common.c b/Radio/HW/BladeRF/fpga_common/src/bladerf2_common.c
new file mode 100644
index 0000000..6719ccd
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/src/bladerf2_common.c
@@ -0,0 +1,194 @@
+/* This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2018 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifdef BLADERF_NIOS_BUILD
+#include "devices.h"
+#endif // BLADERF_NIOS_BUILD
+
+/* Avoid building this in low-memory situations */
+#if !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X)
+
+#include "bladerf2_common.h"
+
+/**
+ * Value of the current sense resistor (R132)
+ */
+float const ina219_r_shunt = 0.001F;
+
+int errno_ad9361_to_bladerf(int err)
+{
+ if (err >= 0) {
+ return 0;
+ }
+
+ switch (err) {
+ case EIO:
+ return BLADERF_ERR_IO;
+ case EAGAIN:
+ return BLADERF_ERR_WOULD_BLOCK;
+ case ENOMEM:
+ return BLADERF_ERR_MEM;
+ case EFAULT:
+ return BLADERF_ERR_UNEXPECTED;
+ case ENODEV:
+ return BLADERF_ERR_NODEV;
+ case EINVAL:
+ return BLADERF_ERR_INVAL;
+ case ETIMEDOUT:
+ return BLADERF_ERR_TIMEOUT;
+ }
+
+ return BLADERF_ERR_UNEXPECTED;
+}
+
+struct band_port_map const *_get_band_port_map_by_freq(bladerf_channel ch,
+ bladerf_frequency freq)
+{
+ struct band_port_map const *port_map;
+ size_t port_map_len;
+ int64_t freqi = (int64_t)freq;
+ size_t i;
+
+ /* Select the band->port map for RX vs TX */
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ port_map = bladerf2_tx_band_port_map;
+ port_map_len = ARRAY_SIZE(bladerf2_tx_band_port_map);
+ } else {
+ port_map = bladerf2_rx_band_port_map;
+ port_map_len = ARRAY_SIZE(bladerf2_rx_band_port_map);
+ }
+
+ if (NULL == port_map) {
+ return NULL;
+ }
+
+ /* Search through the band->port map for the desired band */
+ for (i = 0; i < port_map_len; i++) {
+ if (is_within_range(&port_map[i].frequency, freqi)) {
+ return &port_map[i];
+ }
+ }
+
+ /* Wasn't found, return a null ptr */
+ return NULL;
+}
+
+int _modify_spdt_bits_by_freq(uint32_t *reg,
+ bladerf_channel ch,
+ bool enabled,
+ bladerf_frequency freq)
+{
+ struct band_port_map const *port_map;
+ uint32_t shift;
+
+ if (NULL == reg) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Look up the port configuration for this frequency */
+ port_map = _get_band_port_map_by_freq(ch, enabled ? freq : 0);
+
+ if (NULL == port_map) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Modify the reg bits accordingly */
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ shift = RFFE_CONTROL_RX_SPDT_1;
+ break;
+ case BLADERF_CHANNEL_RX(1):
+ shift = RFFE_CONTROL_RX_SPDT_2;
+ break;
+ case BLADERF_CHANNEL_TX(0):
+ shift = RFFE_CONTROL_TX_SPDT_1;
+ break;
+ case BLADERF_CHANNEL_TX(1):
+ shift = RFFE_CONTROL_TX_SPDT_2;
+ break;
+ default:
+ return BLADERF_ERR_INVAL;
+ }
+
+ *reg &= ~(RFFE_CONTROL_SPDT_MASK << shift);
+ *reg |= port_map->spdt << shift;
+
+ return 0;
+}
+
+int _get_rffe_control_bit_for_dir(bladerf_direction dir)
+{
+ switch (dir) {
+ case BLADERF_RX:
+ return RFFE_CONTROL_ENABLE;
+ case BLADERF_TX:
+ return RFFE_CONTROL_TXNRX;
+ default:
+ return UINT32_MAX;
+ }
+}
+
+int _get_rffe_control_bit_for_ch(bladerf_channel ch)
+{
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ return RFFE_CONTROL_MIMO_RX_EN_0;
+ case BLADERF_CHANNEL_RX(1):
+ return RFFE_CONTROL_MIMO_RX_EN_1;
+ case BLADERF_CHANNEL_TX(0):
+ return RFFE_CONTROL_MIMO_TX_EN_0;
+ case BLADERF_CHANNEL_TX(1):
+ return RFFE_CONTROL_MIMO_TX_EN_1;
+ default:
+ return UINT32_MAX;
+ }
+}
+
+bool _rffe_ch_enabled(uint32_t reg, bladerf_channel ch)
+{
+ return (reg >> _get_rffe_control_bit_for_ch(ch)) & 0x1;
+}
+
+bool _rffe_dir_enabled(uint32_t reg, bladerf_direction dir)
+{
+ return (reg >> _get_rffe_control_bit_for_dir(dir)) & 0x1;
+}
+
+bool _rffe_dir_otherwise_enabled(uint32_t reg, bladerf_channel ch)
+{
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ return _rffe_ch_enabled(reg, BLADERF_CHANNEL_RX(1));
+ case BLADERF_CHANNEL_RX(1):
+ return _rffe_ch_enabled(reg, BLADERF_CHANNEL_RX(0));
+ case BLADERF_CHANNEL_TX(0):
+ return _rffe_ch_enabled(reg, BLADERF_CHANNEL_TX(1));
+ case BLADERF_CHANNEL_TX(1):
+ return _rffe_ch_enabled(reg, BLADERF_CHANNEL_TX(0));
+ }
+
+ return false;
+}
+
+#endif // !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X)
diff --git a/Radio/HW/BladeRF/fpga_common/src/lms.c b/Radio/HW/BladeRF/fpga_common/src/lms.c
new file mode 100644
index 0000000..4f2f930
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/src/lms.c
@@ -0,0 +1,3637 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013-2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * If you're diving into this file, have the following documentation handy.
+ *
+ * As most registers don't have a clearly defined names, or are not grouped by
+ * a specific set of functionality, there's little value in providing named
+ * macro definitions, hence the hard-coded addresses and bitmasks.
+ *
+ * LMS6002D Project page:
+ * http://www.limemicro.com/products/LMS6002D.php?sector=default
+ *
+ * LMS6002D Datasheet:
+ * http://www.limemicro.com/download/LMS6002Dr2-DataSheet-1.2r0.pdf
+ *
+ * LMS6002D Programming and Calibration Guide:
+ * http://www.limemicro.com/download/LMS6002Dr2-Programming_and_Calibration_Guide-1.1r1.pdf
+ *
+ * LMS6002D FAQ:
+ * http://www.limemicro.com/download/FAQ_v1.0r10.pdf
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <libbladeRF.h>
+
+#include "lms.h"
+
+#ifndef BLADERF_NIOS_BUILD
+# include "log.h"
+# include "rel_assert.h"
+# include "board/board.h"
+# include "board/bladerf1/capabilities.h"
+
+// #define LMS_COUNT_BUSY_WAITS
+
+ /* Unneeded, due to USB transfer duration */
+# define VTUNE_BUSY_WAIT(us) \
+ do { \
+ INC_BUSY_WAIT_COUNT(us); \
+ log_verbose("VTUNE_BUSY_WAIT(%u)\n", us); \
+ } while(0)
+#else
+# include <unistd.h>
+# define VTUNE_BUSY_WAIT(us) { usleep(us); INC_BUSY_WAIT_COUNT(us); }
+#endif
+
+/* By counting the busy waits between a VCOCAP write and VTUNE read, we can
+ * get a sense of how good/bad our initial VCOCAP guess is and how
+ * (in)efficient our tuning routine is. */
+#ifdef LMS_COUNT_BUSY_WAITS
+ static volatile uint32_t busy_wait_count = 0;
+ static volatile uint32_t busy_wait_duration = 0;
+
+# define INC_BUSY_WAIT_COUNT(us) do { \
+ busy_wait_count++; \
+ busy_wait_duration += us; \
+ } while (0)
+
+# define RESET_BUSY_WAIT_COUNT() do { \
+ busy_wait_count = 0; \
+ busy_wait_duration = 0; \
+ } while (0)
+ static inline void PRINT_BUSY_WAIT_INFO()
+ {
+ if (busy_wait_count > 10) {
+ log_warning("Busy wait count: %u\n", busy_wait_count);
+ } else {
+ log_debug("Busy wait count: %u\n", busy_wait_count);
+ }
+
+ log_debug("Busy wait duration: %u us\n", busy_wait_duration);
+ }
+#else
+# define INC_BUSY_WAIT_COUNT(us) do {} while (0)
+# define RESET_BUSY_WAIT_COUNT() do {} while (0)
+# define PRINT_BUSY_WAIT_INFO()
+#endif
+
+#define LMS_REFERENCE_HZ (38400000u)
+
+#define kHz(x) (x * 1000)
+#define MHz(x) (x * 1000000)
+#define GHz(x) (x * 1000000000)
+
+struct dc_cal_state {
+ uint8_t clk_en; /* Backup of clock enables */
+
+ uint8_t reg0x72; /* Register backup */
+
+ bladerf_lna_gain lna_gain; /* Backup of gain values */
+ int rxvga1_gain;
+ int rxvga2_gain;
+
+ uint8_t base_addr; /* Base address of DC cal regs */
+ unsigned int num_submodules; /* # of DC cal submodules to operate on */
+
+ int rxvga1_curr_gain; /* Current gains used in retry loops */
+ int rxvga2_curr_gain;
+};
+
+#ifndef BLADERF_NIOS_BUILD
+/* LPF conversion table */
+static const unsigned int uint_bandwidths[] = {
+ MHz(28),
+ MHz(20),
+ MHz(14),
+ MHz(12),
+ MHz(10),
+ kHz(8750),
+ MHz(7),
+ MHz(6),
+ kHz(5500),
+ MHz(5),
+ kHz(3840),
+ MHz(3),
+ kHz(2750),
+ kHz(2500),
+ kHz(1750),
+ kHz(1500)
+};
+#endif
+
+#define FREQ_RANGE(low_, high_, value_) \
+{ \
+ FIELD_INIT(.low, low_), \
+ FIELD_INIT(.high, high_), \
+ FIELD_INIT(.value, value_), \
+}
+
+/* Here we define more conservative band ranges than those in the
+ * LMS FAQ (5.24), with the intent of avoiding the use of "edges" that might
+ * cause the PLLs to lose lock over temperature changes */
+#define VCO4_LOW 3800000000ull
+#define VCO4_HIGH 4535000000ull
+
+#define VCO3_LOW VCO4_HIGH
+#define VCO3_HIGH 5408000000ull
+
+#define VCO2_LOW VCO3_HIGH
+#define VCO2_HIGH 6480000000ull
+
+#define VCO1_LOW VCO2_HIGH
+#define VCO1_HIGH 7600000000ull
+
+#if VCO4_LOW/16 != BLADERF_FREQUENCY_MIN
+# error "BLADERF_FREQUENCY_MIN is not actual VCO4_LOW/16 minimum"
+#endif
+
+#if VCO1_HIGH/2 != BLADERF_FREQUENCY_MAX
+# error "BLADERF_FREQUENCY_MAX is not actual VCO1_HIGH/2 maximum"
+#endif
+
+/* SELVCO values */
+#define VCO4 (4 << 3)
+#define VCO3 (5 << 3)
+#define VCO2 (6 << 3)
+#define VCO1 (7 << 3)
+
+/* FRANGE values */
+#define DIV2 0x4
+#define DIV4 0x5
+#define DIV8 0x6
+#define DIV16 0x7
+
+#ifndef BLADERF_NIOS_BUILD
+/* Frequency Range table. Corresponds to the LMS FREQSEL table.
+ * Per feedback from the LMS google group, the last entry, listed as 3.72G
+ * in the programming manual, can be applied up to 3.8G */
+static const struct freq_range {
+ uint32_t low;
+ uint32_t high;
+ uint8_t value;
+} bands[] = {
+ FREQ_RANGE(BLADERF_FREQUENCY_MIN, VCO4_HIGH/16, VCO4 | DIV16),
+ FREQ_RANGE(VCO3_LOW/16, VCO3_HIGH/16, VCO3 | DIV16),
+ FREQ_RANGE(VCO2_LOW/16, VCO2_HIGH/16, VCO2 | DIV16),
+ FREQ_RANGE(VCO1_LOW/16, VCO1_HIGH/16, VCO1 | DIV16),
+ FREQ_RANGE(VCO4_LOW/8, VCO4_HIGH/8, VCO4 | DIV8),
+ FREQ_RANGE(VCO3_LOW/8, VCO3_HIGH/8, VCO3 | DIV8),
+ FREQ_RANGE(VCO2_LOW/8, VCO2_HIGH/8, VCO2 | DIV8),
+ FREQ_RANGE(VCO1_LOW/8, VCO1_HIGH/8, VCO1 | DIV8),
+ FREQ_RANGE(VCO4_LOW/4, VCO4_HIGH/4, VCO4 | DIV4),
+ FREQ_RANGE(VCO3_LOW/4, VCO3_HIGH/4, VCO3 | DIV4),
+ FREQ_RANGE(VCO2_LOW/4, VCO2_HIGH/4, VCO2 | DIV4),
+ FREQ_RANGE(VCO1_LOW/4, VCO1_HIGH/4, VCO1 | DIV4),
+ FREQ_RANGE(VCO4_LOW/2, VCO4_HIGH/2, VCO4 | DIV2),
+ FREQ_RANGE(VCO3_LOW/2, VCO3_HIGH/2, VCO3 | DIV2),
+ FREQ_RANGE(VCO2_LOW/2, VCO2_HIGH/2, VCO2 | DIV2),
+ FREQ_RANGE(VCO1_LOW/2, BLADERF_FREQUENCY_MAX, VCO1 | DIV2),
+};
+
+ /*
+ * The LMS FAQ (Rev 1.0r10, Section 5.20) states that the RXVGA1 codes may be
+ * converted to dB via:
+ * value_db = 20 * log10(127 / (127 - code))
+ *
+ * However, an offset of 5 appears to be required, yielding:
+ * value_db = 5 + 20 * log10(127 / (127 - code))
+ *
+ */
+static const uint8_t rxvga1_lut_code2val[] = {
+ 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10,
+ 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 13, 13,
+ 13, 13, 13, 13, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 16, 16, 16, 16, 17,
+ 17, 17, 18, 18, 18, 18, 19, 19, 19, 20, 20, 21, 21, 22, 22, 22, 23, 24, 24,
+ 25, 25, 26, 27, 28, 29, 30
+};
+
+/* The closest values from the above forumla have been selected.
+ * indicides 0 - 4 are clamped to 5dB */
+static const uint8_t rxvga1_lut_val2code[] = {
+ 2, 2, 2, 2, 2, 2, 14, 26, 37, 47, 56, 63, 70, 76, 82, 87,
+ 91, 95, 99, 102, 104, 107, 109, 111, 113, 114, 116, 117, 118, 119, 120,
+};
+
+static const uint8_t lms_reg_dumpset[] = {
+ /* Top level configuration */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
+ 0x0E, 0x0F,
+
+ /* TX PLL Configuration */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B,
+ 0x1C, 0x1D, 0x1E, 0x1F,
+
+ /* RX PLL Configuration */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B,
+ 0x2C, 0x2D, 0x2E, 0x2F,
+
+ /* TX LPF Modules Configuration */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
+
+ /* TX RF Modules Configuration */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B,
+ 0x4C, 0x4D, 0x4E, 0x4F,
+
+ /* RX LPF, ADC, and DAC Modules Configuration */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B,
+ 0x5C, 0x5D, 0x5E, 0x5F,
+
+ /* RX VGA2 Configuration */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+
+ /* RX FE Modules Configuration */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C
+};
+#endif
+
+/* Register 0x08: RF loopback config and additional BB config
+ *
+ * LBRFEN[3:0] @ [3:0]
+ * 0000 - RF loopback disabled
+ * 0001 - TXMIX output connected to LNA1 path
+ * 0010 - TXMIX output connected to LNA2 path
+ * 0011 - TXMIX output connected to LNA3 path
+ * else - Reserved
+ *
+ * LBEN_OPIN @ [4]
+ * 0 - Disabled
+ * 1 - TX BB loopback signal is connected to RX output pins
+ *
+ * LBEN_VGA2IN @ [5]
+ * 0 - Disabled
+ * 1 - TX BB loopback signal is connected to RXVGA2 input
+ *
+ * LBEN_LPFIN @ [6]
+ * 0 - Disabled
+ * 1 - TX BB loopback signal is connected to RXLPF input
+ *
+ */
+#define LBEN_OPIN (1 << 4)
+#define LBEN_VGA2IN (1 << 5)
+#define LBEN_LPFIN (1 << 6)
+#define LBEN_MASK (LBEN_OPIN | LBEN_VGA2IN | LBEN_LPFIN)
+
+#define LBRFEN_LNA1 1
+#define LBRFEN_LNA2 2
+#define LBRFEN_LNA3 3
+#define LBRFEN_MASK 0xf /* [3:2] are marked reserved */
+
+
+/* Register 0x46: Baseband loopback config
+ *
+ * LOOPBBEN[1:0] @ [3:2]
+ * 00 - All Baseband loops opened (default)
+ * 01 - TX loopback path connected from TXLPF output
+ * 10 - TX loopback path connected from TXVGA1 output
+ * 11 - TX loopback path connected from Env/peak detect output
+ */
+#define LOOPBBEN_TXLPF (1 << 2)
+#define LOOPBBEN_TXVGA (2 << 2)
+#define LOOPBBEN_ENVPK (3 << 2)
+#define LOOBBBEN_MASK (3 << 2)
+
+static inline int is_loopback_enabled(struct bladerf *dev)
+{
+ bladerf_loopback loopback;
+ int status;
+
+ status = lms_get_loopback_mode(dev, &loopback);
+ if (status != 0) {
+ return status;
+ }
+
+ return loopback != BLADERF_LB_NONE;
+}
+
+/* VCOCAP estimation. The MIN/MAX values were determined experimentally by
+ * sampling the VCOCAP values over frequency, for each of the VCOs and finding
+ * these to be in the "middle" of a linear regression. Although the curve
+ * isn't actually linear, the linear approximation yields satisfactory error. */
+#define VCOCAP_MAX_VALUE 0x3f
+#define VCOCAP_EST_MIN 15
+#define VCOCAP_EST_MAX 55
+#define VCOCAP_EST_RANGE (VCOCAP_EST_MAX - VCOCAP_EST_MIN)
+#define VCOCAP_EST_THRESH 7 /* Complain if we're +/- 7 on our guess */
+
+/* This is a linear interpolation of our experimentally identified
+ * mean VCOCAP min and VCOCAP max values:
+ */
+static inline uint8_t estimate_vcocap(unsigned int f_target,
+ unsigned int f_low, unsigned int f_high)
+{
+ unsigned int vcocap;
+ const float denom = (float) (f_high - f_low);
+ const float num = VCOCAP_EST_RANGE;
+ const float f_diff = (float) (f_target - f_low);
+
+ vcocap = (unsigned int) ((num / denom * f_diff) + 0.5 + VCOCAP_EST_MIN);
+
+ if (vcocap > VCOCAP_MAX_VALUE) {
+ log_warning("Clamping VCOCAP estimate from %u to %u\n",
+ vcocap, VCOCAP_MAX_VALUE);
+ vcocap = VCOCAP_MAX_VALUE;
+ } else {
+ log_verbose("VCOCAP estimate: %u\n", vcocap);
+ }
+ return (uint8_t) vcocap;
+}
+
+static int write_pll_config(struct bladerf *dev, bladerf_module module,
+ uint8_t freqsel, bool low_band)
+{
+ int status;
+ uint8_t regval;
+ uint8_t selout;
+ uint8_t addr;
+
+ if (module == BLADERF_MODULE_TX) {
+ addr = 0x15;
+ } else {
+ addr = 0x25;
+ }
+
+ status = LMS_READ(dev, addr, &regval);
+ if (status != 0) {
+ return status;
+ }
+
+ status = is_loopback_enabled(dev);
+ if (status < 0) {
+ return status;
+ }
+
+ if (status == 0) {
+ /* Loopback not enabled - update the PLL output buffer. */
+ selout = low_band ? 1 : 2;
+ regval = (freqsel << 2) | selout;
+ } else {
+ /* Loopback is enabled - don't touch PLL output buffer. */
+ regval = (regval & ~0xfc) | (freqsel << 2);
+ }
+
+ return LMS_WRITE(dev, addr, regval);
+}
+
+#ifndef BLADERF_NIOS_BUILD
+
+int lms_config_charge_pumps(struct bladerf *dev, bladerf_module module)
+{
+ int status;
+ uint8_t data;
+ const uint8_t base = (module == BLADERF_MODULE_RX) ? 0x20 : 0x10;
+
+ /* Set the PLL Ichp, Iup and Idn currents */
+ status = LMS_READ(dev, base + 6, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ data &= ~(0x1f);
+ data |= 0x0c;
+
+ status = LMS_WRITE(dev, base + 6, data);
+ if (status != 0) {
+ return status;
+ }
+
+ status = LMS_READ(dev, base + 7, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ data &= ~(0x1f);
+ data |= 3;
+
+ status = LMS_WRITE(dev, base + 7, data);
+ if (status != 0) {
+ return status;
+ }
+
+ status = LMS_READ(dev, base + 8, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ data &= ~(0x1f);
+ data |= 3;
+ status = LMS_WRITE(dev, base + 8, data);
+ if (status != 0) {
+ return status;
+ }
+
+ return 0;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_lpf_enable(struct bladerf *dev, bladerf_module mod, bool enable)
+{
+ int status;
+ uint8_t data;
+ const uint8_t reg = (mod == BLADERF_MODULE_RX) ? 0x54 : 0x34;
+
+ status = LMS_READ(dev, reg, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ if (enable) {
+ data |= (1 << 1);
+ } else {
+ data &= ~(1 << 1);
+ }
+
+ status = LMS_WRITE(dev, reg, data);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Check to see if we are bypassed */
+ status = LMS_READ(dev, reg + 1, &data);
+ if (status != 0) {
+ return status;
+ } else if (data & (1 << 6)) {
+ /* Bypass is enabled; switch back to normal operation */
+ data &= ~(1 << 6);
+ status = LMS_WRITE(dev, reg + 1, data);
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_lpf_get_mode(struct bladerf *dev, bladerf_module mod,
+ bladerf_lpf_mode *mode)
+{
+ int status;
+ const uint8_t reg = (mod == BLADERF_MODULE_RX) ? 0x54 : 0x34;
+ uint8_t data_h, data_l;
+ bool lpf_enabled, lpf_bypassed;
+
+ status = LMS_READ(dev, reg, &data_l);
+ if (status != 0) {
+ return status;
+ }
+
+ status = LMS_READ(dev, reg + 1, &data_h);
+ if (status != 0) {
+ return status;
+ }
+
+ lpf_enabled = (data_l & (1 << 1)) != 0;
+ lpf_bypassed = (data_h & (1 << 6)) != 0;
+
+ if (lpf_enabled && !lpf_bypassed) {
+ *mode = BLADERF_LPF_NORMAL;
+ } else if (!lpf_enabled && lpf_bypassed) {
+ *mode = BLADERF_LPF_BYPASSED;
+ } else if (!lpf_enabled && !lpf_bypassed) {
+ *mode = BLADERF_LPF_DISABLED;
+ } else {
+ log_debug("Invalid LPF configuration: 0x%02x, 0x%02x\n",
+ data_l, data_h);
+ status = BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_lpf_set_mode(struct bladerf *dev, bladerf_module mod,
+ bladerf_lpf_mode mode)
+{
+ int status;
+ const uint8_t reg = (mod == BLADERF_MODULE_RX) ? 0x54 : 0x34;
+ uint8_t data_l, data_h;
+
+ status = LMS_READ(dev, reg, &data_l);
+ if (status != 0) {
+ return status;
+ }
+
+ status = LMS_READ(dev, reg + 1, &data_h);
+ if (status != 0) {
+ return status;
+ }
+
+ switch (mode) {
+ case BLADERF_LPF_NORMAL:
+ data_l |= (1 << 1); /* Enable LPF */
+ data_h &= ~(1 << 6); /* Disable LPF bypass */
+ break;
+
+ case BLADERF_LPF_BYPASSED:
+ data_l &= ~(1 << 1); /* Power down LPF */
+ data_h |= (1 << 6); /* Enable LPF bypass */
+ break;
+
+ case BLADERF_LPF_DISABLED:
+ data_l &= ~(1 << 1); /* Power down LPF */
+ data_h &= ~(1 << 6); /* Disable LPF bypass */
+ break;
+
+ default:
+ log_debug("Invalid LPF mode: %d\n", mode);
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = LMS_WRITE(dev, reg, data_l);
+ if (status != 0) {
+ return status;
+ }
+
+ status = LMS_WRITE(dev, reg + 1, data_h);
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_set_bandwidth(struct bladerf *dev, bladerf_module mod, lms_bw bw)
+{
+ int status;
+ uint8_t data;
+ const uint8_t reg = (mod == BLADERF_MODULE_RX) ? 0x54 : 0x34;
+
+ status = LMS_READ(dev, reg, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ data &= ~0x3c; /* Clear out previous bandwidth setting */
+ data |= (bw << 2); /* Apply new bandwidth setting */
+
+ return LMS_WRITE(dev, reg, data);
+
+}
+#endif
+
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_get_bandwidth(struct bladerf *dev, bladerf_module mod, lms_bw *bw)
+{
+ int status;
+ uint8_t data;
+ const uint8_t reg = (mod == BLADERF_MODULE_RX) ? 0x54 : 0x34;
+
+ status = LMS_READ(dev, reg, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Fetch bandwidth table index from reg[5:2] */
+ data >>= 2;
+ data &= 0xf;
+
+ assert(data < ARRAY_SIZE(uint_bandwidths));
+ *bw = (lms_bw)data;
+ return 0;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+lms_bw lms_uint2bw(unsigned int req)
+{
+ lms_bw ret;
+
+ if ( req <= kHz(1500)) ret = BW_1p5MHz;
+ else if (req <= kHz(1750)) ret = BW_1p75MHz;
+ else if (req <= kHz(2500)) ret = BW_2p5MHz;
+ else if (req <= kHz(2750)) ret = BW_2p75MHz;
+ else if (req <= MHz(3) ) ret = BW_3MHz;
+ else if (req <= kHz(3840)) ret = BW_3p84MHz;
+ else if (req <= MHz(5) ) ret = BW_5MHz;
+ else if (req <= kHz(5500)) ret = BW_5p5MHz;
+ else if (req <= MHz(6) ) ret = BW_6MHz;
+ else if (req <= MHz(7) ) ret = BW_7MHz;
+ else if (req <= kHz(8750)) ret = BW_8p75MHz;
+ else if (req <= MHz(10) ) ret = BW_10MHz;
+ else if (req <= MHz(12) ) ret = BW_12MHz;
+ else if (req <= MHz(14) ) ret = BW_14MHz;
+ else if (req <= MHz(20) ) ret = BW_20MHz;
+ else ret = BW_28MHz;
+
+ return ret;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+/* Return the table entry */
+unsigned int lms_bw2uint(lms_bw bw)
+{
+ unsigned int idx = bw & 0xf;
+ assert(idx < ARRAY_SIZE(uint_bandwidths));
+ return uint_bandwidths[idx];
+}
+#endif
+
+/* Enable dithering on the module PLL */
+#ifndef BLADERF_NIOS_BUILD
+int lms_dither_enable(struct bladerf *dev, bladerf_module mod,
+ uint8_t nbits, bool enable)
+{
+ int status;
+
+ /* Select the base address based on which PLL we are configuring */
+ const uint8_t reg = (mod == BLADERF_MODULE_RX) ? 0x24 : 0x14;
+ uint8_t data;
+
+ /* Valid range is 1 - 8 bits (inclusive) */
+ if (nbits < 1 || nbits > 8) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Read what we currently have in there */
+ status = LMS_READ(dev, reg, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ if (enable) {
+ /* Enable dithering */
+ data |= (1 << 7);
+
+ /* Clear out the previous setting of the number of bits to dither */
+ data &= ~(7 << 4);
+
+ /* Update with the desired number of bits to dither */
+ data |= (((nbits - 1) & 7) << 4);
+
+ } else {
+ /* Clear dithering enable bit */
+ data &= ~(1 << 7);
+ }
+
+ /* Write it out */
+ status = LMS_WRITE(dev, reg, data);
+ return status;
+}
+#endif
+
+/* Soft reset of the LMS */
+#ifndef BLADERF_NIOS_BUILD
+int lms_soft_reset(struct bladerf *dev)
+{
+
+ int status = LMS_WRITE(dev, 0x05, 0x12);
+
+ if (status == 0) {
+ status = LMS_WRITE(dev, 0x05, 0x32);
+ }
+
+ return status;
+}
+#endif
+
+/* Set the gain on the LNA */
+#ifndef BLADERF_NIOS_BUILD
+int lms_lna_set_gain(struct bladerf *dev, bladerf_lna_gain gain)
+{
+ int status;
+ uint8_t data;
+
+ if (gain == BLADERF_LNA_GAIN_BYPASS || gain == BLADERF_LNA_GAIN_MID ||
+ gain == BLADERF_LNA_GAIN_MAX) {
+
+ status = LMS_READ(dev, 0x75, &data);
+ if (status == 0) {
+ data &= ~(3 << 6); /* Clear out previous gain setting */
+ data |= ((gain & 3) << 6); /* Update gain value */
+ status = LMS_WRITE(dev, 0x75, data);
+ }
+
+ } else {
+ status = BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_lna_get_gain(struct bladerf *dev, bladerf_lna_gain *gain)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x75, &data);
+ if (status == 0) {
+ data >>= 6;
+ data &= 3;
+ *gain = (bladerf_lna_gain)data;
+
+ if (*gain == BLADERF_LNA_GAIN_UNKNOWN) {
+ status = BLADERF_ERR_INVAL;
+ }
+ }
+
+ return status;
+}
+#endif
+
+/* Select which LNA to enable */
+int lms_select_lna(struct bladerf *dev, lms_lna lna)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x75, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ data &= ~(3 << 4);
+ data |= ((lna & 3) << 4);
+
+ return LMS_WRITE(dev, 0x75, data);
+}
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_get_lna(struct bladerf *dev, lms_lna *lna)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x75, &data);
+ if (status != 0) {
+ *lna = LNA_NONE;
+ return status;
+ } else {
+ *lna = (lms_lna) ((data >> 4) & 0x3);
+ return 0;
+ }
+}
+#endif
+
+/* Enable bit is in reserved register documented in this thread:
+ * https://groups.google.com/forum/#!topic/limemicro-opensource/8iTannzlfzg
+ */
+#ifndef BLADERF_NIOS_BUILD
+int lms_rxvga1_enable(struct bladerf *dev, bool enable)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x7d, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ if (enable) {
+ data &= ~(1 << 3);
+ } else {
+ data |= (1 << 3);
+ }
+
+ return LMS_WRITE(dev, 0x7d, data);
+}
+#endif
+
+/* Set the RFB_TIA_RXFE mixer gain */
+#ifndef BLADERF_NIOS_BUILD
+int lms_rxvga1_set_gain(struct bladerf *dev, int gain)
+{
+ if (gain > BLADERF_RXVGA1_GAIN_MAX) {
+ gain = BLADERF_RXVGA1_GAIN_MAX;
+ log_info("Clamping RXVGA1 gain to %ddB\n", gain);
+ } else if (gain < BLADERF_RXVGA1_GAIN_MIN) {
+ gain = BLADERF_RXVGA1_GAIN_MIN;
+ log_info("Clamping RXVGA1 gain to %ddB\n", gain);
+ }
+
+ return LMS_WRITE(dev, 0x76, rxvga1_lut_val2code[gain]);
+}
+#endif
+
+/* Get the RFB_TIA_RXFE mixer gain */
+#ifndef BLADERF_NIOS_BUILD
+int lms_rxvga1_get_gain(struct bladerf *dev, int *gain)
+{
+ uint8_t data;
+ int status = LMS_READ(dev, 0x76, &data);
+
+ if (status == 0) {
+ data &= 0x7f;
+ if (data > 120) {
+ data = 120;
+ }
+
+ *gain = rxvga1_lut_code2val[data];
+ }
+
+ return status;
+}
+#endif
+
+/* Enable RXVGA2 */
+#ifndef BLADERF_NIOS_BUILD
+int lms_rxvga2_enable(struct bladerf *dev, bool enable)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x64, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ if (enable) {
+ data |= (1 << 1);
+ } else {
+ data &= ~(1 << 1);
+ }
+
+ return LMS_WRITE(dev, 0x64, data);
+}
+#endif
+
+
+/* Set the gain on RXVGA2 */
+#ifndef BLADERF_NIOS_BUILD
+int lms_rxvga2_set_gain(struct bladerf *dev, int gain)
+{
+ if (gain > BLADERF_RXVGA2_GAIN_MAX) {
+ gain = BLADERF_RXVGA2_GAIN_MAX;
+ log_info("Clamping RXVGA2 gain to %ddB\n", gain);
+ } else if (gain < BLADERF_RXVGA2_GAIN_MIN) {
+ gain = BLADERF_RXVGA2_GAIN_MIN;
+ log_info("Clamping RXVGA2 gain to %ddB\n", gain);
+ }
+
+ /* 3 dB per register code */
+ return LMS_WRITE(dev, 0x65, gain / 3);
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_rxvga2_get_gain(struct bladerf *dev, int *gain)
+{
+
+ uint8_t data;
+ const int status = LMS_READ(dev, 0x65, &data);
+
+ if (status == 0) {
+ /* 3 dB per code */
+ data *= 3;
+ *gain = data;
+ }
+
+ return status;
+}
+#endif
+
+int lms_select_pa(struct bladerf *dev, lms_pa pa)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x44, &data);
+
+ /* Disable PA1, PA2, and AUX PA - we'll enable as requested below. */
+ data &= ~0x1C;
+
+ /* AUX PA powered down */
+ data |= (1 << 1);
+
+ switch (pa) {
+ case PA_AUX:
+ data &= ~(1 << 1); /* Power up the AUX PA */
+ break;
+
+ case PA_1:
+ data |= (2 << 2); /* PA_EN[2:0] = 010 - Enable PA1 */
+ break;
+
+ case PA_2:
+ data |= (4 << 2); /* PA_EN[2:0] = 100 - Enable PA2 */
+ break;
+
+ case PA_NONE:
+ break;
+
+ default:
+ assert(!"Invalid PA selection");
+ status = BLADERF_ERR_INVAL;
+ }
+
+ if (status == 0) {
+ status = LMS_WRITE(dev, 0x44, data);
+ }
+
+ return status;
+
+};
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_peakdetect_enable(struct bladerf *dev, bool enable)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x44, &data);
+
+ if (status == 0) {
+ if (enable) {
+ data &= ~(1 << 0);
+ } else {
+ data |= (1 << 0);
+ }
+ status = LMS_WRITE(dev, 0x44, data);
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_enable_rffe(struct bladerf *dev, bladerf_module module, bool enable)
+{
+ int status;
+ uint8_t data;
+ uint8_t addr = (module == BLADERF_MODULE_TX ? 0x40 : 0x70);
+
+ status = LMS_READ(dev, addr, &data);
+ if (status == 0) {
+
+ if (module == BLADERF_MODULE_TX) {
+ if (enable) {
+ data |= (1 << 1);
+ } else {
+ data &= ~(1 << 1);
+ }
+ } else {
+ if (enable) {
+ data |= (1 << 0);
+ } else {
+ data &= ~(1 << 0);
+ }
+ }
+
+ status = LMS_WRITE(dev, addr, data);
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_txvga2_set_gain(struct bladerf *dev, int gain_int)
+{
+ int status;
+ uint8_t data;
+ int8_t gain;
+
+ if (gain_int > BLADERF_TXVGA2_GAIN_MAX) {
+ gain = BLADERF_TXVGA2_GAIN_MAX;
+ log_info("Clamping TXVGA2 gain to %ddB\n", gain);
+ } else if (gain_int < BLADERF_TXVGA2_GAIN_MIN) {
+ gain = 0;
+ log_info("Clamping TXVGA2 gain to %ddB\n", gain);
+ } else {
+ gain = gain_int;
+ }
+
+ status = LMS_READ(dev, 0x45, &data);
+ if (status == 0) {
+ data &= ~(0x1f << 3);
+ data |= ((gain & 0x1f) << 3);
+ status = LMS_WRITE(dev, 0x45, data);
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_txvga2_get_gain(struct bladerf *dev, int *gain)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x45, &data);
+
+ if (status == 0) {
+ *gain = (data >> 3) & 0x1f;
+
+ /* Register values of 25-31 all correspond to 25 dB */
+ if (*gain > 25) {
+ *gain = 25;
+ }
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_txvga1_set_gain(struct bladerf *dev, int gain_int)
+{
+ int8_t gain;
+
+ if (gain_int < BLADERF_TXVGA1_GAIN_MIN) {
+ gain = BLADERF_TXVGA1_GAIN_MIN;
+ log_info("Clamping TXVGA1 gain to %ddB\n", gain);
+ } else if (gain_int > BLADERF_TXVGA1_GAIN_MAX) {
+ gain = BLADERF_TXVGA1_GAIN_MAX;
+ log_info("Clamping TXVGA1 gain to %ddB\n", gain);
+ } else {
+ gain = gain_int;
+ }
+
+ /* Apply offset to convert gain to register table index */
+ gain = (gain + 35);
+
+ /* Since 0x41 is only VGA1GAIN, we don't need to RMW */
+ return LMS_WRITE(dev, 0x41, gain);
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_txvga1_get_gain(struct bladerf *dev, int *gain)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x41, &data);
+ if (status == 0) {
+ /* Clamp to max value */
+ data = data & 0x1f;
+
+ /* Convert table index to value */
+ *gain = data - 35;
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static inline int enable_lna_power(struct bladerf *dev, bool enable)
+{
+ int status;
+ uint8_t regval;
+
+ /* Magic test register to power down LNAs */
+ status = LMS_READ(dev, 0x7d, &regval);
+ if (status != 0) {
+ return status;
+ }
+
+ if (enable) {
+ regval &= ~(1 << 0);
+ } else {
+ regval |= (1 << 0);
+ }
+
+ status = LMS_WRITE(dev, 0x7d, regval);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Decode test registers */
+ status = LMS_READ(dev, 0x70, &regval);
+ if (status != 0) {
+ return status;
+ }
+
+ if (enable) {
+ regval &= ~(1 << 1);
+ } else {
+ regval |= (1 << 1);
+ }
+
+ return LMS_WRITE(dev, 0x70, regval);
+}
+#endif
+
+/* Power up/down RF loopback switch */
+#ifndef BLADERF_NIOS_BUILD
+static inline int enable_rf_loopback_switch(struct bladerf *dev, bool enable)
+{
+ int status;
+ uint8_t regval;
+
+ status = LMS_READ(dev, 0x0b, &regval);
+ if (status != 0) {
+ return status;
+ }
+
+ if (enable) {
+ regval |= (1 << 0);
+ } else {
+ regval &= ~(1 << 0);
+ }
+
+ return LMS_WRITE(dev, 0x0b, regval);
+}
+#endif
+
+
+/* Configure TX-side of loopback */
+#ifndef BLADERF_NIOS_BUILD
+static int loopback_tx(struct bladerf *dev, bladerf_loopback mode)
+{
+ int status = 0;
+
+ switch(mode) {
+ case BLADERF_LB_BB_TXLPF_RXVGA2:
+ case BLADERF_LB_BB_TXLPF_RXLPF:
+ case BLADERF_LB_BB_TXVGA1_RXVGA2:
+ case BLADERF_LB_BB_TXVGA1_RXLPF:
+ break;
+
+ case BLADERF_LB_RF_LNA1:
+ case BLADERF_LB_RF_LNA2:
+ case BLADERF_LB_RF_LNA3:
+ status = lms_select_pa(dev, PA_AUX);
+ break;
+
+ case BLADERF_LB_NONE:
+ {
+ struct lms_freq f;
+
+ /* Restore proper settings (PA) for this frequency */
+ status = lms_get_frequency(dev, BLADERF_MODULE_TX, &f);
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_set_frequency(dev, BLADERF_MODULE_TX,
+ lms_frequency_to_hz(&f));
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_select_band(dev, BLADERF_MODULE_TX,
+ lms_frequency_to_hz(&f) < BLADERF1_BAND_HIGH);
+ break;
+ }
+
+ default:
+ assert(!"Invalid loopback mode encountered");
+ status = BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
+#endif
+
+/* Configure RX-side of loopback */
+#ifndef BLADERF_NIOS_BUILD
+static int loopback_rx(struct bladerf *dev, bladerf_loopback mode)
+{
+ int status;
+ bladerf_lpf_mode lpf_mode;
+ uint8_t lna;
+ uint8_t regval;
+
+ status = lms_lpf_get_mode(dev, BLADERF_MODULE_RX, &lpf_mode);
+ if (status != 0) {
+ return status;
+ }
+
+ switch (mode) {
+ case BLADERF_LB_BB_TXLPF_RXVGA2:
+ case BLADERF_LB_BB_TXVGA1_RXVGA2:
+
+ /* Ensure RXVGA2 is enabled */
+ status = lms_rxvga2_enable(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ /* RXLPF must be disabled */
+ status = lms_lpf_set_mode(dev, BLADERF_MODULE_RX,
+ BLADERF_LPF_DISABLED);
+ if (status != 0) {
+ return status;
+ }
+ break;
+
+ case BLADERF_LB_BB_TXLPF_RXLPF:
+ case BLADERF_LB_BB_TXVGA1_RXLPF:
+
+ /* RXVGA1 must be disabled */
+ status = lms_rxvga1_enable(dev, false);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Enable the RXLPF if needed */
+ if (lpf_mode == BLADERF_LPF_DISABLED) {
+ status = lms_lpf_set_mode(dev, BLADERF_MODULE_RX,
+ BLADERF_LPF_NORMAL);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ /* Ensure RXVGA2 is enabled */
+ status = lms_rxvga2_enable(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ break;
+
+ case BLADERF_LB_RF_LNA1:
+ case BLADERF_LB_RF_LNA2:
+ case BLADERF_LB_RF_LNA3:
+ lna = mode - BLADERF_LB_RF_LNA1 + 1;
+ assert(lna >= 1 && lna <= 3);
+
+ /* Power down LNAs */
+ status = enable_lna_power(dev, false);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Ensure RXVGA1 is enabled */
+ status = lms_rxvga1_enable(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Enable the RXLPF if needed */
+ if (lpf_mode == BLADERF_LPF_DISABLED) {
+ status = lms_lpf_set_mode(dev, BLADERF_MODULE_RX,
+ BLADERF_LPF_NORMAL);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ /* Ensure RXVGA2 is enabled */
+ status = lms_rxvga2_enable(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Select output buffer in RX PLL and select the desired LNA */
+ status = LMS_READ(dev, 0x25, &regval);
+ if (status != 0) {
+ return status;
+ }
+
+ regval &= ~0x03;
+ regval |= lna;
+
+ status = LMS_WRITE(dev, 0x25, regval);
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_select_lna(dev, (lms_lna) lna);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Enable RF loopback switch */
+ status = enable_rf_loopback_switch(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ break;
+
+ case BLADERF_LB_NONE:
+ {
+ struct lms_freq f;
+
+ /* Ensure all RX blocks are enabled */
+ status = lms_rxvga1_enable(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ if (lpf_mode == BLADERF_LPF_DISABLED) {
+ status = lms_lpf_set_mode(dev, BLADERF_MODULE_RX,
+ BLADERF_LPF_NORMAL);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ status = lms_rxvga2_enable(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Disable RF loopback switch */
+ status = enable_rf_loopback_switch(dev, false);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Power up LNAs */
+ status = enable_lna_power(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Restore proper settings (LNA, RX PLL) for this frequency */
+ status = lms_get_frequency(dev, BLADERF_MODULE_RX, &f);
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_set_frequency(dev, BLADERF_MODULE_RX,
+ lms_frequency_to_hz(&f));
+ if (status != 0) {
+ return status;
+ }
+
+
+ status = lms_select_band(dev, BLADERF_MODULE_RX,
+ lms_frequency_to_hz(&f) < BLADERF1_BAND_HIGH);
+ break;
+ }
+
+ default:
+ assert(!"Invalid loopback mode encountered");
+ status = BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
+#endif
+
+/* Configure "switches" in loopback path */
+#ifndef BLADERF_NIOS_BUILD
+static int loopback_path(struct bladerf *dev, bladerf_loopback mode)
+{
+ int status;
+ uint8_t loopbben, lben_lbrf;
+
+ status = LMS_READ(dev, 0x46, &loopbben);
+ if (status != 0) {
+ return status;
+ }
+
+ status = LMS_READ(dev, 0x08, &lben_lbrf);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Default to baseband loopback being disabled */
+ loopbben &= ~LOOBBBEN_MASK;
+
+ /* Default to RF and BB loopback options being disabled */
+ lben_lbrf &= ~(LBRFEN_MASK | LBEN_MASK);
+
+ switch(mode) {
+ case BLADERF_LB_BB_TXLPF_RXVGA2:
+ loopbben |= LOOPBBEN_TXLPF;
+ lben_lbrf |= LBEN_VGA2IN;
+ break;
+
+ case BLADERF_LB_BB_TXLPF_RXLPF:
+ loopbben |= LOOPBBEN_TXLPF;
+ lben_lbrf |= LBEN_LPFIN;
+ break;
+
+ case BLADERF_LB_BB_TXVGA1_RXVGA2:
+ loopbben |= LOOPBBEN_TXVGA;
+ lben_lbrf |= LBEN_VGA2IN;
+ break;
+
+ case BLADERF_LB_BB_TXVGA1_RXLPF:
+ loopbben |= LOOPBBEN_TXVGA;
+ lben_lbrf |= LBEN_LPFIN;
+ break;
+
+ case BLADERF_LB_RF_LNA1:
+ lben_lbrf |= LBRFEN_LNA1;
+ break;
+
+ case BLADERF_LB_RF_LNA2:
+ lben_lbrf |= LBRFEN_LNA2;
+ break;
+
+ case BLADERF_LB_RF_LNA3:
+ lben_lbrf |= LBRFEN_LNA3;
+ break;
+
+ case BLADERF_LB_NONE:
+ break;
+
+ default:
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = LMS_WRITE(dev, 0x46, loopbben);
+ if (status == 0) {
+ status = LMS_WRITE(dev, 0x08, lben_lbrf);
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_set_loopback_mode(struct bladerf *dev, bladerf_loopback mode)
+{
+ int status;
+
+ /* Verify a valid mode is provided before shutting anything down */
+ switch (mode) {
+ case BLADERF_LB_BB_TXLPF_RXVGA2:
+ case BLADERF_LB_BB_TXLPF_RXLPF:
+ case BLADERF_LB_BB_TXVGA1_RXVGA2:
+ case BLADERF_LB_BB_TXVGA1_RXLPF:
+ case BLADERF_LB_RF_LNA1:
+ case BLADERF_LB_RF_LNA2:
+ case BLADERF_LB_RF_LNA3:
+ case BLADERF_LB_NONE:
+ break;
+
+ default:
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Disable all PA/LNAs while entering loopback mode or making changes */
+ status = lms_select_pa(dev, PA_NONE);
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_select_lna(dev, LNA_NONE);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Disconnect loopback paths while we re-configure blocks */
+ status = loopback_path(dev, BLADERF_LB_NONE);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Configure the RX side of the loopback path */
+ status = loopback_rx(dev, mode);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Configure the TX side of the path */
+ status = loopback_tx(dev, mode);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Configure "switches" along the loopback path */
+ status = loopback_path(dev, mode);
+ if (status != 0) {
+ return status;
+ }
+
+ return 0;
+}
+#endif
+
+int lms_get_loopback_mode(struct bladerf *dev, bladerf_loopback *loopback)
+{
+ int status;
+ uint8_t lben_lbrfen, loopbben;
+
+
+ status = LMS_READ(dev, 0x08, &lben_lbrfen);
+ if (status != 0) {
+ return status;
+ }
+
+ status = LMS_READ(dev, 0x46, &loopbben);
+ if (status != 0) {
+ return status;
+ }
+
+ switch (lben_lbrfen & 0x7) {
+ case LBRFEN_LNA1:
+ *loopback = BLADERF_LB_RF_LNA1;
+ return 0;
+
+ case LBRFEN_LNA2:
+ *loopback = BLADERF_LB_RF_LNA2;
+ return 0;
+
+ case LBRFEN_LNA3:
+ *loopback = BLADERF_LB_RF_LNA3;
+ return 0;
+
+ default:
+ break;
+ }
+
+ switch (lben_lbrfen & LBEN_MASK) {
+ case LBEN_VGA2IN:
+ if (loopbben & LOOPBBEN_TXLPF) {
+ *loopback = BLADERF_LB_BB_TXLPF_RXVGA2;
+ return 0;
+ } else if (loopbben & LOOPBBEN_TXVGA) {
+ *loopback = BLADERF_LB_BB_TXVGA1_RXVGA2;
+ return 0;
+ }
+ break;
+
+ case LBEN_LPFIN:
+ if (loopbben & LOOPBBEN_TXLPF) {
+ *loopback = BLADERF_LB_BB_TXLPF_RXLPF;
+ return 0;
+ } else if (loopbben & LOOPBBEN_TXVGA) {
+ *loopback = BLADERF_LB_BB_TXVGA1_RXLPF;
+ return 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ *loopback = BLADERF_LB_NONE;
+ return 0;
+}
+
+/* Top level power down of the LMS */
+#ifndef BLADERF_NIOS_BUILD
+int lms_power_down(struct bladerf *dev)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x05, &data);
+ if (status == 0) {
+ data &= ~(1 << 4);
+ status = LMS_WRITE(dev, 0x05, data);
+ }
+
+ return status;
+}
+#endif
+
+/* Enable the PLL of a module */
+#ifndef BLADERF_NIOS_BUILD
+int lms_pll_enable(struct bladerf *dev, bladerf_module mod, bool enable)
+{
+ int status;
+ const uint8_t reg = (mod == BLADERF_MODULE_RX) ? 0x24 : 0x14;
+ uint8_t data;
+
+ status = LMS_READ(dev, reg, &data);
+ if (status == 0) {
+ if (enable) {
+ data |= (1 << 3);
+ } else {
+ data &= ~(1 << 3);
+ }
+ status = LMS_WRITE(dev, reg, data);
+ }
+
+ return status;
+}
+#endif
+
+/* Enable the RX subsystem */
+#ifndef BLADERF_NIOS_BUILD
+int lms_rx_enable(struct bladerf *dev, bool enable)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x05, &data);
+ if (status == 0) {
+ if (enable) {
+ data |= (1 << 2);
+ } else {
+ data &= ~(1 << 2);
+ }
+ status = LMS_WRITE(dev, 0x05, data);
+ }
+
+ return status;
+}
+#endif
+
+/* Enable the TX subsystem */
+#ifndef BLADERF_NIOS_BUILD
+int lms_tx_enable(struct bladerf *dev, bool enable)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x05, &data);
+
+ if (status == 0) {
+ if (enable) {
+ data |= (1 << 3);
+ } else {
+ data &= ~(1 << 3);
+ }
+ status = LMS_WRITE(dev, 0x05, data);
+ }
+
+ return status;
+}
+#endif
+
+/* Converts frequency structure to Hz */
+#ifndef BLADERF_NIOS_BUILD
+uint32_t lms_frequency_to_hz(struct lms_freq *f)
+{
+ uint64_t pll_coeff;
+ uint32_t div;
+
+ pll_coeff = (((uint64_t)f->nint) << 23) + f->nfrac;
+ div = (f->x << 23);
+
+ return (uint32_t)(((LMS_REFERENCE_HZ * pll_coeff) + (div >> 1)) / div);
+}
+#endif
+
+/* Print a frequency structure */
+#ifndef BLADERF_NIOS_BUILD
+void lms_print_frequency(struct lms_freq *f)
+{
+ log_verbose("---- Frequency ----\n");
+ log_verbose(" x : %d\n", f->x);
+ log_verbose(" nint : %d\n", f->nint);
+ log_verbose(" nfrac : %u\n", f->nfrac);
+ log_verbose(" freqsel : 0x%02x\n", f->freqsel);
+ log_verbose(" reference: %u\n", LMS_REFERENCE_HZ);
+ log_verbose(" freq : %u\n", lms_frequency_to_hz(f));
+}
+#define PRINT_FREQUENCY lms_print_frequency
+#else
+#define PRINT_FREQUENCY(f)
+#endif
+
+/* Get the frequency structure */
+#ifndef BLADERF_NIOS_BUILD
+int lms_get_frequency(struct bladerf *dev, bladerf_module mod,
+ struct lms_freq *f)
+{
+ const uint8_t base = (mod == BLADERF_MODULE_RX) ? 0x20 : 0x10;
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, base + 0, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ f->nint = ((uint16_t)data) << 1;
+
+ status = LMS_READ(dev, base + 1, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ f->nint |= (data & 0x80) >> 7;
+ f->nfrac = ((uint32_t)data & 0x7f) << 16;
+
+ status = LMS_READ(dev, base + 2, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ f->nfrac |= ((uint32_t)data)<<8;
+
+ status = LMS_READ(dev, base + 3, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ f->nfrac |= data;
+
+ status = LMS_READ(dev, base + 5, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ f->freqsel = (data>>2);
+ f->x = 1 << ((f->freqsel & 7) - 3);
+
+ status = LMS_READ(dev, base + 9, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ f->vcocap = data & 0x3f;
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_get_quick_tune(struct bladerf *dev,
+ bladerf_module mod,
+ struct bladerf_quick_tune *quick_tune)
+{
+ struct lms_freq f;
+ uint32_t val;
+ int status = lms_get_frequency(dev, mod, &f);
+ if (status == 0) {
+ quick_tune->freqsel = f.freqsel;
+ quick_tune->vcocap = f.vcocap;
+ quick_tune->nint = f.nint;
+ quick_tune->nfrac = f.nfrac;
+ quick_tune->xb_gpio = 0;
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (status != 0)
+ goto out;
+
+ if (dev->xb == BLADERF_XB_200) {
+ quick_tune->xb_gpio |= LMS_FREQ_XB_200_ENABLE;
+ if (mod == BLADERF_CHANNEL_RX(0)) {
+ quick_tune->xb_gpio |= LMS_FREQ_XB_200_MODULE_RX;
+ /* BLADERF_XB_CONFIG_RX_BYPASS_MASK */
+ quick_tune->xb_gpio |= ( (val & 0x30 ) >> 4)
+ << LMS_FREQ_XB_200_PATH_SHIFT;
+ /* BLADERF_XB_RX_MASK */
+ quick_tune->xb_gpio |= ( (val & 0x30000000 ) >> 28)
+ << LMS_FREQ_XB_200_FILTER_SW_SHIFT;
+ } else {
+ /* BLADERF_XB_CONFIG_TX_BYPASS_MASK */
+ quick_tune->xb_gpio |= ( (val & 0x0C ) >> 2)
+ << LMS_FREQ_XB_200_FILTER_SW_SHIFT;
+ /* BLADERF_XB_TX_MASK */
+ quick_tune->xb_gpio |= ( (val & 0x0C000000 ) >> 26)
+ << LMS_FREQ_XB_200_PATH_SHIFT;
+ }
+ }
+
+ quick_tune->flags = LMS_FREQ_FLAGS_FORCE_VCOCAP;
+
+ if (lms_frequency_to_hz(&f) < BLADERF1_BAND_HIGH) {
+ quick_tune->flags |= LMS_FREQ_FLAGS_LOW_BAND;
+ }
+ }
+
+out:
+ return status;
+}
+#endif
+
+static inline int get_vtune(struct bladerf *dev, uint8_t base, uint8_t delay,
+ uint8_t *vtune)
+{
+ int status;
+
+ if (delay != 0) {
+ VTUNE_BUSY_WAIT(delay);
+ }
+
+ status = LMS_READ(dev, base + 10, vtune);
+ *vtune >>= 6;
+
+ return status;
+}
+
+static inline int write_vcocap(struct bladerf *dev, uint8_t base,
+ uint8_t vcocap, uint8_t vcocap_reg_state)
+{
+ int status;
+
+ assert(vcocap <= VCOCAP_MAX_VALUE);
+ log_verbose("Writing VCOCAP=%u\n", vcocap);
+
+ status = LMS_WRITE(dev, base + 9, vcocap | vcocap_reg_state);
+
+ if (status != 0) {
+ log_debug("VCOCAP write failed: %d\n", status);
+ }
+
+ return status;
+}
+
+#define VTUNE_DELAY_LARGE 50
+#define VTUNE_DELAY_SMALL 25
+#define VTUNE_MAX_ITERATIONS 20
+
+#define VCO_HIGH 0x02
+#define VCO_NORM 0x00
+#define VCO_LOW 0x01
+
+#if defined(LOGGING_ENABLED) || defined(BLADERF_NIOS_DEBUG)
+static const char *vtune_str(uint8_t value) {
+ switch (value) {
+ case VCO_HIGH:
+ return "HIGH";
+
+ case VCO_NORM:
+ return "NORM";
+
+ case VCO_LOW:
+ return "LOW";
+
+ default:
+ return "INVALID";
+ }
+}
+#endif
+
+static int vtune_high_to_norm(struct bladerf *dev, uint8_t base,
+ uint8_t vcocap, uint8_t vcocap_reg_state,
+ uint8_t *vtune_high_limit)
+{
+ int status;
+ unsigned int i;
+ uint8_t vtune = 0xff;
+
+ for (i = 0; i < VTUNE_MAX_ITERATIONS; i++) {
+
+ if (vcocap >= VCOCAP_MAX_VALUE) {
+ *vtune_high_limit = VCOCAP_MAX_VALUE;
+ log_warning("%s: VCOCAP hit max value.\n", __FUNCTION__);
+ return 0;
+ }
+
+ vcocap++;
+
+ status = write_vcocap(dev, base, vcocap, vcocap_reg_state);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_vtune(dev, base, VTUNE_DELAY_SMALL, &vtune);
+ if (status != 0) {
+ return status;
+ }
+
+ if (vtune == VCO_NORM) {
+ *vtune_high_limit = vcocap - 1;
+ log_verbose("VTUNE NORM @ VCOCAP=%u\n", vcocap);
+ log_verbose("VTUNE HIGH @ VCOCAP=%u\n", *vtune_high_limit);
+ return 0;
+ }
+ }
+
+ log_error("VTUNE High->Norm loop failed to converge.\n");
+ return BLADERF_ERR_UNEXPECTED;
+}
+
+static int vtune_norm_to_high(struct bladerf *dev, uint8_t base,
+ uint8_t vcocap, uint8_t vcocap_reg_state,
+ uint8_t *vtune_high_limit)
+{
+ int status;
+ unsigned int i;
+ uint8_t vtune = 0xff;
+
+ for (i = 0; i < VTUNE_MAX_ITERATIONS; i++) {
+
+ if (vcocap == 0) {
+ *vtune_high_limit = 0;
+ log_warning("%s: VCOCAP hit min value.\n", __FUNCTION__);
+ return 0;
+ }
+
+ vcocap--;
+
+ status = write_vcocap(dev, base, vcocap, vcocap_reg_state);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_vtune(dev, base, VTUNE_DELAY_SMALL, &vtune);
+ if (status != 0) {
+ return status;
+ }
+
+ if (vtune == VCO_HIGH) {
+ *vtune_high_limit = vcocap;
+ log_verbose("VTUNE high @ VCOCAP=%u\n", *vtune_high_limit);
+ return 0;
+ }
+ }
+
+ log_error("VTUNE High->Norm loop failed to converge.\n");
+ return BLADERF_ERR_UNEXPECTED;
+}
+
+static int vtune_low_to_norm(struct bladerf *dev, uint8_t base,
+ uint8_t vcocap, uint8_t vcocap_reg_state,
+ uint8_t *vtune_low_limit)
+{
+ int status;
+ unsigned int i;
+ uint8_t vtune = 0xff;
+
+ for (i = 0; i < VTUNE_MAX_ITERATIONS; i++) {
+
+ if (vcocap == 0) {
+ *vtune_low_limit = 0;
+ log_warning("VCOCAP hit min value.\n");
+ return 0;
+ }
+
+ vcocap--;
+
+ status = write_vcocap(dev, base, vcocap, vcocap_reg_state);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_vtune(dev, base, VTUNE_DELAY_SMALL, &vtune);
+ if (status != 0) {
+ return status;
+ }
+
+ if (vtune == VCO_NORM) {
+ *vtune_low_limit = vcocap + 1;
+ log_verbose("VTUNE NORM @ VCOCAP=%u\n", vcocap);
+ log_verbose("VTUNE LOW @ VCOCAP=%u\n", *vtune_low_limit);
+ return 0;
+ }
+ }
+
+ log_error("VTUNE Low->Norm loop failed to converge.\n");
+ return BLADERF_ERR_UNEXPECTED;
+}
+
+/* Wait for VTUNE to reach HIGH or LOW. NORM is not a valid option here */
+static int wait_for_vtune_value(struct bladerf *dev,
+ uint8_t base, uint8_t target_value,
+ uint8_t *vcocap, uint8_t vcocap_reg_state)
+{
+ uint8_t vtune;
+ unsigned int i;
+ int status = 0;
+ const unsigned int max_retries = 15;
+ const uint8_t limit = (target_value == VCO_HIGH) ? 0 : VCOCAP_MAX_VALUE;
+ int8_t inc = (target_value == VCO_HIGH) ? -1 : 1;
+
+ assert(target_value == VCO_HIGH || target_value == VCO_LOW);
+
+ for (i = 0; i < max_retries; i++) {
+ status = get_vtune(dev, base, 0, &vtune);
+ if (status != 0) {
+ return status;
+ }
+
+ if (vtune == target_value) {
+ log_verbose("VTUNE reached %s at iteration %u\n",
+ vtune_str(target_value), i);
+ return 0;
+ } else {
+ log_verbose("VTUNE was %s. Waiting and retrying...\n",
+ vtune_str(vtune));
+
+ VTUNE_BUSY_WAIT(10);
+ }
+ }
+
+ log_debug("Timed out while waiting for VTUNE=%s. Walking VCOCAP...\n",
+ vtune_str(target_value));
+
+ while (*vcocap != limit) {
+ *vcocap += inc;
+
+ status = write_vcocap(dev, base, *vcocap, vcocap_reg_state);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_vtune(dev, base, VTUNE_DELAY_SMALL, &vtune);
+ if (status != 0) {
+ return status;
+ } else if (vtune == target_value) {
+ log_debug("VTUNE=%s reached with VCOCAP=%u\n",
+ vtune_str(vtune), *vcocap);
+ return 0;
+ }
+ }
+
+ log_warning("VTUNE did not reach %s. Tuning may not be nominal.\n",
+ vtune_str(target_value));
+
+# ifdef ERROR_ON_NO_VTUNE_LIMIT
+ return BLADERF_ERR_UNEXPECTED;
+# else
+ return 0;
+# endif
+}
+
+/* These values are the max counts we've seen (experimentally) between
+ * VCOCAP values that converged */
+#define VCOCAP_MAX_LOW_HIGH 12
+
+/* This function assumes an initial VCOCAP estimate has already been written.
+ *
+ * Remember, increasing VCOCAP works towards a lower voltage, and vice versa:
+ * From experimental observations, we don't expect to see the "normal" region
+ * extend beyond 16 counts.
+ *
+ * VCOCAP = 0 VCOCAP=63
+ * / \
+ * v v
+ * |----High-----[ Normal ]----Low----| VTUNE voltage comparison
+ *
+ * The VTUNE voltage can be found on R263 (RX) or R265 (Tx). (They're under the
+ * can shielding the LMS6002D.) By placing a scope probe on these and retuning,
+ * you should be able to see the relationship between VCOCAP changes and
+ * the voltage changes.
+ */
+static int tune_vcocap(struct bladerf *dev, uint8_t vcocap_est,
+ uint8_t base, uint8_t vcocap_reg_state,
+ uint8_t *vcocap_result)
+{
+ int status;
+ uint8_t vcocap = vcocap_est;
+ uint8_t vtune;
+ uint8_t vtune_high_limit; /* Where VCOCAP puts use into VTUNE HIGH region */
+ uint8_t vtune_low_limit; /* Where VCOCAP puts use into VTUNE HIGH region */
+
+ RESET_BUSY_WAIT_COUNT();
+
+ vtune_high_limit = VCOCAP_MAX_VALUE;
+ vtune_low_limit = 0;
+
+ status = get_vtune(dev, base, VTUNE_DELAY_LARGE, &vtune);
+ if (status != 0) {
+ return status;
+ }
+
+ switch (vtune) {
+ case VCO_HIGH:
+ log_verbose("Estimate HIGH: Walking down to NORM.\n");
+ status = vtune_high_to_norm(dev, base, vcocap, vcocap_reg_state,
+ &vtune_high_limit);
+ break;
+
+ case VCO_NORM:
+ log_verbose("Estimate NORM: Walking up to HIGH.\n");
+ status = vtune_norm_to_high(dev, base, vcocap, vcocap_reg_state,
+ &vtune_high_limit);
+ break;
+
+ case VCO_LOW:
+ log_verbose("Estimate LOW: Walking down to NORM.\n");
+ status = vtune_low_to_norm(dev, base, vcocap, vcocap_reg_state,
+ &vtune_low_limit);
+ break;
+ }
+
+ if (status != 0) {
+ return status;
+ } else if (vtune_high_limit != VCOCAP_MAX_VALUE) {
+
+ /* We determined our VTUNE HIGH limit. Try to force ourselves to the
+ * LOW limit and then walk back up to norm from there.
+ *
+ * Reminder - There's an inverse relationship between VTUNE and VCOCAP
+ */
+ switch (vtune) {
+ case VCO_HIGH:
+ case VCO_NORM:
+ if ( ((int) vtune_high_limit + VCOCAP_MAX_LOW_HIGH) < VCOCAP_MAX_VALUE) {
+ vcocap = vtune_high_limit + VCOCAP_MAX_LOW_HIGH;
+ } else {
+ vcocap = VCOCAP_MAX_VALUE;
+ log_verbose("Clamping VCOCAP to %u.\n", vcocap);
+ }
+ break;
+
+ default:
+ assert(!"Invalid state");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ status = write_vcocap(dev, base, vcocap, vcocap_reg_state);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose("Waiting for VTUNE LOW @ VCOCAP=%u,\n", vcocap);
+ status = wait_for_vtune_value(dev, base, VCO_LOW,
+ &vcocap, vcocap_reg_state);
+
+ if (status == 0) {
+ log_verbose("Walking VTUNE LOW to NORM from VCOCAP=%u,\n", vcocap);
+ status = vtune_low_to_norm(dev, base, vcocap, vcocap_reg_state,
+ &vtune_low_limit);
+ }
+ } else {
+
+ /* We determined our VTUNE LOW limit. Try to force ourselves up to
+ * the HIGH limit and then walk down to NORM from there
+ *
+ * Reminder - There's an inverse relationship between VTUNE and VCOCAP
+ */
+ switch (vtune) {
+ case VCO_LOW:
+ case VCO_NORM:
+ if ( ((int) vtune_low_limit - VCOCAP_MAX_LOW_HIGH) > 0) {
+ vcocap = vtune_low_limit - VCOCAP_MAX_LOW_HIGH;
+ } else {
+ vcocap = 0;
+ log_verbose("Clamping VCOCAP to %u.\n", vcocap);
+ }
+ break;
+
+ default:
+ assert(!"Invalid state");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ status = write_vcocap(dev, base, vcocap, vcocap_reg_state);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose("Waiting for VTUNE HIGH @ VCOCAP=%u\n", vcocap);
+ status = wait_for_vtune_value(dev, base, VCO_HIGH,
+ &vcocap, vcocap_reg_state);
+
+ if (status == 0) {
+ log_verbose("Walking VTUNE HIGH to NORM from VCOCAP=%u,\n", vcocap);
+ status = vtune_high_to_norm(dev, base, vcocap, vcocap_reg_state,
+ &vtune_high_limit);
+ }
+ }
+
+ if (status == 0) {
+ vcocap = vtune_high_limit + (vtune_low_limit - vtune_high_limit) / 2;
+
+ log_verbose("VTUNE LOW: %u\n", vtune_low_limit);
+ log_verbose("VTUNE NORM: %u\n", vcocap);
+ log_verbose("VTUNE Est: %u (%d)\n",
+ vcocap_est, (int) vcocap_est - vcocap);
+ log_verbose("VTUNE HIGH: %u\n", vtune_high_limit);
+
+# if LMS_COUNT_BUSY_WAITS
+ log_verbose("Busy waits: %u\n", busy_wait_count);
+ log_verbose("Busy us: %u\n", busy_wait_duration);
+# endif
+
+ status = write_vcocap(dev, base, vcocap, vcocap_reg_state);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Inform the caller of what we converged to */
+ *vcocap_result = vcocap;
+
+ status = get_vtune(dev, base, VTUNE_DELAY_SMALL, &vtune);
+ if (status != 0) {
+ return status;
+ }
+
+ PRINT_BUSY_WAIT_INFO();
+
+ if (vtune != VCO_NORM) {
+ status = BLADERF_ERR_UNEXPECTED;
+ log_error("Final VCOCAP=%u is not in VTUNE NORM region.\n", vcocap);
+ }
+ }
+
+ return status;
+}
+
+int lms_select_band(struct bladerf *dev, bladerf_module module, bool low_band)
+{
+ int status;
+
+ /* If loopback mode disabled, avoid changing the PA or LNA selection,
+ * as these need to remain are powered down or disabled */
+ status = is_loopback_enabled(dev);
+ if (status < 0) {
+ return status;
+ } else if (status > 0) {
+ return 0;
+ }
+
+ if (module == BLADERF_MODULE_TX) {
+ lms_pa pa = low_band ? PA_1 : PA_2;
+ status = lms_select_pa(dev, pa);
+ } else {
+ lms_lna lna = low_band ? LNA_1 : LNA_2;
+ status = lms_select_lna(dev, lna);
+ }
+
+ return status;
+}
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_calculate_tuning_params(uint32_t freq, struct lms_freq *f)
+{
+ uint64_t vco_x;
+ uint64_t temp;
+ uint16_t nint;
+ uint32_t nfrac;
+ uint8_t freqsel = bands[0].value;
+ uint8_t i = 0;
+ const uint64_t ref_clock = LMS_REFERENCE_HZ;
+
+ /* Clamp out of range values */
+ if (freq < BLADERF_FREQUENCY_MIN) {
+ freq = BLADERF_FREQUENCY_MIN;
+ log_info("Clamping frequency to %uHz\n", freq);
+ } else if (freq > BLADERF_FREQUENCY_MAX) {
+ freq = BLADERF_FREQUENCY_MAX;
+ log_info("Clamping frequency to %uHz\n", freq);
+ }
+
+ /* Figure out freqsel */
+
+ while (i < ARRAY_SIZE(bands)) {
+ if ((freq >= bands[i].low) && (freq <= bands[i].high)) {
+ freqsel = bands[i].value;
+ break;
+ }
+ i++;
+ }
+
+ /* This condition should never occur. There's a bug if it does. */
+ if (i >= ARRAY_SIZE(bands)) {
+ log_critical("BUG: Failed to find frequency band information. "
+ "Setting frequency to %u Hz.\n", BLADERF_FREQUENCY_MIN);
+
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* Estimate our target VCOCAP value. */
+ f->vcocap = estimate_vcocap(freq, bands[i].low, bands[i].high);
+
+ /* Calculate integer portion of the frequency value */
+ vco_x = ((uint64_t)1) << ((freqsel & 7) - 3);
+ temp = (vco_x * freq) / ref_clock;
+ assert(temp <= UINT16_MAX);
+ nint = (uint16_t)temp;
+
+ temp = (1 << 23) * (vco_x * freq - nint * ref_clock);
+ temp = (temp + ref_clock / 2) / ref_clock;
+ assert(temp <= UINT32_MAX);
+ nfrac = (uint32_t)temp;
+
+ assert(vco_x <= UINT8_MAX);
+ f->x = (uint8_t)vco_x;
+ f->nint = nint;
+ f->nfrac = nfrac;
+ f->freqsel = freqsel;
+ f->xb_gpio = 0;
+ assert(ref_clock <= UINT32_MAX);
+
+ f->flags = 0;
+
+ if (freq < BLADERF1_BAND_HIGH) {
+ f->flags |= LMS_FREQ_FLAGS_LOW_BAND;
+ }
+
+ PRINT_FREQUENCY(f);
+
+ return 0;
+}
+#endif
+
+int lms_set_precalculated_frequency(struct bladerf *dev, bladerf_module mod,
+ struct lms_freq *f)
+{
+ /* Select the base address based on which PLL we are configuring */
+ const uint8_t base = (mod == BLADERF_MODULE_RX) ? 0x20 : 0x10;
+
+ uint8_t data;
+ uint8_t vcocap_reg_state;
+ int status, dsm_status;
+
+ /* Utilize atomic writes to the PLL registers, if possible. This
+ * "multiwrite" is indicated by the MSB being set. */
+# ifdef BLADERF_NIOS_BUILD
+ const uint8_t pll_base = base | 0x80;
+# else
+ const uint8_t pll_base =
+ have_cap(dev->board->get_capabilities(dev),
+ BLADERF_CAP_ATOMIC_NINT_NFRAC_WRITE) ?
+ (base | 0x80) : base;
+# endif
+
+ f->vcocap_result = 0xff;
+
+ /* Turn on the DSMs */
+ status = LMS_READ(dev, 0x09, &data);
+ if (status == 0) {
+ data |= 0x05;
+ status = LMS_WRITE(dev, 0x09, data);
+ }
+
+ if (status != 0) {
+ log_debug("Failed to turn on DSMs\n");
+ return status;
+ }
+
+ /* Write the initial vcocap estimate first to allow for adequate time for
+ * VTUNE to stabilize. We need to be sure to keep the upper bits of
+ * this register and perform a RMW, as bit 7 is VOVCOREG[0]. */
+ status = LMS_READ(dev, base + 9, &vcocap_reg_state);
+ if (status != 0) {
+ goto error;
+ }
+
+ vcocap_reg_state &= ~(0x3f);
+
+ status = write_vcocap(dev, base, f->vcocap, vcocap_reg_state);
+ if (status != 0) {
+ goto error;
+ }
+
+ status = write_pll_config(dev, mod, f->freqsel,
+ (f->flags & LMS_FREQ_FLAGS_LOW_BAND) != 0);
+ if (status != 0) {
+ goto error;
+ }
+
+ data = f->nint >> 1;
+ status = LMS_WRITE(dev, pll_base + 0, data);
+ if (status != 0) {
+ goto error;
+ }
+
+ data = ((f->nint & 1) << 7) | ((f->nfrac >> 16) & 0x7f);
+ status = LMS_WRITE(dev, pll_base + 1, data);
+ if (status != 0) {
+ goto error;
+ }
+
+ data = ((f->nfrac >> 8) & 0xff);
+ status = LMS_WRITE(dev, pll_base + 2, data);
+ if (status != 0) {
+ goto error;
+ }
+
+ data = (f->nfrac & 0xff);
+ status = LMS_WRITE(dev, pll_base + 3, data);
+ if (status != 0) {
+ goto error;
+ }
+
+ /* Perform tuning algorithm unless we've been instructed to just use
+ * the VCOCAP hint as-is. */
+ if (f->flags & LMS_FREQ_FLAGS_FORCE_VCOCAP) {
+ f->vcocap_result = f->vcocap;
+ } else {
+ /* Walk down VCOCAP values find an optimal values */
+ status = tune_vcocap(dev, f->vcocap, base, vcocap_reg_state,
+ &f->vcocap_result);
+ }
+
+error:
+ /* Turn off the DSMs */
+ dsm_status = LMS_READ(dev, 0x09, &data);
+ if (dsm_status == 0) {
+ data &= ~(0x05);
+ dsm_status = LMS_WRITE(dev, 0x09, data);
+ }
+
+ return (status == 0) ? dsm_status : status;
+}
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_dump_registers(struct bladerf *dev)
+{
+ int status = 0;
+ uint8_t data,i;
+ const uint16_t num_reg = sizeof(lms_reg_dumpset);
+
+ for (i = 0; i < num_reg; i++) {
+ status = LMS_READ(dev, lms_reg_dumpset[i], &data);
+ if (status != 0) {
+ log_debug("Failed to read LMS @ 0x%02x\n", lms_reg_dumpset[i]);
+ return status;
+ } else {
+ log_debug("LMS[0x%02x] = 0x%02x\n", lms_reg_dumpset[i], data);
+ }
+ }
+
+ return status;
+}
+#endif
+
+/* Reference LMS6002D calibration guide, section 4.1 flow chart */
+#ifndef BLADERF_NIOS_BUILD
+static int lms_dc_cal_loop(struct bladerf *dev, uint8_t base,
+ uint8_t cal_address, uint8_t dc_cntval,
+ uint8_t *dc_regval)
+{
+ int status;
+ uint8_t i, val;
+ bool done = false;
+ const unsigned int max_cal_count = 25;
+
+ log_debug("Calibrating module %2.2x:%2.2x\n", base, cal_address);
+
+ /* Set the calibration address for the block, and start it up */
+ status = LMS_READ(dev, base + 0x03, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ val &= ~(0x07);
+ val |= cal_address&0x07;
+
+ status = LMS_WRITE(dev, base + 0x03, val);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Set and latch the DC_CNTVAL */
+ status = LMS_WRITE(dev, base + 0x02, dc_cntval);
+ if (status != 0) {
+ return status;
+ }
+
+ val |= (1 << 4);
+ status = LMS_WRITE(dev, base + 0x03, val);
+ if (status != 0) {
+ return status;
+ }
+
+ val &= ~(1 << 4);
+ status = LMS_WRITE(dev, base + 0x03, val);
+ if (status != 0) {
+ return status;
+ }
+
+
+ /* Start the calibration by toggling DC_START_CLBR */
+ val |= (1 << 5);
+ status = LMS_WRITE(dev, base + 0x03, val);
+ if (status != 0) {
+ return status;
+ }
+
+ val &= ~(1 << 5);
+ status = LMS_WRITE(dev, base + 0x03, val);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Main loop checking the calibration */
+ for (i = 0 ; i < max_cal_count && !done; i++) {
+ /* Read active low DC_CLBR_DONE */
+ status = LMS_READ(dev, base + 0x01, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Check if calibration is done */
+ if (((val >> 1) & 1) == 0) {
+ done = true;
+ /* Per LMS FAQ item 4.7, we should check DC_REG_VAL, as
+ * DC_LOCK is not a reliable indicator */
+ status = LMS_READ(dev, base, dc_regval);
+ if (status == 0) {
+ *dc_regval &= 0x3f;
+ }
+ }
+ }
+
+ if (done == false) {
+ log_warning("DC calibration loop did not converge.\n");
+ status = BLADERF_ERR_UNEXPECTED;
+ } else {
+ log_debug( "DC_REGVAL: %d\n", *dc_regval );
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static inline int dc_cal_backup(struct bladerf *dev,
+ bladerf_cal_module module,
+ struct dc_cal_state *state)
+{
+ int status;
+
+ memset(state, 0, sizeof(state[0]));
+
+ status = LMS_READ(dev, 0x09, &state->clk_en);
+ if (status != 0) {
+ return status;
+ }
+
+ if (module == BLADERF_DC_CAL_RX_LPF || module == BLADERF_DC_CAL_RXVGA2) {
+ status = LMS_READ(dev, 0x72, &state->reg0x72);
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_lna_get_gain(dev, &state->lna_gain);
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_rxvga1_get_gain(dev, &state->rxvga1_gain);
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_rxvga2_get_gain(dev, &state->rxvga2_gain);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ return 0;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static int dc_cal_module_init(struct bladerf *dev,
+ bladerf_cal_module module,
+ struct dc_cal_state *state)
+{
+ int status;
+ uint8_t cal_clock;
+ uint8_t val;
+
+ switch (module) {
+ case BLADERF_DC_CAL_LPF_TUNING:
+ cal_clock = (1 << 5); /* CLK_EN[5] - LPF CAL Clock */
+ state->base_addr = 0x00;
+ state->num_submodules = 1;
+ break;
+
+ case BLADERF_DC_CAL_TX_LPF:
+ cal_clock = (1 << 1); /* CLK_EN[1] - TX LPF DCCAL Clock */
+ state->base_addr = 0x30;
+ state->num_submodules = 2;
+ break;
+
+ case BLADERF_DC_CAL_RX_LPF:
+ cal_clock = (1 << 3); /* CLK_EN[3] - RX LPF DCCAL Clock */
+ state->base_addr = 0x50;
+ state->num_submodules = 2;
+ break;
+
+ case BLADERF_DC_CAL_RXVGA2:
+ cal_clock = (1 << 4); /* CLK_EN[4] - RX VGA2 DCCAL Clock */
+ state->base_addr = 0x60;
+ state->num_submodules = 5;
+ break;
+
+ default:
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Enable the appropriate clock based on the module */
+ status = LMS_WRITE(dev, 0x09, state->clk_en | cal_clock);
+ if (status != 0) {
+ return status;
+ }
+
+ switch (module) {
+
+ case BLADERF_DC_CAL_LPF_TUNING:
+ /* Nothing special to do */
+ break;
+
+ case BLADERF_DC_CAL_RX_LPF:
+ case BLADERF_DC_CAL_RXVGA2:
+ /* FAQ 5.26 (rev 1.0r10) notes that the DC comparators should be
+ * powered up when performing DC calibration, and then powered down
+ * afterwards to improve receiver linearity */
+ if (module == BLADERF_DC_CAL_RXVGA2) {
+ status = lms_clear(dev, 0x6e, (3 << 6));
+ if (status != 0) {
+ return status;
+ }
+ } else {
+ /* Power up RX LPF DC calibration comparator */
+ status = lms_clear(dev, 0x5f, (1 << 7));
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ /* Disconnect LNA from the RXMIX input by opening up the
+ * INLOAD_LNA_RXFE switch. This should help reduce external
+ * interference while calibrating */
+ val = state->reg0x72 & ~(1 << 7);
+ status = LMS_WRITE(dev, 0x72, val);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Attempt to calibrate at max gain. */
+ status = lms_lna_set_gain(dev, BLADERF_LNA_GAIN_MAX);
+ if (status != 0) {
+ return status;
+ }
+
+ state->rxvga1_curr_gain = BLADERF_RXVGA1_GAIN_MAX;
+ status = lms_rxvga1_set_gain(dev, state->rxvga1_curr_gain);
+ if (status != 0) {
+ return status;
+ }
+
+ state->rxvga2_curr_gain = BLADERF_RXVGA2_GAIN_MAX;
+ status = lms_rxvga2_set_gain(dev, state->rxvga2_curr_gain);
+ if (status != 0) {
+ return status;
+ }
+
+ break;
+
+
+ case BLADERF_DC_CAL_TX_LPF:
+ /* FAQ item 4.1 notes that the DAC should be turned off or set
+ * to generate minimum DC */
+ status = lms_set(dev, 0x36, (1 << 7));
+ if (status != 0) {
+ return status;
+ }
+
+ /* Ensure TX LPF DC calibration comparator is powered up */
+ status = lms_clear(dev, 0x3f, (1 << 7));
+ if (status != 0) {
+ return status;
+ }
+ break;
+
+ default:
+ assert(!"Invalid module");
+ status = BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
+#endif
+
+/* The RXVGA2 items here are based upon Lime Microsystems' recommendations
+ * in their "Improving RxVGA2 DC Offset Calibration Stability" Document:
+ * https://groups.google.com/group/limemicro-opensource/attach/19b675d099a22b89/Improving%20RxVGA2%20DC%20Offset%20Calibration%20Stability_v1.pdf?part=0.1&authuser=0
+ *
+ * This function assumes that the submodules are preformed in a consecutive
+ * and increasing order, as outlined in the above document.
+ */
+#ifndef BLADERF_NIOS_BUILD
+static int dc_cal_submodule(struct bladerf *dev,
+ bladerf_cal_module module,
+ unsigned int submodule,
+ struct dc_cal_state *state,
+ bool *converged)
+{
+ int status;
+ uint8_t val, dc_regval;
+
+ *converged = false;
+
+ if (module == BLADERF_DC_CAL_RXVGA2) {
+ switch (submodule) {
+ case 0:
+ /* Reset VGA2GAINA and VGA2GAINB to the default power-on values,
+ * in case we're retrying this calibration due to one of the
+ * later submodules failing. For the same reason, RXVGA2 decode
+ * is disabled; it is not used for the RC reference module (0)
+ */
+
+ /* Disable RXVGA2 DECODE */
+ status = lms_clear(dev, 0x64, (1 << 0));
+ if (status != 0) {
+ return status;
+ }
+
+ /* VGA2GAINA = 0, VGA2GAINB = 0 */
+ status = LMS_WRITE(dev, 0x68, 0x01);
+ if (status != 0) {
+ return status;
+ }
+ break;
+
+ case 1:
+ /* Setup for Stage 1 I and Q channels (submodules 1 and 2) */
+
+ /* Set to direct control signals: RXVGA2 Decode = 1 */
+ status = lms_set(dev, 0x64, (1 << 0));
+ if (status != 0) {
+ return status;
+ }
+
+ /* VGA2GAINA = 0110, VGA2GAINB = 0 */
+ val = 0x06;
+ status = LMS_WRITE(dev, 0x68, val);
+ if (status != 0) {
+ return status;
+ }
+ break;
+
+ case 2:
+ /* No additional changes needed - covered by previous execution
+ * of submodule == 1. */
+ break;
+
+ case 3:
+ /* Setup for Stage 2 I and Q channels (submodules 3 and 4) */
+
+ /* VGA2GAINA = 0, VGA2GAINB = 0110 */
+ val = 0x60;
+ status = LMS_WRITE(dev, 0x68, val);
+ if (status != 0) {
+ return status;
+ }
+ break;
+
+ case 4:
+ /* No additional changes needed - covered by execution
+ * of submodule == 3 */
+ break;
+
+ default:
+ assert(!"Invalid submodule");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+ }
+
+ status = lms_dc_cal_loop(dev, state->base_addr, submodule, 31, &dc_regval);
+ if (status != 0) {
+ return status;
+ }
+
+ if (dc_regval == 31) {
+ log_debug("DC_REGVAL suboptimal value - retrying DC cal loop.\n");
+
+ /* FAQ item 4.7 indcates that can retry with DC_CNTVAL reset */
+ status = lms_dc_cal_loop(dev, state->base_addr, submodule, 0, &dc_regval);
+ if (status != 0) {
+ return status;
+ } else if (dc_regval == 0) {
+ log_debug("Bad DC_REGVAL detected. DC cal failed.\n");
+ return 0;
+ }
+ }
+
+ if (module == BLADERF_DC_CAL_LPF_TUNING) {
+ /* Special case for LPF tuning module where results are
+ * written to TX/RX LPF DCCAL */
+
+ /* Set the DC level to RX and TX DCCAL modules */
+ status = LMS_READ(dev, 0x35, &val);
+ if (status == 0) {
+ val &= ~(0x3f);
+ val |= dc_regval;
+ status = LMS_WRITE(dev, 0x35, val);
+ }
+
+ if (status != 0) {
+ return status;
+ }
+
+ status = LMS_READ(dev, 0x55, &val);
+ if (status == 0) {
+ val &= ~(0x3f);
+ val |= dc_regval;
+ status = LMS_WRITE(dev, 0x55, val);
+ }
+
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ *converged = true;
+ return 0;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static int dc_cal_retry_adjustment(struct bladerf *dev,
+ bladerf_cal_module module,
+ struct dc_cal_state *state,
+ bool *limit_reached)
+{
+ int status = 0;
+
+ switch (module) {
+ case BLADERF_DC_CAL_LPF_TUNING:
+ case BLADERF_DC_CAL_TX_LPF:
+ /* Nothing to adjust here */
+ *limit_reached = true;
+ break;
+
+ case BLADERF_DC_CAL_RX_LPF:
+ if (state->rxvga1_curr_gain > BLADERF_RXVGA1_GAIN_MIN) {
+ state->rxvga1_curr_gain -= 1;
+ log_debug("Retrying DC cal with RXVGA1=%d\n",
+ state->rxvga1_curr_gain);
+ status = lms_rxvga1_set_gain(dev, state->rxvga1_curr_gain);
+ } else {
+ *limit_reached = true;
+ }
+ break;
+
+ case BLADERF_DC_CAL_RXVGA2:
+ if (state->rxvga1_curr_gain > BLADERF_RXVGA1_GAIN_MIN) {
+ state->rxvga1_curr_gain -= 1;
+ log_debug("Retrying DC cal with RXVGA1=%d\n",
+ state->rxvga1_curr_gain);
+ status = lms_rxvga1_set_gain(dev, state->rxvga1_curr_gain);
+ } else if (state->rxvga2_curr_gain > BLADERF_RXVGA2_GAIN_MIN) {
+ state->rxvga2_curr_gain -= 3;
+ log_debug("Retrying DC cal with RXVGA2=%d\n",
+ state->rxvga2_curr_gain);
+ status = lms_rxvga2_set_gain(dev, state->rxvga2_curr_gain);
+ } else {
+ *limit_reached = true;
+ }
+ break;
+
+ default:
+ *limit_reached = true;
+ assert(!"Invalid module");
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ if (*limit_reached) {
+ log_debug("DC Cal retry limit reached\n");
+ }
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static int dc_cal_module_deinit(struct bladerf *dev,
+ bladerf_cal_module module,
+ struct dc_cal_state *state)
+{
+ int status = 0;
+
+ switch (module) {
+ case BLADERF_DC_CAL_LPF_TUNING:
+ /* Nothing special to do here */
+ break;
+
+ case BLADERF_DC_CAL_RX_LPF:
+ /* Power down RX LPF calibration comparator */
+ status = lms_set(dev, 0x5f, (1 << 7));
+ if (status != 0) {
+ return status;
+ }
+ break;
+
+ case BLADERF_DC_CAL_RXVGA2:
+ /* Restore defaults: VGA2GAINA = 1, VGA2GAINB = 0 */
+ status = LMS_WRITE(dev, 0x68, 0x01);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Disable decode control signals: RXVGA2 Decode = 0 */
+ status = lms_clear(dev, 0x64, (1 << 0));
+ if (status != 0) {
+ return status;
+ }
+
+ /* Power DC comparitors down, per FAQ 5.26 (rev 1.0r10) */
+ status = lms_set(dev, 0x6e, (3 << 6));
+ if (status != 0) {
+ return status;
+ }
+ break;
+
+ case BLADERF_DC_CAL_TX_LPF:
+ /* Power down TX LPF DC calibration comparator */
+ status = lms_set(dev, 0x3f, (1 << 7));
+ if (status != 0) {
+ return status;
+ }
+
+ /* Re-enable the DACs */
+ status = lms_clear(dev, 0x36, (1 << 7));
+ if (status != 0) {
+ return status;
+ }
+ break;
+
+ default:
+ assert(!"Invalid module");
+ status = BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static inline int dc_cal_restore(struct bladerf *dev,
+ bladerf_cal_module module,
+ struct dc_cal_state *state)
+{
+ int status, ret;
+ ret = 0;
+
+ status = LMS_WRITE(dev, 0x09, state->clk_en);
+ if (status != 0) {
+ ret = status;
+ }
+
+ if (module == BLADERF_DC_CAL_RX_LPF || module == BLADERF_DC_CAL_RXVGA2) {
+ status = LMS_WRITE(dev, 0x72, state->reg0x72);
+ if (status != 0 && ret == 0) {
+ ret = status;
+ }
+
+ status = lms_lna_set_gain(dev, state->lna_gain);
+ if (status != 0 && ret == 0) {
+ ret = status;
+ }
+
+ status = lms_rxvga1_set_gain(dev, state->rxvga1_gain);
+ if (status != 0 && ret == 0) {
+ ret = status;
+ }
+
+ status = lms_rxvga2_set_gain(dev, state->rxvga2_gain);
+ if (status != 0) {
+ ret = status;
+ }
+ }
+
+ return ret;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static inline int dc_cal_module(struct bladerf *dev,
+ bladerf_cal_module module,
+ struct dc_cal_state *state,
+ bool *converged)
+{
+ unsigned int i;
+ int status = 0;
+
+ *converged = true;
+
+ for (i = 0; i < state->num_submodules && *converged && status == 0; i++) {
+ status = dc_cal_submodule(dev, module, i, state, converged);
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_calibrate_dc(struct bladerf *dev, bladerf_cal_module module)
+{
+ int status, tmp_status;
+ struct dc_cal_state state;
+ bool converged, limit_reached;
+
+ status = dc_cal_backup(dev, module, &state);
+ if (status != 0) {
+ return status;
+ }
+
+ status = dc_cal_module_init(dev, module, &state);
+ if (status != 0) {
+ goto error;
+ }
+
+ converged = false;
+ limit_reached = false;
+
+ while (!converged && !limit_reached && status == 0) {
+ status = dc_cal_module(dev, module, &state, &converged);
+
+ if (status == 0 && !converged) {
+ status = dc_cal_retry_adjustment(dev, module, &state,
+ &limit_reached);
+ }
+ }
+
+ if (!converged && status == 0) {
+ log_warning("DC Calibration (module=%d) failed to converge.\n", module);
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+error:
+ tmp_status = dc_cal_module_deinit(dev, module, &state);
+ status = (status != 0) ? status : tmp_status;
+
+ tmp_status = dc_cal_restore(dev, module, &state);
+ status = (status != 0) ? status : tmp_status;
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static inline int enable_lpf_cal_clock(struct bladerf *dev, bool enable)
+{
+ const uint8_t mask = (1 << 5);
+
+ if (enable) {
+ return lms_set(dev, 0x09, mask);
+ } else {
+ return lms_clear(dev, 0x09, mask);
+ }
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static inline int enable_rxvga2_dccal_clock(struct bladerf *dev, bool enable)
+{
+ const uint8_t mask = (1 << 4);
+
+ if (enable) {
+ return lms_set(dev, 0x09, mask);
+ } else {
+ return lms_clear(dev, 0x09, mask);
+ }
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static inline int enable_rxlpf_dccal_clock(struct bladerf *dev, bool enable)
+{
+ const uint8_t mask = (1 << 3);
+
+ if (enable) {
+ return lms_set(dev, 0x09, mask);
+ } else {
+ return lms_clear(dev, 0x09, mask);
+ }
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static inline int enable_txlpf_dccal_clock(struct bladerf *dev, bool enable)
+{
+ const uint8_t mask = (1 << 1);
+
+ if (enable) {
+ return lms_set(dev, 0x09, mask);
+ } else {
+ return lms_clear(dev, 0x09, mask);
+ }
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static int set_dc_cal_value(struct bladerf *dev, uint8_t base,
+ uint8_t dc_addr, int16_t value)
+{
+ int status;
+ const uint8_t new_value = (uint8_t)value;
+ uint8_t regval = (0x08 | dc_addr);
+
+ /* Keep reset inactive, cal disable, load addr */
+ status = LMS_WRITE(dev, base + 3, regval);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Update DC_CNTVAL */
+ status = LMS_WRITE(dev, base + 2, new_value);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Strobe DC_LOAD */
+ regval |= (1 << 4);
+ status = LMS_WRITE(dev, base + 3, regval);
+ if (status != 0) {
+ return status;
+ }
+
+ regval &= ~(1 << 4);
+ status = LMS_WRITE(dev, base + 3, regval);
+ if (status != 0) {
+ return status;
+ }
+
+ status = LMS_READ(dev, base, &regval);
+ if (status != 0) {
+ return status;
+ }
+
+ return 0;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static int get_dc_cal_value(struct bladerf *dev, uint8_t base,
+ uint8_t dc_addr, int16_t *value)
+{
+ int status;
+ uint8_t regval;
+
+ /* Keep reset inactive, cal disable, load addr */
+ status = LMS_WRITE(dev, base + 3, (0x08 | dc_addr));
+ if (status != 0) {
+ return status;
+ }
+
+ /* Fetch value from DC_REGVAL */
+ status = LMS_READ(dev, base, &regval);
+ if (status != 0) {
+ *value = -1;
+ return status;
+ }
+
+ *value = regval;
+ return 0;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_set_dc_cals(struct bladerf *dev,
+ const struct bladerf_lms_dc_cals *dc_cals)
+{
+ int status;
+ const bool cal_tx_lpf =
+ (dc_cals->tx_lpf_i >= 0) || (dc_cals->tx_lpf_q >= 0);
+
+ const bool cal_rx_lpf =
+ (dc_cals->rx_lpf_i >= 0) || (dc_cals->rx_lpf_q >= 0);
+
+ const bool cal_rxvga2 =
+ (dc_cals->dc_ref >= 0) ||
+ (dc_cals->rxvga2a_i >= 0) || (dc_cals->rxvga2a_q >= 0) ||
+ (dc_cals->rxvga2b_i >= 0) || (dc_cals->rxvga2b_q >= 0);
+
+ if (dc_cals->lpf_tuning >= 0) {
+ status = enable_lpf_cal_clock(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ status = set_dc_cal_value(dev, 0x00, 0, dc_cals->lpf_tuning);
+ if (status != 0) {
+ return status;
+ }
+
+ status = enable_lpf_cal_clock(dev, false);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ if (cal_tx_lpf) {
+ status = enable_txlpf_dccal_clock(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ if (dc_cals->tx_lpf_i >= 0) {
+ status = set_dc_cal_value(dev, 0x30, 0, dc_cals->tx_lpf_i);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ if (dc_cals->tx_lpf_q >= 0) {
+ status = set_dc_cal_value(dev, 0x30, 1, dc_cals->tx_lpf_q);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ status = enable_txlpf_dccal_clock(dev, false);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ if (cal_rx_lpf) {
+ status = enable_rxlpf_dccal_clock(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ if (dc_cals->rx_lpf_i >= 0) {
+ status = set_dc_cal_value(dev, 0x50, 0, dc_cals->rx_lpf_i);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ if (dc_cals->rx_lpf_q >= 0) {
+ status = set_dc_cal_value(dev, 0x50, 1, dc_cals->rx_lpf_q);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ status = enable_rxlpf_dccal_clock(dev, false);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ if (cal_rxvga2) {
+ status = enable_rxvga2_dccal_clock(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ if (dc_cals->dc_ref >= 0) {
+ status = set_dc_cal_value(dev, 0x60, 0, dc_cals->dc_ref);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ if (dc_cals->rxvga2a_i >= 0) {
+ status = set_dc_cal_value(dev, 0x60, 1, dc_cals->rxvga2a_i);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ if (dc_cals->rxvga2a_q >= 0) {
+ status = set_dc_cal_value(dev, 0x60, 2, dc_cals->rxvga2a_q);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ if (dc_cals->rxvga2b_i >= 0) {
+ status = set_dc_cal_value(dev, 0x60, 3, dc_cals->rxvga2b_i);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ if (dc_cals->rxvga2b_q >= 0) {
+ status = set_dc_cal_value(dev, 0x60, 4, dc_cals->rxvga2b_q);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ status = enable_rxvga2_dccal_clock(dev, false);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ return 0;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_get_dc_cals(struct bladerf *dev, struct bladerf_lms_dc_cals *dc_cals)
+{
+ int status;
+
+ status = get_dc_cal_value(dev, 0x00, 0, &dc_cals->lpf_tuning);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_dc_cal_value(dev, 0x30, 0, &dc_cals->tx_lpf_i);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_dc_cal_value(dev, 0x30, 1, &dc_cals->tx_lpf_q);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_dc_cal_value(dev, 0x50, 0, &dc_cals->rx_lpf_i);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_dc_cal_value(dev, 0x50, 1, &dc_cals->rx_lpf_q);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_dc_cal_value(dev, 0x60, 0, &dc_cals->dc_ref);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_dc_cal_value(dev, 0x60, 1, &dc_cals->rxvga2a_i);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_dc_cal_value(dev, 0x60, 2, &dc_cals->rxvga2a_q);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_dc_cal_value(dev, 0x60, 3, &dc_cals->rxvga2b_i);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_dc_cal_value(dev, 0x60, 4, &dc_cals->rxvga2b_q);
+ if (status != 0) {
+ return status;
+ }
+
+ return 0;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_select_sampling(struct bladerf *dev, bladerf_sampling sampling)
+{
+ uint8_t val;
+ int status = 0;
+
+ if (sampling == BLADERF_SAMPLING_INTERNAL) {
+ /* Disconnect the ADC input from the outside world */
+ status = LMS_READ( dev, 0x09, &val );
+ if (status) {
+ log_warning( "Could not read LMS to connect ADC to external pins\n" );
+ goto out;
+ }
+
+ val &= ~(1<<7);
+ status = LMS_WRITE( dev, 0x09, val );
+ if (status) {
+ log_warning( "Could not write LMS to connect ADC to external pins\n" );
+ goto out;
+ }
+
+ /* Turn on RXVGA2 */
+ status = LMS_READ( dev, 0x64, &val );
+ if (status) {
+ log_warning( "Could not read LMS to enable RXVGA2\n" );
+ goto out;
+ }
+
+ val |= (1<<1);
+ status = LMS_WRITE( dev, 0x64, val );
+ if (status) {
+ log_warning( "Could not write LMS to enable RXVGA2\n" );
+ goto out;
+ }
+ } else if (sampling == BLADERF_SAMPLING_EXTERNAL) {
+ /* Turn off RXVGA2 */
+ status = LMS_READ( dev, 0x64, &val );
+ if (status) {
+ log_warning( "Could not read the LMS to disable RXVGA2\n" );
+ goto out;
+ }
+
+ val &= ~(1<<1);
+ status = LMS_WRITE( dev, 0x64, val );
+ if (status) {
+ log_warning( "Could not write the LMS to disable RXVGA2\n" );
+ goto out;
+ }
+
+ /* Connect the external ADC pins to the internal ADC input */
+ status = LMS_READ( dev, 0x09, &val );
+ if (status) {
+ log_warning( "Could not read the LMS to connect ADC to internal pins\n" );
+ goto out;
+ }
+
+ val |= (1<<7);
+ status = LMS_WRITE( dev, 0x09, val );
+ if (status) {
+ log_warning( "Could not write the LMS to connect ADC to internal pins\n" );
+ }
+ } else {
+ status = BLADERF_ERR_INVAL;
+ }
+
+out:
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_get_sampling(struct bladerf *dev, bladerf_sampling *sampling)
+{
+ int status = 0, external = 0;
+ uint8_t val = 0;
+
+ status = LMS_READ(dev, 0x09, &val);
+ if (status != 0) {
+ log_warning("Could not read state of ADC pin connectivity\n");
+ goto out;
+ }
+ external = (val & (1 << 7)) ? 1 : 0;
+
+ status = LMS_READ(dev, 0x64, &val);
+ if (status != 0) {
+ log_warning( "Could not read RXVGA2 state\n" );
+ goto out;
+ }
+ external |= (val & (1 << 1)) ? 0 : 2;
+
+ switch (external) {
+ case 0:
+ *sampling = BLADERF_SAMPLING_INTERNAL;
+ break;
+
+ case 3:
+ *sampling = BLADERF_SAMPLING_EXTERNAL;
+ break;
+
+ default:
+ *sampling = BLADERF_SAMPLING_UNKNOWN;
+ break;
+ }
+
+out:
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static inline uint8_t scale_dc_offset(bladerf_module module, int16_t value)
+{
+ uint8_t ret;
+
+ switch (module) {
+ case BLADERF_MODULE_RX:
+ /* RX only has 6 bits of scale to work with, remove normalization */
+ value >>= 5;
+
+ if (value < 0) {
+ if (value <= -64) {
+ /* Clamp */
+ value = 0x3f;
+ } else {
+ value = (-value) & 0x3f;
+ }
+
+ /* This register uses bit 6 to denote a negative value */
+ value |= (1 << 6);
+ } else {
+ if (value >= 64) {
+ /* Clamp */
+ value = 0x3f;
+ } else {
+ value = value & 0x3f;
+ }
+ }
+
+ ret = (uint8_t) value;
+ break;
+
+ case BLADERF_MODULE_TX:
+ /* TX only has 7 bits of scale to work with, remove normalization */
+ value >>= 4;
+
+ /* LMS6002D 0x00 = -16, 0x80 = 0, 0xff = 15.9375 */
+ if (value >= 0) {
+ ret = (uint8_t) (value >= 128) ? 0x7f : (value & 0x7f);
+
+ /* Assert bit 7 for positive numbers */
+ ret = (1 << 7) | ret;
+ } else {
+ ret = (uint8_t) (value <= -128) ? 0x00 : (value & 0x7f);
+ }
+ break;
+
+ default:
+ assert(!"Invalid module provided");
+ ret = 0x00;
+ }
+
+ return ret;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static int set_dc_offset_reg(struct bladerf *dev, bladerf_module module,
+ uint8_t addr, int16_t value)
+{
+ int status;
+ uint8_t regval, tmp;
+
+ switch (module) {
+ case BLADERF_MODULE_RX:
+ status = LMS_READ(dev, addr, &tmp);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Bit 7 is unrelated to lms dc correction, save its state */
+ tmp = tmp & (1 << 7);
+ regval = scale_dc_offset(module, value) | tmp;
+ break;
+
+ case BLADERF_MODULE_TX:
+ regval = scale_dc_offset(module, value);
+ break;
+
+ default:
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = LMS_WRITE(dev, addr, regval);
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_set_dc_offset_i(struct bladerf *dev,
+ bladerf_module module, uint16_t value)
+{
+ const uint8_t addr = (module == BLADERF_MODULE_TX) ? 0x42 : 0x71;
+ return set_dc_offset_reg(dev, module, addr, value);
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_set_dc_offset_q(struct bladerf *dev,
+ bladerf_module module, int16_t value)
+{
+ const uint8_t addr = (module == BLADERF_MODULE_TX) ? 0x43 : 0x72;
+ return set_dc_offset_reg(dev, module, addr, value);
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int get_dc_offset(struct bladerf *dev, bladerf_module module,
+ uint8_t addr, int16_t *value)
+{
+ int status;
+ uint8_t tmp;
+
+ status = LMS_READ(dev, addr, &tmp);
+ if (status != 0) {
+ return status;
+ }
+
+ switch (module) {
+ case BLADERF_MODULE_RX:
+
+ /* Mask out an unrelated control bit */
+ tmp = tmp & 0x7f;
+
+ /* Determine sign */
+ if (tmp & (1 << 6)) {
+ *value = -(int16_t)(tmp & 0x3f);
+ } else {
+ *value = (int16_t)(tmp & 0x3f);
+ }
+
+ /* Renormalize to 2048 */
+ *value <<= 5;
+ break;
+
+ case BLADERF_MODULE_TX:
+ *value = (int16_t) tmp;
+
+ /* Renormalize to 2048 */
+ *value <<= 4;
+ break;
+
+ default:
+ return BLADERF_ERR_INVAL;
+ }
+
+ return 0;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_get_dc_offset_i(struct bladerf *dev,
+ bladerf_module module, int16_t *value)
+{
+ const uint8_t addr = (module == BLADERF_MODULE_TX) ? 0x42 : 0x71;
+ return get_dc_offset(dev, module, addr, value);
+}
+#endif
+
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_get_dc_offset_q(struct bladerf *dev,
+ bladerf_module module, int16_t *value)
+{
+ const uint8_t addr = (module == BLADERF_MODULE_TX) ? 0x43 : 0x72;
+ return get_dc_offset(dev, module, addr, value);
+}
+#endif
diff --git a/Radio/HW/BladeRF/include/CMakeLists.txt b/Radio/HW/BladeRF/include/CMakeLists.txt
new file mode 100644
index 0000000..2c5db08
--- /dev/null
+++ b/Radio/HW/BladeRF/include/CMakeLists.txt
@@ -0,0 +1,12 @@
+cmake_minimum_required(VERSION 3.5)
+
+################################################################################
+# Install libbladeRF header files
+################################################################################
+install(FILES
+ libbladeRF.h
+ bladeRF1.h
+ bladeRF2.h
+ DESTINATION include
+ )
+
diff --git a/Radio/HW/BladeRF/include/bladeRF1.h b/Radio/HW/BladeRF/include/bladeRF1.h
new file mode 100644
index 0000000..5c09f65
--- /dev/null
+++ b/Radio/HW/BladeRF/include/bladeRF1.h
@@ -0,0 +1,1569 @@
+/**
+ * @file bladeRF1.h
+ *
+ * @brief bladeRF1-specific API
+ *
+ * Copyright (C) 2013-2017 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef BLADERF1_H_
+#define BLADERF1_H_
+
+/**
+ * @defgroup BLADERF1 bladeRF1-specific API
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * @defgroup BLADERF1_CONSTANTS Constants (deprecated)
+ *
+ * \deprecated These constants are deprecated, and only apply to the original
+ * bladeRF (x40/x115). They are here for compatibility with legacy
+ * applications. Alternatives are noted below.
+ *
+ * @{
+ */
+
+/** Minimum sample rate, in Hz.
+ *
+ * \deprecated Use bladerf_get_sample_rate_range()
+ */
+#define BLADERF_SAMPLERATE_MIN 80000u
+
+/**
+ * Maximum recommended sample rate, in Hz.
+ *
+ * \deprecated Use bladerf_get_sample_rate_range()
+ */
+#define BLADERF_SAMPLERATE_REC_MAX 40000000u
+
+/** Minimum bandwidth, in Hz
+ *
+ * \deprecated Use bladerf_get_bandwidth_range()
+ */
+#define BLADERF_BANDWIDTH_MIN 1500000u
+
+/** Maximum bandwidth, in Hz
+ *
+ * \deprecated Use bladerf_get_bandwidth_range()
+ */
+#define BLADERF_BANDWIDTH_MAX 28000000u
+
+/**
+ * Minimum tunable frequency (with an XB-200 attached), in Hz.
+ *
+ * While this value is the lowest permitted, note that the components on the
+ * XB-200 are only rated down to 50 MHz. Be aware that performance will likely
+ * degrade as you tune to lower frequencies.
+ *
+ * \deprecated Call bladerf_expansion_attach(), then use
+ * bladerf_get_frequency_range() to get the frequency range.
+ */
+#define BLADERF_FREQUENCY_MIN_XB200 0u
+
+/** Minimum tunable frequency (without an XB-200 attached), in Hz
+ *
+ * \deprecated Use bladerf_get_frequency_range()
+ */
+#define BLADERF_FREQUENCY_MIN 237500000u
+
+/** Maximum tunable frequency, in Hz
+ *
+ * \deprecated Use bladerf_get_frequency_range()
+ */
+#define BLADERF_FREQUENCY_MAX 3800000000u
+
+/** @} (End of BLADERF1_CONSTANTS) */
+
+/**
+ * @ingroup FN_IMAGE
+ * @defgroup BLADERF_FLASH_CONSTANTS Flash image format constants
+ *
+ * \note These apply to both the bladeRF1 and bladeRF2, but they are still in
+ * bladeRF1.h for the time being.
+ *
+ * @{
+ */
+
+/** Byte address of FX3 firmware */
+#define BLADERF_FLASH_ADDR_FIRMWARE 0x00000000
+
+/** Length of firmware region of flash, in bytes */
+#define BLADERF_FLASH_BYTE_LEN_FIRMWARE 0x00030000
+
+/** Byte address of calibration data region */
+#define BLADERF_FLASH_ADDR_CAL 0x00030000
+
+/** Length of calibration data, in bytes */
+#define BLADERF_FLASH_BYTE_LEN_CAL 0x100
+
+/**
+ * Byte address of of the autoloaded FPGA and associated metadata.
+ *
+ * The first page is allocated for metadata, and the FPGA bitstream resides
+ * in the following pages.
+ */
+#define BLADERF_FLASH_ADDR_FPGA 0x00040000
+
+/** @} (End of BLADERF_FLASH_CONSTANTS) */
+
+/**
+ * @defgroup FN_BLADERF1_GAIN Gain stages (deprecated)
+ *
+ * These functions provide control over the device's RX and TX gain stages.
+ *
+ * \deprecated Use bladerf_get_gain_range(), bladerf_set_gain(), and
+ * bladerf_get_gain() to control total system gain. For direct
+ * control of individual gain stages, use bladerf_get_gain_stages(),
+ * bladerf_get_gain_stage_range(), bladerf_set_gain_stage(), and
+ * bladerf_get_gain_stage().
+ *
+ * @{
+ */
+
+/**
+ * In general, the gains should be incremented in the following order (and
+ * decremented in the reverse order).
+ *
+ * <b>TX:</b> `TXVGA1`, `TXVGA2`
+ *
+ * <b>RX:</b> `LNA`, `RXVGA`, `RXVGA2`
+ *
+ */
+
+/** Minimum RXVGA1 gain, in dB
+ *
+ * \deprecated Use bladerf_get_gain_stage_range()
+ */
+#define BLADERF_RXVGA1_GAIN_MIN 5
+
+/** Maximum RXVGA1 gain, in dB
+ *
+ * \deprecated Use bladerf_get_gain_stage_range()
+ */
+#define BLADERF_RXVGA1_GAIN_MAX 30
+
+/** Minimum RXVGA2 gain, in dB
+ *
+ * \deprecated Use bladerf_get_gain_stage_range()
+ */
+#define BLADERF_RXVGA2_GAIN_MIN 0
+
+/** Maximum RXVGA2 gain, in dB
+ *
+ * \deprecated Use bladerf_get_gain_stage_range()
+ */
+#define BLADERF_RXVGA2_GAIN_MAX 30
+
+/** Minimum TXVGA1 gain, in dB
+ *
+ * \deprecated Use bladerf_get_gain_stage_range()
+ */
+#define BLADERF_TXVGA1_GAIN_MIN (-35)
+
+/** Maximum TXVGA1 gain, in dB
+ *
+ * \deprecated Use bladerf_get_gain_stage_range()
+ */
+#define BLADERF_TXVGA1_GAIN_MAX (-4)
+
+/** Minimum TXVGA2 gain, in dB
+ *
+ * \deprecated Use bladerf_get_gain_stage_range()
+ */
+#define BLADERF_TXVGA2_GAIN_MIN 0
+
+/** Maximum TXVGA2 gain, in dB
+ *
+ * \deprecated Use bladerf_get_gain_stage_range()
+ */
+#define BLADERF_TXVGA2_GAIN_MAX 25
+
+/**
+ * LNA gain options
+ *
+ * \deprecated Use bladerf_get_gain_stage_range()
+ */
+typedef enum {
+ BLADERF_LNA_GAIN_UNKNOWN, /**< Invalid LNA gain */
+ BLADERF_LNA_GAIN_BYPASS, /**< LNA bypassed - 0dB gain */
+ BLADERF_LNA_GAIN_MID, /**< LNA Mid Gain (MAX-6dB) */
+ BLADERF_LNA_GAIN_MAX /**< LNA Max Gain */
+} bladerf_lna_gain;
+
+/**
+ * Gain in dB of the LNA at mid setting
+ *
+ * \deprecated Use bladerf_get_gain_stage_range()
+ */
+#define BLADERF_LNA_GAIN_MID_DB 3
+
+/**
+ * Gain in db of the LNA at max setting
+ *
+ * \deprecated Use bladerf_get_gain_stage_range()
+ */
+#define BLADERF_LNA_GAIN_MAX_DB 6
+
+/**
+ * Set the PA gain in dB
+ *
+ * \deprecated Use either bladerf_set_gain() or bladerf_set_gain_stage().
+ *
+ * Values outside the range of
+ * [ \ref BLADERF_TXVGA2_GAIN_MIN, \ref BLADERF_TXVGA2_GAIN_MAX ]
+ * will be clamped.
+ *
+ * @param dev Device handle
+ * @param[in] gain Desired gain
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_txvga2(struct bladerf *dev, int gain);
+
+/**
+ * Get the PA gain in dB
+ *
+ * \deprecated Use either bladerf_get_gain() or bladerf_get_gain_stage().
+ *
+ * @param dev Device handle
+ * @param[out] gain Pointer to returned gain
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT int CALL_CONV bladerf_get_txvga2(struct bladerf *dev, int *gain);
+
+/**
+ * Set the post-LPF gain in dB
+ *
+ * \deprecated Use either bladerf_set_gain() or bladerf_set_gain_stage().
+ *
+ * Values outside the range of
+ * [ \ref BLADERF_TXVGA1_GAIN_MIN, \ref BLADERF_TXVGA1_GAIN_MAX ]
+ * will be clamped.
+ *
+ * @param dev Device handle
+ * @param[in] gain Desired gain
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_txvga1(struct bladerf *dev, int gain);
+
+/**
+ * Get the post-LPF gain in dB
+ *
+ * \deprecated Use either bladerf_get_gain() or bladerf_get_gain_stage().
+ *
+ * @param dev Device handle
+ * @param[out] gain Pointer to returned gain
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_txvga1(struct bladerf *dev, int *gain);
+
+/**
+ * Set the LNA gain
+ *
+ * \deprecated Use either bladerf_set_gain() or bladerf_set_gain_stage().
+ *
+ * @param dev Device handle
+ * @param[in] gain Desired gain level
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_lna_gain(struct bladerf *dev, bladerf_lna_gain gain);
+
+/**
+ * Get the LNA gain
+ *
+ * \deprecated Use either bladerf_get_gain() or bladerf_get_gain_stage().
+ *
+ * @param dev Device handle
+ * @param[out] gain Pointer to the set gain level
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_lna_gain(struct bladerf *dev, bladerf_lna_gain *gain);
+
+/**
+ * Set the pre-LPF VGA gain
+ *
+ * \deprecated Use either bladerf_set_gain() or bladerf_set_gain_stage().
+ *
+ * Values outside the range of
+ * [ \ref BLADERF_RXVGA1_GAIN_MIN, \ref BLADERF_RXVGA1_GAIN_MAX ]
+ * will be clamped.
+ *
+ * @param dev Device handle
+ * @param[in] gain Desired gain
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_rxvga1(struct bladerf *dev, int gain);
+
+/**
+ * Get the pre-LPF VGA gain
+ *
+ * \deprecated Use either bladerf_get_gain() or bladerf_get_gain_stage().
+ *
+ * @param dev Device handle
+ * @param[out] gain Pointer to the set gain level
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_rxvga1(struct bladerf *dev, int *gain);
+
+/**
+ * Set the post-LPF VGA gain
+ *
+ * \deprecated Use either bladerf_set_gain() or bladerf_set_gain_stage().
+ *
+ * Values outside the range of
+ * [ \ref BLADERF_RXVGA2_GAIN_MIN, \ref BLADERF_RXVGA2_GAIN_MAX ]
+ * will be clamped.
+ *
+ * @param dev Device handle
+ * @param[in] gain Desired gain
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_rxvga2(struct bladerf *dev, int gain);
+
+/**
+ * Get the post-LPF VGA gain
+ *
+ * \deprecated Use either bladerf_get_gain() or bladerf_get_gain_stage().
+ *
+ * @param dev Device handle
+ * @param[out] gain Pointer to the set gain level
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_rxvga2(struct bladerf *dev, int *gain);
+
+/** @} (End of FN_BLADERF1_GAIN) */
+
+/**
+ * @defgroup FN_BLADERF1_SAMPLING_MUX Sampling Mux
+ *
+ * These functions provide control over internal and direct sampling modes of
+ * the LMS6002D.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * Sampling connection
+ */
+typedef enum {
+ BLADERF_SAMPLING_UNKNOWN, /**< Unable to determine connection type */
+ BLADERF_SAMPLING_INTERNAL, /**< Sample from RX/TX connector */
+ BLADERF_SAMPLING_EXTERNAL /**< Sample from J60 or J61 */
+} bladerf_sampling;
+
+/**
+ * Configure the sampling of the LMS6002D to be either internal or external.
+ *
+ * Internal sampling will read from the RXVGA2 driver internal to the chip.
+ * External sampling will connect the ADC inputs to the external inputs for
+ * direct sampling.
+ *
+ * @param dev Device handle
+ * @param[in] sampling Sampling connection
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_sampling(struct bladerf *dev,
+ bladerf_sampling sampling);
+
+/**
+ * Read the device's current state of RXVGA2 and ADC pin connection
+ * to figure out which sampling mode it is currently configured in.
+ *
+ * @param dev Device handle
+ * @param[out] sampling Sampling connection
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_sampling(struct bladerf *dev,
+ bladerf_sampling *sampling);
+
+
+/** @} (End of FN_BLADERF1_SAMPLING_MUX) */
+
+/**
+ * @defgroup FN_BLADERF1_LPF_BYPASS LPF Bypass
+ *
+ * These functions provide control over the LPF bypass mode of the LMS6002D.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * Low-Pass Filter (LPF) mode
+ */
+typedef enum {
+ BLADERF_LPF_NORMAL, /**< LPF connected and enabled */
+ BLADERF_LPF_BYPASSED, /**< LPF bypassed */
+ BLADERF_LPF_DISABLED /**< LPF disabled */
+} bladerf_lpf_mode;
+
+/**
+ * Set the LMS LPF mode to bypass or disable it
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] mode Mode to be set
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_lpf_mode(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_lpf_mode mode);
+
+/**
+ * Get the current mode of the LMS LPF
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] mode Current mode of the LPF
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_lpf_mode(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_lpf_mode *mode);
+
+/** @} (End of FN_BLADERF1_LPF_BYPASS) */
+
+/**
+ * @defgroup FN_SMB_CLOCK SMB clock port control
+ *
+ * The SMB clock port (J62) may be used to synchronize sampling on multiple
+ * devices, or to generate an arbitrary clock output for a different device.
+ *
+ * For MIMO configurations, one device is the clock "master" and outputs its
+ * 38.4 MHz reference on this port. The clock "slave" devices configure the SMB
+ * port as an input and expect to see this 38.4 MHz reference on this port. This
+ * implies that the "master" must be configured first.
+ *
+ * Alternatively, this port may be used to generate an arbitrary clock signal
+ * for use with other devices via the bladerf_set_smb_frequency() and
+ * bladerf_set_rational_smb_frequency() functions.
+ *
+ * @warning <b>Do not</b> use these functions when operating an expansion board.
+ * A different clock configuration is required for the XB devices which cannot
+ * be used simultaneously with the SMB clock port.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * Maximum output frequency on SMB connector, if no expansion board attached.
+ */
+#define BLADERF_SMB_FREQUENCY_MAX 200000000u
+
+/**
+ * Minimum output frequency on SMB connector, if no expansion board attached.
+ */
+#define BLADERF_SMB_FREQUENCY_MIN ((38400000u * 66u) / (32 * 567))
+
+
+/**
+ * SMB clock port mode of operation
+ */
+typedef enum {
+ BLADERF_SMB_MODE_INVALID = -1, /**< Invalid selection */
+
+ BLADERF_SMB_MODE_DISABLED, /**< Not in use. Device operates from its onboard
+ * clock and does not use J62.
+ */
+
+ BLADERF_SMB_MODE_OUTPUT, /**< Device outputs a 38.4 MHz reference clock on
+ * J62. This may be used to drive another device
+ * that is configured with
+ * ::BLADERF_SMB_MODE_INPUT.
+ */
+
+ BLADERF_SMB_MODE_INPUT, /**< Device configures J62 as an input and expects a
+ * 38.4 MHz reference to be available when this
+ * setting is applied.
+ */
+
+ BLADERF_SMB_MODE_UNAVAILBLE, /**< SMB port is unavailable for use due to the
+ * underlying clock being used elsewhere (e.g.,
+ * for an expansion board).
+ */
+
+} bladerf_smb_mode;
+
+/**
+ * Set the current mode of operation of the SMB clock port
+ *
+ * In a MIMO configuration, one "master" device should first be configured to
+ * output its reference clock to the slave devices via
+ * `bladerf_set_smb_mode(dev, BLADERF_SMB_MODE_OUTPUT)`.
+ *
+ * Next, all "slave" devices should be configured to use the reference clock
+ * provided on the SMB clock port (instead of using their on-board reference)
+ * via `bladerf_set_smb_mode(dev, BLADERF_SMB_MODE_INPUT)`.
+ *
+ * @param dev Device handle
+ * @param[in] mode Desired mode
+ *
+ * @return 0 on success, or a value from \ref RETCODES list on failure.
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_smb_mode(struct bladerf *dev, bladerf_smb_mode mode);
+
+/**
+ * Get the current mode of operation of the SMB clock port
+ *
+ * @param dev Device handle
+ * @param[out] mode Desired mode
+ *
+ * @return 0 on success, or a value from \ref RETCODES list on failure.
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_smb_mode(struct bladerf *dev, bladerf_smb_mode *mode);
+
+/**
+ * Set the SMB clock port frequency in rational Hz
+ *
+ * @param dev Device handle
+ * @param[in] rate Rational frequency
+ * @param[out] actual If non-NULL, this is written with the actual
+ *
+ * The frequency must be between \ref BLADERF_SMB_FREQUENCY_MIN and
+ * \ref BLADERF_SMB_FREQUENCY_MAX.
+ *
+ * This function inherently configures the SMB clock port as an output. Do not
+ * call bladerf_set_smb_mode() with ::BLADERF_SMB_MODE_OUTPUT, as this will
+ * reset the output frequency to the 38.4 MHz reference.
+ *
+ * @warning This clock should not be set if an expansion board is connected.
+ *
+ * @return 0 on success,
+ * BLADERF_ERR_INVAL for an invalid frequency,
+ * or a value from \ref RETCODES list on failure.
+ */
+API_EXPORT
+int CALL_CONV
+ bladerf_set_rational_smb_frequency(struct bladerf *dev,
+ struct bladerf_rational_rate *rate,
+ struct bladerf_rational_rate *actual);
+
+/**
+ * Set the SMB connector output frequency in Hz.
+ * Use bladerf_set_rational_smb_frequency() for more arbitrary values.
+ *
+ * @param dev Device handle
+ * @param[in] rate Frequency
+ * @param[out] actual If non-NULL. this is written with the actual
+ * frequency achieved.
+ *
+ * This function inherently configures the SMB clock port as an output. Do not
+ * call bladerf_set_smb_mode() with ::BLADERF_SMB_MODE_OUTPUT, as this will
+ * reset the output frequency to the 38.4 MHz reference.
+ *
+ * The frequency must be between \ref BLADERF_SMB_FREQUENCY_MIN and
+ * \ref BLADERF_SMB_FREQUENCY_MAX.
+ *
+ * @warning This clock should not be set if an expansion board is connected.
+ *
+ * @return 0 on success,
+ * BLADERF_ERR_INVAL for an invalid frequency,
+ * or a value from \ref RETCODES list on other failures
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_smb_frequency(struct bladerf *dev,
+ uint32_t rate,
+ uint32_t *actual);
+
+/**
+ * Read the SMB connector output frequency in rational Hz
+ *
+ * @param dev Device handle
+ * @param[out] rate Pointer to returned rational frequency
+ *
+ * @return 0 on success, value from \ref RETCODES list upon failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_rational_smb_frequency(
+ struct bladerf *dev, struct bladerf_rational_rate *rate);
+
+/**
+ * Read the SMB connector output frequency in Hz
+ *
+ * @param dev Device handle
+ * @param[out] rate Pointer to returned frequency
+ *
+ * @return 0 on success, value from \ref RETCODES list upon failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_smb_frequency(struct bladerf *dev,
+ unsigned int *rate);
+
+/** @} (End of FN_SMB_CLOCK) */
+
+/**
+ * @defgroup FN_EXP_IO Expansion I/O
+ *
+ * These definitions and functions provide high-level functionality for
+ * manipulating pins on the bladeRF1 U74 Expansion Header, and the associated
+ * mappings on expansion boards.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/** Expansion pin GPIO number to bitmask */
+#define BLADERF_XB_GPIO(n) (1 << (n - 1))
+
+/** Specifies a pin to be an output */
+#define BLADERF_XB_DIR_OUTPUT(pin) (pin)
+
+/** Specifies a pin to be an input */
+#define BLADERF_XB_DIR_INPUT(pin) (0)
+
+/** Pin bitmask for Expansion GPIO 1 (U74 pin 11) */
+#define BLADERF_XB_GPIO_01 BLADERF_XB_GPIO(1)
+
+/** Pin bitmask for Expansion GPIO 2 (U74 pin 13) */
+#define BLADERF_XB_GPIO_02 BLADERF_XB_GPIO(2)
+
+/** Pin bitmask for Expansion GPIO 3 (U74 pin 17) */
+#define BLADERF_XB_GPIO_03 BLADERF_XB_GPIO(3)
+
+/** Pin bitmask for Expansion GPIO 4 (U74 pin 19) */
+#define BLADERF_XB_GPIO_04 BLADERF_XB_GPIO(4)
+
+/** Pin bitmask for Expansion GPIO 5 (U74 pin 23) */
+#define BLADERF_XB_GPIO_05 BLADERF_XB_GPIO(5)
+
+/** Pin bitmask for Expansion GPIO 6 (U74 pin 25) */
+#define BLADERF_XB_GPIO_06 BLADERF_XB_GPIO(6)
+
+/** Pin bitmask for Expansion GPIO 7 (U74 pin 29) */
+#define BLADERF_XB_GPIO_07 BLADERF_XB_GPIO(7)
+
+/** Pin bitmask for Expansion GPIO 8 (U74 pin 31) */
+#define BLADERF_XB_GPIO_08 BLADERF_XB_GPIO(8)
+
+/** Pin bitmask for Expansion GPIO 9 (U74 pin 35) */
+#define BLADERF_XB_GPIO_09 BLADERF_XB_GPIO(9)
+
+/** Pin bitmask for Expansion GPIO 10 (U74 pin 37) */
+#define BLADERF_XB_GPIO_10 BLADERF_XB_GPIO(10)
+
+/** Pin bitmask for Expansion GPIO 11 (U74 pin 41) */
+#define BLADERF_XB_GPIO_11 BLADERF_XB_GPIO(11)
+
+/** Pin bitmask for Expansion GPIO 12 (U74 pin 43) */
+#define BLADERF_XB_GPIO_12 BLADERF_XB_GPIO(12)
+
+/** Pin bitmask for Expansion GPIO 13 (U74 pin 47) */
+#define BLADERF_XB_GPIO_13 BLADERF_XB_GPIO(13)
+
+/** Pin bitmask for Expansion GPIO 14 (U74 pin 49) */
+#define BLADERF_XB_GPIO_14 BLADERF_XB_GPIO(14)
+
+/** Pin bitmask for Expansion GPIO 15 (U74 pin 53) */
+#define BLADERF_XB_GPIO_15 BLADERF_XB_GPIO(15)
+
+/** Pin bitmask for Expansion GPIO 16 (U74 pin 55) */
+#define BLADERF_XB_GPIO_16 BLADERF_XB_GPIO(16)
+
+/** Pin bitmask for Expansion GPIO 17 (U74 pin 12) */
+#define BLADERF_XB_GPIO_17 BLADERF_XB_GPIO(17)
+
+/** Pin bitmask for Expansion GPIO 18 (U74 pin 14) */
+#define BLADERF_XB_GPIO_18 BLADERF_XB_GPIO(18)
+
+/** Pin bitmask for Expansion GPIO 19 (U74 pin 18) */
+#define BLADERF_XB_GPIO_19 BLADERF_XB_GPIO(19)
+
+/** Pin bitmask for Expansion GPIO 20 (U74 pin 20) */
+#define BLADERF_XB_GPIO_20 BLADERF_XB_GPIO(20)
+
+/** Pin bitmask for Expansion GPIO 21 (U74 pin 24) */
+#define BLADERF_XB_GPIO_21 BLADERF_XB_GPIO(21)
+
+/** Pin bitmask for Expansion GPIO 22 (U74 pin 26) */
+#define BLADERF_XB_GPIO_22 BLADERF_XB_GPIO(22)
+
+/** Pin bitmask for Expansion GPIO 23 (U74 pin 30) */
+#define BLADERF_XB_GPIO_23 BLADERF_XB_GPIO(23)
+
+/** Pin bitmask for Expansion GPIO 24 (U74 pin 32) */
+#define BLADERF_XB_GPIO_24 BLADERF_XB_GPIO(24)
+
+/** Pin bitmask for Expansion GPIO 25 (U74 pin 36) */
+#define BLADERF_XB_GPIO_25 BLADERF_XB_GPIO(25)
+
+/** Pin bitmask for Expansion GPIO 26 (U74 pin 38) */
+#define BLADERF_XB_GPIO_26 BLADERF_XB_GPIO(26)
+
+/** Pin bitmask for Expansion GPIO 27 (U74 pin 42) */
+#define BLADERF_XB_GPIO_27 BLADERF_XB_GPIO(27)
+
+/** Pin bitmask for Expansion GPIO 28 (U74 pin 44) */
+#define BLADERF_XB_GPIO_28 BLADERF_XB_GPIO(28)
+
+/** Pin bitmask for Expansion GPIO 29 (U74 pin 48) */
+#define BLADERF_XB_GPIO_29 BLADERF_XB_GPIO(29)
+
+/** Pin bitmask for Expansion GPIO 30 (U74 pin 50) */
+#define BLADERF_XB_GPIO_30 BLADERF_XB_GPIO(30)
+
+/** Pin bitmask for Expansion GPIO 31 (U74 pin 54) */
+#define BLADERF_XB_GPIO_31 BLADERF_XB_GPIO(31)
+
+/** Pin bitmask for Expansion GPIO 32 (U74 pin 56) */
+#define BLADERF_XB_GPIO_32 BLADERF_XB_GPIO(32)
+
+
+/** Bitmask for XB-200 header J7, pin 1 */
+#define BLADERF_XB200_PIN_J7_1 BLADERF_XB_GPIO_10
+
+/** Bitmask for XB-200 header J7, pin 2 */
+#define BLADERF_XB200_PIN_J7_2 BLADERF_XB_GPIO_11
+
+/** Bitmask for XB-200 header J7, pin 5 */
+#define BLADERF_XB200_PIN_J7_5 BLADERF_XB_GPIO_08
+
+/** Bitmask for XB-200 header J7, pin 6 */
+#define BLADERF_XB200_PIN_J7_6 BLADERF_XB_GPIO_09
+
+/** Bitmask for XB-200 header J13, pin 1 */
+#define BLADERF_XB200_PIN_J13_1 BLADERF_XB_GPIO_17
+
+/** Bitmask for XB-200 header J13, pin 2 */
+#define BLADERF_XB200_PIN_J13_2 BLADERF_XB_GPIO_18
+
+/* XB-200 J13 Pin 6 is actually reserved for SPI */
+
+/** Bitmask for XB-200 header J16, pin 1 */
+#define BLADERF_XB200_PIN_J16_1 BLADERF_XB_GPIO_31
+
+/** Bitmask for XB-200 header J16, pin 2 */
+#define BLADERF_XB200_PIN_J16_2 BLADERF_XB_GPIO_32
+
+/** Bitmask for XB-200 header J16, pin 3 */
+#define BLADERF_XB200_PIN_J16_3 BLADERF_XB_GPIO_19
+
+/** Bitmask for XB-200 header J16, pin 4 */
+#define BLADERF_XB200_PIN_J16_4 BLADERF_XB_GPIO_20
+
+/** Bitmask for XB-200 header J16, pin 5 */
+#define BLADERF_XB200_PIN_J16_5 BLADERF_XB_GPIO_21
+
+/** Bitmask for XB-200 header J16, pin 6 */
+#define BLADERF_XB200_PIN_J16_6 BLADERF_XB_GPIO_24
+
+/** Bitmask for XB-100 header J2, pin 3 */
+#define BLADERF_XB100_PIN_J2_3 BLADERF_XB_GPIO_07
+
+/** Bitmask for XB-100 header J2, pin 4 */
+#define BLADERF_XB100_PIN_J2_4 BLADERF_XB_GPIO_08
+
+/** Bitmask for XB-100 header J3, pin 3 */
+#define BLADERF_XB100_PIN_J3_3 BLADERF_XB_GPIO_09
+
+/** Bitmask for XB-100 header J3, pin 4 */
+#define BLADERF_XB100_PIN_J3_4 BLADERF_XB_GPIO_10
+
+/** Bitmask for XB-100 header J4, pin 3 */
+#define BLADERF_XB100_PIN_J4_3 BLADERF_XB_GPIO_11
+
+/** Bitmask for XB-100 header J4, pin 4 */
+#define BLADERF_XB100_PIN_J4_4 BLADERF_XB_GPIO_12
+
+/** Bitmask for XB-100 header J5, pin 3 */
+#define BLADERF_XB100_PIN_J5_3 BLADERF_XB_GPIO_13
+
+/** Bitmask for XB-100 header J5, pin 4 */
+#define BLADERF_XB100_PIN_J5_4 BLADERF_XB_GPIO_14
+
+/** Bitmask for XB-100 header J11, pin 2 */
+#define BLADERF_XB100_PIN_J11_2 BLADERF_XB_GPIO_05
+
+/** Bitmask for XB-100 header J11, pin 3 */
+#define BLADERF_XB100_PIN_J11_3 BLADERF_XB_GPIO_04
+
+/** Bitmask for XB-100 header J11, pin 4 */
+#define BLADERF_XB100_PIN_J11_4 BLADERF_XB_GPIO_03
+
+/** Bitmask for XB-100 header J11, pin 5 */
+#define BLADERF_XB100_PIN_J11_5 BLADERF_XB_GPIO_06
+
+/** Bitmask for XB-100 header J12, pin 2 */
+#define BLADERF_XB100_PIN_J12_2 BLADERF_XB_GPIO_01
+
+/* XB-100 header J12, pins 3 and 4 are reserved for SPI */
+
+/** Bitmask for XB-100 header J12, pin 5 */
+#define BLADERF_XB100_PIN_J12_5 BLADERF_XB_GPIO_02
+
+/** Bitmask for XB-100 LED_D1 (blue) */
+#define BLADERF_XB100_LED_D1 BLADERF_XB_GPIO_24
+
+/** Bitmask for XB-100 LED_D2 (blue) */
+#define BLADERF_XB100_LED_D2 BLADERF_XB_GPIO_32
+
+/** Bitmask for XB-100 LED_D3 (blue) */
+#define BLADERF_XB100_LED_D3 BLADERF_XB_GPIO_30
+
+/** Bitmask for XB-100 LED_D4 (red) */
+#define BLADERF_XB100_LED_D4 BLADERF_XB_GPIO_28
+
+/** Bitmask for XB-100 LED_D5 (red) */
+#define BLADERF_XB100_LED_D5 BLADERF_XB_GPIO_23
+
+/** Bitmask for XB-100 LED_D6 (red) */
+#define BLADERF_XB100_LED_D6 BLADERF_XB_GPIO_25
+
+/** Bitmask for XB-100 LED_D7 (green) */
+#define BLADERF_XB100_LED_D7 BLADERF_XB_GPIO_31
+
+/** Bitmask for XB-100 LED_D8 (green) */
+#define BLADERF_XB100_LED_D8 BLADERF_XB_GPIO_29
+
+/** Bitmask for XB-100 tricolor LED, red cathode */
+#define BLADERF_XB100_TLED_RED BLADERF_XB_GPIO_22
+
+/** Bitmask for XB-100 tricolor LED, green cathode */
+#define BLADERF_XB100_TLED_GREEN BLADERF_XB_GPIO_21
+
+/** Bitmask for XB-100 tricolor LED, blue cathode */
+#define BLADERF_XB100_TLED_BLUE BLADERF_XB_GPIO_20
+
+/** Bitmask for XB-100 DIP switch 1 */
+#define BLADERF_XB100_DIP_SW1 BLADERF_XB_GPIO_27
+
+/** Bitmask for XB-100 DIP switch 2 */
+#define BLADERF_XB100_DIP_SW2 BLADERF_XB_GPIO_26
+
+/** Bitmask for XB-100 DIP switch 3 */
+#define BLADERF_XB100_DIP_SW3 BLADERF_XB_GPIO_16
+
+/** Bitmask for XB-100 DIP switch 4 */
+#define BLADERF_XB100_DIP_SW4 BLADERF_XB_GPIO_15
+
+/** Bitmask for XB-100 button J6 */
+#define BLADERF_XB100_BTN_J6 BLADERF_XB_GPIO_19
+
+/** Bitmask for XB-100 button J7 */
+#define BLADERF_XB100_BTN_J7 BLADERF_XB_GPIO_18
+
+/** Bitmask for XB-100 button J8 */
+#define BLADERF_XB100_BTN_J8 BLADERF_XB_GPIO_17
+
+/* XB-100 buttons J9 and J10 are not mapped to the GPIO register,
+ * but instead to reserved SPI pins. FPGA modifications are needed to
+ * use these. */
+
+/**
+ * Read the state of expansion GPIO values
+ *
+ * @param dev Device handle
+ * @param[out] val Value of GPIO pins
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_expansion_gpio_read(struct bladerf *dev, uint32_t *val);
+
+/**
+ * Write expansion GPIO pins.
+ *
+ * Callers should be sure to perform a read-modify-write sequence to avoid
+ * accidentally clearing other GPIO bits that may be set by the library
+ * internally.
+ *
+ * Consider using bladerf_expansion_gpio_masked_write() instead.
+ *
+ * @param dev Device handle
+ * @param[in] val Data to write to GPIO pins
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_expansion_gpio_write(struct bladerf *dev, uint32_t val);
+
+/**
+ * Write values to the specified GPIO pins
+ *
+ * This function alleviates the need for the caller to perform a
+ * read-modify-write sequence. The supplied mask is used by the FPGA to perform
+ * the required RMW operation.
+ *
+ * @param dev Device handle
+ * @param[in] mask Mask of pins to write
+ * @param[in] value Value to write.
+ *
+ * For example, to set XB200 pins J16-1 and J16-2, and clear J16-4 and J16-5:
+ *
+ * @code{.c}
+ * const uint32_t pins_to_write =
+ * BLADERF_XB200_PIN_J16_1 |
+ * BLADERF_XB200_PIN_J16_2 |
+ * BLADERF_XB200_PIN_J16_3 |
+ * BLADERF_XB200_PIN_J16_4;
+ *
+ * const uint32_t values_to_write =
+ * BLADERF_XB200_PIN_J16_1 |
+ * BLADERF_XB200_PIN_J16_2;
+ *
+ * int status = bladerf_expansion_gpio_masked_write(dev,
+ * pins_to_write,
+ * values_to_write);
+ * @endcode
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_expansion_gpio_masked_write(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t value);
+
+/**
+ * Read the expansion GPIO direction register
+ *
+ * @param dev Device handle
+ * @param[out] outputs Pins configured as outputs will be set to '1'.
+ * Pins configured as inputs will be set to '0'.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_expansion_gpio_dir_read(struct bladerf *dev,
+ uint32_t *outputs);
+
+/**
+ * Write to the expansion GPIO direction register.
+ *
+ * Callers should be sure to perform a read-modify-write sequence to avoid
+ * accidentally clearing other GPIO bits that may be set by the library
+ * internally.
+ *
+ * Consider using bladerf_expansion_gpio_dir_masked_write() instead.
+ *
+ * @param dev Device handle
+ * @param[in] outputs Pins set to '1' will be configured as outputs.
+ * Pins set to '0' will be configured as inputs.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_expansion_gpio_dir_write(struct bladerf *dev,
+ uint32_t outputs);
+
+/**
+ * Configure the direction of the specified expansion GPIO pins
+ *
+ * This function alleviates the need for the caller to perform a
+ * read-modify-write sequence. The supplied mask is used by the FPGA to perform
+ * the required RMW operation.
+ *
+ * @param dev Device handle
+ * @param[in] mask Bitmask of pins to configure
+ * @param[in] outputs Pins set to '1' will be configured as outputs.
+ * Pins set to '0' will be configured as inputs.
+ *
+ * For example, to configure XB200 pins J16-1 and J16-2 and pins J16-4 and J16-5
+ * as inputs:
+ *
+ * @code{.c}
+ * const uint32_t pins_to_config =
+ * BLADERF_XB200_PIN_J16_1 |
+ * BLADERF_XB200_PIN_J16_2 |
+ * BLADERF_XB200_PIN_J16_3 |
+ * BLADERF_XB200_PIN_J16_4;
+ *
+ * const uint32_t output_pins =
+ * BLADERF_XB200_PIN_J16_1 |
+ * BLADERF_XB200_PIN_J16_2;
+ *
+ * int status = bladerf_expansion_gpio_masked_write(dev,
+ * pins_to_config,
+ * output_pins);
+ * @endcode
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_expansion_gpio_dir_masked_write(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t outputs);
+
+/** @} (End of FN_EXP_IO) */
+
+/**
+ * @defgroup FN_BLADERF1_XB Expansion board support
+ *
+ * This group of functions provides the ability to control and configure
+ * expansion boards such as the XB-100, XB-200, and XB-300.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * XB-200 filter selection options
+ */
+typedef enum {
+ /** 50-54 MHz (6 meter band) filterbank */
+ BLADERF_XB200_50M = 0,
+
+ /** 144-148 MHz (2 meter band) filterbank */
+ BLADERF_XB200_144M,
+
+ /**
+ * 222-225 MHz (1.25 meter band) filterbank.
+ *
+ * Note that this filter option is technically wider, covering 206-235 MHz.
+ */
+ BLADERF_XB200_222M,
+
+ /**
+ * This option enables the RX/TX channel's custom filter bank path across
+ * the associated FILT and FILT-ANT SMA connectors on the XB-200 board.
+ *
+ * For reception, it is often possible to simply connect the RXFILT and
+ * RXFILT-ANT connectors with an SMA cable (effectively, "no filter"). This
+ * allows for reception of signals outside of the frequency range of the
+ * on-board filters, with some potential trade-off in signal quality.
+ *
+ * For transmission, <b>always</b> use an appropriate filter on the custom
+ * filter path to avoid spurious emissions.
+ *
+ */
+ BLADERF_XB200_CUSTOM,
+
+ /**
+ * When this option is selected, the other filter options are automatically
+ * selected depending on the RX or TX channel's current frequency, based
+ * upon the 1dB points of the on-board filters. For frequencies outside
+ * the range of the on-board filters, the custom path is selected.
+ */
+ BLADERF_XB200_AUTO_1DB,
+
+ /**
+ * When this option is selected, the other filter options are automatically
+ * selected depending on the RX or TX channel's current frequency, based
+ * upon the 3dB points of the on-board filters. For frequencies outside the
+ * range of the on-board filters, the custom path is selected.
+ */
+ BLADERF_XB200_AUTO_3DB
+} bladerf_xb200_filter;
+
+/**
+ * XB-200 signal paths
+ */
+typedef enum {
+ BLADERF_XB200_BYPASS = 0, /**< Bypass the XB-200 mixer */
+ BLADERF_XB200_MIX /**< Pass signals through the XB-200 mixer */
+} bladerf_xb200_path;
+
+/**
+ * XB-300 TRX setting
+ */
+typedef enum {
+ BLADERF_XB300_TRX_INVAL = -1, /**< Invalid TRX selection */
+ BLADERF_XB300_TRX_TX = 0, /**< TRX antenna operates as TX */
+ BLADERF_XB300_TRX_RX, /**< TRX antenna operates as RX */
+ BLADERF_XB300_TRX_UNSET /**< TRX antenna unset */
+} bladerf_xb300_trx;
+
+/**
+ * XB-300 Amplifier selection
+ */
+typedef enum {
+ BLADERF_XB300_AMP_INVAL = -1, /**< Invalid amplifier selection */
+ BLADERF_XB300_AMP_PA = 0, /**< TX Power amplifier */
+ BLADERF_XB300_AMP_LNA, /**< RX LNA */
+ BLADERF_XB300_AMP_PA_AUX /**< Auxillary Power amplifier */
+} bladerf_xb300_amplifier;
+
+/**
+ * Set XB-200 filterbank
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] filter XB200 filterbank
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_xb200_set_filterbank(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_filter filter);
+
+/**
+ * Get current XB-200 filterbank
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] filter Pointer to filterbank, only updated if return
+ * value is 0.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_xb200_get_filterbank(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_filter *filter);
+
+/**
+ * Set XB-200 signal path
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] path Desired XB-200 signal path
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_xb200_set_path(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_path path);
+
+/**
+ * Get current XB-200 signal path
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] path Pointer to XB200 signal path
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_xb200_get_path(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_path *path);
+
+/**
+ * Configure the XB-300 TRX path
+ *
+ * @param dev Device handle
+ * @param[in] trx Desired XB-300 TRX setting
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_xb300_set_trx(struct bladerf *dev, bladerf_xb300_trx trx);
+
+/**
+ * Get the current XB-300 signal path
+ *
+ * @param dev Device handle
+ * @param[out] trx XB300 TRX antenna setting
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_xb300_get_trx(struct bladerf *dev,
+ bladerf_xb300_trx *trx);
+
+/**
+ * Enable or disable selected XB-300 amplifier
+ *
+ * @param dev Device handle
+ * @param[in] amp XB-300 amplifier
+ * @param[in] enable Set true to enable or false to disable
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_xb300_set_amplifier_enable(struct bladerf *dev,
+ bladerf_xb300_amplifier amp,
+ bool enable);
+/**
+ * Get state of selected XB-300 amplifier
+ *
+ * @param dev Device handle
+ * @param[in] amp XB-300 amplifier
+ * @param[out] enable Set true to enable or false to disable
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_xb300_get_amplifier_enable(struct bladerf *dev,
+ bladerf_xb300_amplifier amp,
+ bool *enable);
+/**
+ * Get current PA PDET output power in dBm
+ *
+ * @param dev Device handle
+ * @param[out] val Output power in dBm
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_xb300_get_output_power(struct bladerf *dev, float *val);
+
+/** @} (End of FN_BLADERF1_XB) */
+
+/**
+ * @defgroup FN_BLADERF1_DC_CAL DC Calibration
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * DC Calibration Modules
+ */
+typedef enum {
+ BLADERF_DC_CAL_INVALID = -1,
+ BLADERF_DC_CAL_LPF_TUNING,
+ BLADERF_DC_CAL_TX_LPF,
+ BLADERF_DC_CAL_RX_LPF,
+ BLADERF_DC_CAL_RXVGA2
+} bladerf_cal_module;
+
+/**
+ * Perform DC calibration
+ *
+ * @param dev Device handle
+ * @param[in] module Module to calibrate
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_calibrate_dc(struct bladerf *dev,
+ bladerf_cal_module module);
+
+/** @} (End of FN_BLADERF1_DC_CAL) */
+
+/**
+ * @defgroup FN_BLADERF1_LOW_LEVEL Low-level accessors
+ *
+ * In a most cases, higher-level routines should be used. These routines are
+ * only intended to support development and testing.
+ *
+ * Use these routines with great care, and be sure to reference the relevant
+ * schematics, data sheets, and source code (i.e., firmware and hdl).
+ *
+ * Be careful when mixing these calls with higher-level routines that manipulate
+ * the same registers/settings.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * Enable LMS receive
+ *
+ * @note This bit is set/cleared by bladerf_enable_module()
+ */
+#define BLADERF_GPIO_LMS_RX_ENABLE (1 << 1)
+
+/**
+ * Enable LMS transmit
+ *
+ * @note This bit is set/cleared by bladerf_enable_module()
+ */
+#define BLADERF_GPIO_LMS_TX_ENABLE (1 << 2)
+
+/**
+ * Switch to use TX low band (300MHz - 1.5GHz)
+ *
+ * @note This is set using bladerf_set_frequency().
+ */
+#define BLADERF_GPIO_TX_LB_ENABLE (2 << 3)
+
+/**
+ * Switch to use TX high band (1.5GHz - 3.8GHz)
+ *
+ * @note This is set using bladerf_set_frequency().
+ */
+#define BLADERF_GPIO_TX_HB_ENABLE (1 << 3)
+
+/**
+ * Counter mode enable
+ *
+ * Setting this bit to 1 instructs the FPGA to replace the (I, Q) pair in sample
+ * data with an incrementing, little-endian, 32-bit counter value. A 0 in bit
+ * specifies that sample data should be sent (as normally done).
+ *
+ * This feature is useful when debugging issues involving dropped samples.
+ */
+#define BLADERF_GPIO_COUNTER_ENABLE (1 << 9)
+
+/**
+ * Bit mask representing the rx mux selection
+ *
+ * @note These bits are set using bladerf_set_rx_mux()
+ */
+#define BLADERF_GPIO_RX_MUX_MASK (0x7 << BLADERF_GPIO_RX_MUX_SHIFT)
+
+/**
+ * Starting bit index of the RX mux values in FX3 <-> FPGA GPIO bank
+ */
+#define BLADERF_GPIO_RX_MUX_SHIFT 8
+
+/**
+ * Switch to use RX low band (300M - 1.5GHz)
+ *
+ * @note This is set using bladerf_set_frequency().
+ */
+#define BLADERF_GPIO_RX_LB_ENABLE (2 << 5)
+
+/**
+ * Switch to use RX high band (1.5GHz - 3.8GHz)
+ *
+ * @note This is set using bladerf_set_frequency().
+ */
+#define BLADERF_GPIO_RX_HB_ENABLE (1 << 5)
+
+/**
+ * This GPIO bit configures the FPGA to use smaller DMA transfers (256 cycles
+ * instead of 512). This is required when the device is not connected at Super
+ * Speed (i.e., when it is connected at High Speed).
+ *
+ * However, the caller need not set this in bladerf_config_gpio_write() calls.
+ * The library will set this as needed; callers generally do not need to be
+ * concerned with setting/clearing this bit.
+ */
+#define BLADERF_GPIO_FEATURE_SMALL_DMA_XFER (1 << 7)
+
+/**
+ * Enable Packet mode
+ */
+#define BLADERF_GPIO_PACKET (1 << 19)
+
+/**
+ * Enable 8bit sample mode
+ */
+#define BLADERF_GPIO_8BIT_MODE (1 << 20)
+
+/**
+ * AGC enable control bit
+ *
+ * @note This is set using bladerf_set_gain_mode().
+ */
+#define BLADERF_GPIO_AGC_ENABLE (1 << 18)
+
+/**
+ * Enable-bit for timestamp counter in the FPGA
+ */
+#define BLADERF_GPIO_TIMESTAMP (1 << 16)
+
+/**
+ * Timestamp 2x divider control.
+ *
+ * @note <b>Important</b>: This bit has no effect and is always enabled (1) in
+ * FPGA versions >= v0.3.0.
+ *
+ * @note The remainder of the description of this bit is presented here for
+ * historical purposes only. It is only relevant to FPGA versions <= v0.1.2.
+ *
+ * By default (value = 0), the sample counter is incremented with I and Q,
+ * yielding two counts per sample.
+ *
+ * Set this bit to 1 to enable a 2x timestamp divider, effectively achieving 1
+ * timestamp count per sample.
+ * */
+#define BLADERF_GPIO_TIMESTAMP_DIV2 (1 << 17)
+
+/**
+ * Packet capable core present bit.
+ *
+ * @note This is a read-only bit. The FPGA sets its value, and uses it to inform
+ * host that there is a core capable of using packets in the FPGA.
+ */
+#define BLADERF_GPIO_PACKET_CORE_PRESENT (1 << 28)
+
+/**
+ * Write value to VCTCXO trim DAC.
+ *
+ * \deprecated Use bladerf_trim_dac_write().
+ *
+ * This should not be used when the VCTCXO tamer is enabled.
+ *
+ * @param dev Device handle
+ * @param[in] val Value to write to VCTCXO trim DAC
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_dac_write(struct bladerf *dev, uint16_t val);
+
+/**
+ * Read value from VCTCXO trim DAC.
+ *
+ * \deprecated Use bladerf_trim_dac_read().
+ *
+ * This is similar to bladerf_get_vctcxo_trim(), except that it returns the
+ * current trim DAC value, as opposed to the calibration value read from flash.
+ *
+ * Use this if you are trying to query the value after having previously made
+ * calls to bladerf_dac_write().
+ *
+ * @param dev Device handle
+ * @param[out] val Value to read from VCTCXO trim DAC
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_dac_read(struct bladerf *dev, uint16_t *val);
+
+/**
+ * Read a Si5338 register
+ *
+ * @param dev Device handle
+ * @param[in] address Si5338 register address
+ * @param[out] val Register value
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_si5338_read(struct bladerf *dev,
+ uint8_t address,
+ uint8_t *val);
+
+/**
+ * Write a Si5338 register
+ *
+ * @param dev Device handle
+ * @param[in] address Si5338 register address
+ * @param[in] val Value to write to register
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_si5338_write(struct bladerf *dev,
+ uint8_t address,
+ uint8_t val);
+
+/**
+ * Read a LMS register
+ *
+ * @param dev Device handle
+ * @param[in] address LMS register address
+ * @param[out] val Register value
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_lms_read(struct bladerf *dev,
+ uint8_t address,
+ uint8_t *val);
+
+/**
+ * Write a LMS register
+ *
+ * @param dev Device handle
+ * @param[in] address LMS register address
+ * @param[in] val Value to write to register
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_lms_write(struct bladerf *dev,
+ uint8_t address,
+ uint8_t val);
+
+/**
+ * This structure is used to directly apply DC calibration register values to
+ * the LMS, rather than use the values resulting from an auto-calibration.
+ *
+ * A value < 0 is used to denote that the specified value should not be written.
+ * If a value is to be written, it will be truncated to 8-bits.
+ */
+struct bladerf_lms_dc_cals {
+ int16_t lpf_tuning; /**< LPF tuning module */
+ int16_t tx_lpf_i; /**< TX LPF I filter */
+ int16_t tx_lpf_q; /**< TX LPF Q filter */
+ int16_t rx_lpf_i; /**< RX LPF I filter */
+ int16_t rx_lpf_q; /**< RX LPF Q filter */
+ int16_t dc_ref; /**< RX VGA2 DC reference module */
+ int16_t rxvga2a_i; /**< RX VGA2, I channel of first gain stage */
+ int16_t rxvga2a_q; /**< RX VGA2, Q channel of first gain stage */
+ int16_t rxvga2b_i; /**< RX VGA2, I channel of second gain stage */
+ int16_t rxvga2b_q; /**< RX VGA2, Q channel of second gain stage */
+};
+
+/**
+ * Manually load values into LMS6002 DC calibration registers.
+ *
+ * This is generally intended for applying a set of known values resulting from
+ * a previous run of the LMS autocalibrations.
+ *
+ * @param dev Device handle
+ * @param[in] dc_cals Calibration values to load. Values set to <0 will
+ * not be applied.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_lms_set_dc_cals(
+ struct bladerf *dev, const struct bladerf_lms_dc_cals *dc_cals);
+
+/**
+ * Retrieve the current DC calibration values from the LMS6002
+ *
+ * @param dev Device handle
+ * @param[out] dc_cals Populated with current values
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_lms_get_dc_cals(struct bladerf *dev,
+ struct bladerf_lms_dc_cals *dc_cals);
+
+/**
+ * Write value to secondary XB SPI
+ *
+ * @param dev Device handle
+ * @param[out] val Value to write to XB SPI
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_xb_spi_write(struct bladerf *dev, uint32_t val);
+
+/** @} (End of FN_BLADERF1_LOW_LEVEL) */
+
+/** @} (End of BLADERF1) */
+
+#endif /* BLADERF1_H_ */
diff --git a/Radio/HW/BladeRF/include/bladeRF2.h b/Radio/HW/BladeRF/include/bladeRF2.h
new file mode 100644
index 0000000..9de1f9e
--- /dev/null
+++ b/Radio/HW/BladeRF/include/bladeRF2.h
@@ -0,0 +1,561 @@
+/**
+ * @file bladeRF2.h
+ *
+ * @brief bladeRF2-specific API
+ *
+ * Copyright (C) 2013-2017 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef BLADERF2_H_
+#define BLADERF2_H_
+
+/**
+ * @defgroup BLADERF2 bladeRF2-specific API
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * @defgroup FN_BLADERF2_BIAS_TEE Bias Tee Control
+ *
+ * @{
+ */
+
+/**
+ * Get current bias tee state
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] enable True if bias tee active, false otherwise
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_bias_tee(struct bladerf *dev,
+ bladerf_channel ch,
+ bool *enable);
+
+/**
+ * Get current bias tee state
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] enable True to activate bias tee, false to deactivate
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_bias_tee(struct bladerf *dev,
+ bladerf_channel ch,
+ bool enable);
+
+/** @} (End of FN_BLADERF2_BIAS_TEE) */
+
+/**
+ * @defgroup FN_BLADERF2_LOW_LEVEL Low-level accessors
+ *
+ * In most cases, higher-level routines should be used. These routines are only
+ * intended to support development and testing.
+ *
+ * Use these routines with great care, and be sure to reference the relevant
+ * schematics, data sheets, and source code (i.e., firmware and hdl).
+ *
+ * Be careful when mixing these calls with higher-level routines that manipulate
+ * the same registers/settings.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * @defgroup FN_BLADERF2_LOW_LEVEL_RFIC RF Integrated Circuit
+ *
+ * @{
+ */
+
+/**
+ * Read a RFIC register
+ *
+ * @param dev Device handle
+ * @param[in] address Register address
+ * @param[out] val Register value
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_rfic_register(struct bladerf *dev,
+ uint16_t address,
+ uint8_t *val);
+/**
+ * Write a RFIC register
+ *
+ * @param dev Device handle
+ * @param[in] address Register address
+ * @param[in] val Value to write to register
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_rfic_register(struct bladerf *dev,
+ uint16_t address,
+ uint8_t val);
+
+/**
+ * Read the temperature from the RFIC
+ *
+ * @param dev Device handle
+ * @param[out] val Temperature in degrees C
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_rfic_temperature(struct bladerf *dev, float *val);
+
+/**
+ * Read the RSSI for the selected channel from the RFIC
+ *
+ * @note This is a relative value, not an absolute value. If an absolute
+ * value (e.g. in dBm) is desired, a calibration should be performed
+ * against a reference signal.
+ *
+ * @note See `fpga_common/src/ad936x_params.c` for the RSSI control parameters.
+ *
+ * Reference: AD9361 Reference Manual UG-570
+ *
+ * @param dev Device handle
+ * @param ch Channel to query
+ * @param[out] pre_rssi Preamble RSSI in dB (first calculated RSSI result)
+ * @param[out] sym_rssi Symbol RSSI in dB (most recent RSSI result)
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_rfic_rssi(struct bladerf *dev,
+ bladerf_channel ch,
+ int32_t *pre_rssi,
+ int32_t *sym_rssi);
+
+/**
+ * Read the CTRL_OUT pins from the RFIC
+ *
+ * @note See AD9361 Reference Manual UG-570's "Control Output" chapter for
+ * complete information about this feature.
+ *
+ * @see bladerf_set_rfic_register()
+ *
+ * @param dev Device handle
+ * @param[out] ctrl_out Pointer for storing the retrieved value
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_rfic_ctrl_out(struct bladerf *dev, uint8_t *ctrl_out);
+
+/**
+ * RFIC RX FIR filter choices
+ */
+typedef enum {
+ BLADERF_RFIC_RXFIR_BYPASS = 0, /**< No filter */
+ BLADERF_RFIC_RXFIR_CUSTOM, /**< Custom FIR filter (currently unused) */
+ BLADERF_RFIC_RXFIR_DEC1, /**< Decimate by 1 (default) */
+ BLADERF_RFIC_RXFIR_DEC2, /**< Decimate by 2 */
+ BLADERF_RFIC_RXFIR_DEC4, /**< Decimate by 4 */
+} bladerf_rfic_rxfir;
+
+/** Default RFIC RX FIR filter */
+#define BLADERF_RFIC_RXFIR_DEFAULT BLADERF_RFIC_RXFIR_DEC1
+
+/**
+ * RFIC TX FIR filter choices
+ */
+typedef enum {
+ BLADERF_RFIC_TXFIR_BYPASS = 0, /**< No filter (default) */
+ BLADERF_RFIC_TXFIR_CUSTOM, /**< Custom FIR filter (currently unused) */
+ BLADERF_RFIC_TXFIR_INT1, /**< Interpolate by 1 */
+ BLADERF_RFIC_TXFIR_INT2, /**< Interpolate by 2 */
+ BLADERF_RFIC_TXFIR_INT4, /**< Interpolate by 4 */
+} bladerf_rfic_txfir;
+
+/** Default RFIC TX FIR filter */
+#define BLADERF_RFIC_TXFIR_DEFAULT BLADERF_RFIC_TXFIR_BYPASS
+
+/**
+ * Get the current status of the RX FIR filter on the RFIC.
+ *
+ * @param dev Device handle
+ * @param rxfir RX FIR selection
+ *
+ * @note See `fpga_common/src/ad936x_params.c` for FIR parameters.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_rfic_rx_fir(struct bladerf *dev,
+ bladerf_rfic_rxfir *rxfir);
+
+/**
+ * Set the RX FIR filter on the RFIC.
+ *
+ * @param dev Device handle
+ * @param rxfir RX FIR selection
+ *
+ * @note See `fpga_common/src/ad936x_params.c` for FIR parameters.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_rfic_rx_fir(struct bladerf *dev,
+ bladerf_rfic_rxfir rxfir);
+
+/**
+ * Get the current status of the TX FIR filter on the RFIC.
+ *
+ * @param dev Device handle
+ * @param txfir TX FIR selection
+ *
+ * @note See `fpga_common/src/ad936x_params.c` for FIR parameters.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_rfic_tx_fir(struct bladerf *dev,
+ bladerf_rfic_txfir *txfir);
+
+/**
+ * Set the TX FIR filter on the RFIC.
+ *
+ * @param dev Device handle
+ * @param txfir TX FIR selection
+ *
+ * @note See `fpga_common/src/ad936x_params.c` for FIR parameters.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_rfic_tx_fir(struct bladerf *dev,
+ bladerf_rfic_txfir txfir);
+
+/** @} (End of FN_BLADERF2_LOW_LEVEL_RFIC) */
+
+/**
+ * @defgroup FN_BLADERF2_LOW_LEVEL_PLL Phase Detector/Freq. Synth Control
+ *
+ * Reference:
+ * http://www.analog.com/media/en/technical-documentation/data-sheets/ADF4002.pdf
+ *
+ * @{
+ */
+
+/**
+ * Fetch the lock state of the Phase Detector/Frequency Synthesizer
+ *
+ * @param dev Device handle
+ * @param[out] locked True if locked, False otherwise
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_pll_lock_state(struct bladerf *dev, bool *locked);
+
+/**
+ * Fetch the state of the Phase Detector/Frequency Synthesizer
+ *
+ * @param dev Device handle
+ * @param[out] enabled True if enabled, False otherwise
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_pll_enable(struct bladerf *dev, bool *enabled);
+
+/**
+ * Enable the Phase Detector/Frequency Synthesizer
+ *
+ * Enabling this disables the VCTCXO trimmer DAC, and vice versa.
+ *
+ * @param dev Device handle
+ * @param[in] enable True to enable, False otherwise
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_pll_enable(struct bladerf *dev, bool enable);
+
+/**
+ * Get the valid range of frequencies for the reference clock input
+ *
+ * @param dev Device handle
+ * @param[out] range Reference clock frequency range
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_pll_refclk_range(struct bladerf *dev,
+ const struct bladerf_range **range);
+
+/**
+ * Get the currently-configured frequency for the reference clock
+ * input.
+ *
+ * @param dev Device handle
+ * @param[out] frequency Reference clock frequency
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_pll_refclk(struct bladerf *dev, uint64_t *frequency);
+
+/**
+ * Set the expected frequency for the reference clock input.
+ *
+ * @param dev Device handle
+ * @param[in] frequency Reference clock frequency
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_pll_refclk(struct bladerf *dev, uint64_t frequency);
+
+/**
+ * Read value from Phase Detector/Frequency Synthesizer
+ *
+ * The `address` is interpreted as the control bits (DB1 and DB0) used to write
+ * to a specific latch.
+ *
+ * @param dev Device handle
+ * @param[in] address Latch address
+ * @param[out] val Value to read from
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_pll_register(struct bladerf *dev,
+ uint8_t address,
+ uint32_t *val);
+
+/**
+ * Write value to Phase Detector/Frequency Synthesizer
+ *
+ * The `address` is interpreted as the control bits (DB1 and DB0) used to write
+ * to a specific latch. These bits are masked out in `val`
+ *
+ * @param dev Device handle
+ * @param[in] address Latch address
+ * @param[in] val Value to write to
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_pll_register(struct bladerf *dev,
+ uint8_t address,
+ uint32_t val);
+
+/** @} (End of FN_BLADERF2_LOW_LEVEL_PLL) */
+
+/**
+ * @defgroup FN_BLADERF2_LOW_LEVEL_POWER_SOURCE Power Multiplexer
+ *
+ * @{
+ */
+
+/**
+ * Power sources
+ */
+typedef enum {
+ BLADERF_UNKNOWN, /**< Unknown; manual observation may be required */
+ BLADERF_PS_DC, /**< DC Barrel Plug */
+ BLADERF_PS_USB_VBUS /**< USB Bus */
+} bladerf_power_sources;
+
+/**
+ * Get the active power source reported by the power multiplexer
+ *
+ * Reference: http://www.ti.com/product/TPS2115A
+ *
+ * @param dev Device handle
+ * @param[out] val Value read from power multiplexer
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_power_source(struct bladerf *dev,
+ bladerf_power_sources *val);
+
+/** @} (End of FN_BLADERF2_LOW_LEVEL_POWER_SOURCE) */
+
+/**
+ * @defgroup FN_BLADERF2_LOW_LEVEL_CLOCK_BUFFER Clock Buffer
+ *
+ * @{
+ */
+
+/**
+ * @defgroup FN_BLADERF2_LOW_LEVEL_CLOCK_BUFFER_SELECT Clock input selection
+ *
+ * @{
+ */
+
+/**
+ * Available clock sources
+ */
+typedef enum {
+ CLOCK_SELECT_ONBOARD, /**< Use onboard VCTCXO */
+ CLOCK_SELECT_EXTERNAL /**< Use external clock input */
+} bladerf_clock_select;
+
+/**
+ * Get the selected clock source
+ *
+ * Reference: https://www.silabs.com/documents/public/data-sheets/Si53304.pdf
+ *
+ * @param dev Device handle
+ * @param[out] sel Clock input source currently in use
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_clock_select(struct bladerf *dev,
+ bladerf_clock_select *sel);
+
+/**
+ * Set the clock source
+ *
+ * Reference: https://www.silabs.com/documents/public/data-sheets/Si53304.pdf
+ *
+ * @param dev Device handle
+ * @param[in] sel Clock input source to use
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_clock_select(struct bladerf *dev,
+ bladerf_clock_select sel);
+
+/** @} (End of FN_BLADERF2_LOW_LEVEL_CLOCK_BUFFER_SELECT) */
+
+/**
+ * @defgroup FN_BLADERF2_LOW_LEVEL_CLOCK_BUFFER_OUTPUT Clock output control
+ *
+ * @{
+ */
+
+/**
+ * Get the current state of the clock output
+ *
+ * @param dev Device handle
+ * @param[out] state Clock output state
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_clock_output(struct bladerf *dev, bool *state);
+
+/**
+ * Set the clock output (enable/disable)
+ *
+ * @param dev Device handle
+ * @param[in] enable Clock output enable
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_clock_output(struct bladerf *dev, bool enable);
+
+/** @} (End of FN_BLADERF2_LOW_LEVEL_CLOCK_BUFFER_OUTPUT) */
+
+/** @} (End of FN_BLADERF2_LOW_LEVEL_CLOCK_BUFFER) */
+
+/**
+ * @defgroup FN_BLADERF2_LOW_LEVEL_PMIC Power Monitoring
+ *
+ * @{
+ */
+
+/**
+ * Register identifiers for PMIC
+ */
+typedef enum {
+ BLADERF_PMIC_CONFIGURATION, /**< Configuration register (uint16_t) */
+ BLADERF_PMIC_VOLTAGE_SHUNT, /**< Shunt voltage (float) */
+ BLADERF_PMIC_VOLTAGE_BUS, /**< Bus voltage (float) */
+ BLADERF_PMIC_POWER, /**< Load power (float) */
+ BLADERF_PMIC_CURRENT, /**< Load current (float) */
+ BLADERF_PMIC_CALIBRATION, /**< Calibration (uint16_t) */
+} bladerf_pmic_register;
+
+/**
+ * Read value from Power Monitor IC
+ *
+ * Reference: http://www.ti.com/product/INA219
+ *
+ * @param dev Device handle
+ * @param[in] reg Register to read from
+ * @param[out] val Value read from PMIC
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_pmic_register(struct bladerf *dev,
+ bladerf_pmic_register reg,
+ void *val);
+
+/** @} (End of FN_BLADERF2_LOW_LEVEL_PMIC) */
+
+/**
+ * @defgroup FN_BLADERF2_LOW_LEVEL_RF_SWITCHING RF Switching Control
+ *
+ * @{
+ */
+
+/**
+ * RF switch configuration structure
+ */
+typedef struct {
+ uint32_t tx1_rfic_port; /**< Active TX1 output from RFIC */
+ uint32_t tx1_spdt_port; /**< RF switch configuration for the TX1 path */
+ uint32_t tx2_rfic_port; /**< Active TX2 output from RFIC */
+ uint32_t tx2_spdt_port; /**< RF switch configuration for the TX2 path */
+ uint32_t rx1_rfic_port; /**< Active RX1 input to RFIC */
+ uint32_t rx1_spdt_port; /**< RF switch configuration for the RX1 path */
+ uint32_t rx2_rfic_port; /**< Active RX2 input to RFIC */
+ uint32_t rx2_spdt_port; /**< RF switch configuration for the RX2 path */
+} bladerf_rf_switch_config;
+
+/**
+ * Read the current RF switching configuration from the bladeRF hardware.
+ *
+ * Queries both the RFIC and the RF switch and passes back a
+ * bladerf_rf_switch_config stucture.
+ *
+ * @param dev Device handle
+ * @param[out] config Switch configuration struct
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_rf_switch_config(struct bladerf *dev,
+ bladerf_rf_switch_config *config);
+
+/** @} (End of FN_BLADERF2_LOW_LEVEL_RF_SWITCHING) */
+
+/** @} (End of FN_BLADERF2_LOW_LEVEL) */
+
+/** @} (End of BLADERF2) */
+
+#endif /* BLADERF2_H_ */
diff --git a/Radio/HW/BladeRF/include/device_calibration.h b/Radio/HW/BladeRF/include/device_calibration.h
new file mode 100644
index 0000000..61cdd8a
--- /dev/null
+++ b/Radio/HW/BladeRF/include/device_calibration.h
@@ -0,0 +1,128 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2023 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef BLADERF2_CALIBRATION_H_
+#define BLADERF2_CALIBRATION_H_
+
+#include <stdint.h>
+#include "libbladeRF.h"
+
+/**
+ * @brief Converts gain calibration CSV data to a binary format.
+ *
+ * This function reads frequency and gain data from a CSV file and writes it
+ * to a binary file.
+ *
+ * @param csv_path Path to the input CSV file.
+ * @param binary_path Path to the output binary file.
+ * @param ch bladeRF channel
+ * @return 0 on success, BLADERF_ERR_* code on failure.
+ */
+int gain_cal_csv_to_bin(struct bladerf *dev, const char *csv_path, const char *binary_path, bladerf_channel ch);
+
+/**
+ * @brief Loads gain calibration data from a binary file into a bladeRF device.
+ *
+ * This function reads frequency and gain calibration data from a binary file and
+ * loads it into the specified bladeRF device.
+ *
+ * @param dev Pointer to the bladeRF device structure.
+ * @param ch Channel for which the gain calibration data is loaded.
+ * @param binary_path Path to the binary file containing frequency-gain data.
+ * @return 0 on success, BLADERF_ERR_* code on failure.
+ */
+int load_gain_calibration(struct bladerf *dev, bladerf_channel ch, const char *binary_path);
+
+/**
+ * @brief Retrieve the gain correction gain delta between the current frequency and the target frequency.
+ *
+ * This function determines the gain correction difference between two frequencies,
+ * using the calibration table associated with the specified channel.
+ * The result is based on the gain correction values retrieved from the calibration table entries.
+ *
+ * @param dev Pointer to the bladeRF device structure.
+ * @param freq The target frequency for which the gain correction gain is to be determined.
+ * @param ch The bladeRF channel to use when fetching the gain calibration table.
+ * @param compensated_gain The gain correction result
+ *
+ * @return 0 on success, BLADERF_ERR_* code on failure.
+ */
+int get_gain_correction(struct bladerf *dev,
+ bladerf_frequency freq,
+ bladerf_channel ch,
+ bladerf_gain *compensated_gain);
+
+
+/**
+ * Retrieves a gain calibration entry for a specified frequency by interpolating
+ * between the nearest lower and higher frequency calibration entries in the given
+ * calibration table.
+ *
+ * @param[in] tbl Pointer to the gain calibration table containing pre-calculated
+ * gain correction values across various frequencies.
+ * @param[in] freq The frequency for which the gain calibration entry is requested.
+ * @param[out] result Pointer to a `struct bladerf_gain_cal_entry` that will be filled
+ * with the interpolated gain calibration entry for the specified frequency.
+ * This structure must be allocated by the caller.
+ *
+ * The function first identifies the calibration entries immediately below and above the
+ * requested frequency. If the requested frequency matches a calibration point exactly,
+ * that entry is directly copied into `result`. If the requested frequency falls between
+ * two calibration points, a linear interpolation is performed to compute the gain
+ * correction value for the requested frequency, and the result is stored in `result`.
+ *
+ * @note This function assumes that the calibration table is sorted by frequency.
+ *
+ * @return 0 on success, indicating that `result` has been populated with the interpolated
+ * gain correction value. If the function fails, a negative error code is returned:
+ * - BLADERF_ERR_INVAL if the `result` pointer is NULL,
+ * - BLADERF_ERR_UNEXPECTED if suitable floor and ceiling entries cannot be found
+ * in the calibration table.
+ */
+int get_gain_cal_entry(const struct bladerf_gain_cal_tbl *tbl,
+ bladerf_frequency freq,
+ struct bladerf_gain_cal_entry *result);
+
+/**
+ * Applies compensated gain given the current gain target and center frequency
+ *
+ * @param dev The bladeRF device structure pointer.
+ * @param ch The bladeRF channel to use.
+ * @param frequency The target frequency.
+ */
+int apply_gain_correction(struct bladerf *dev, bladerf_channel ch, bladerf_frequency frequency);
+
+/**
+ * @brief Frees the resources of a gain calibration table and resets its fields.
+ *
+ * This function frees the dynamically allocated memory for the 'entries' and 'file_path'
+ * fields of the bladerf_gain_cal_tbl structure. After freeing these resources, it resets all fields
+ * of the structure to their default values, ensuring the structure is clean and can be
+ * reused or safely deallocated. The function logs a verbose message upon starting the
+ * freeing process.
+ *
+ * @param tbl Pointer to the bladerf_gain_cal_tbl structure. If this pointer is NULL,
+ * the function returns immediately without performing any operations.
+ * It is assumed that the caller ensures the validity of this pointer.
+ */
+void gain_cal_tbl_free(struct bladerf_gain_cal_tbl *tbl);
+
+#endif
diff --git a/Radio/HW/BladeRF/include/libbladeRF.h b/Radio/HW/BladeRF/include/libbladeRF.h
new file mode 100644
index 0000000..6124465
--- /dev/null
+++ b/Radio/HW/BladeRF/include/libbladeRF.h
@@ -0,0 +1,4501 @@
+/**
+ * @file libbladeRF.h
+ *
+ * @brief bladeRF library
+ *
+ * Copyright (C) 2013-2017 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef LIBBLADERF_H_
+#define LIBBLADERF_H_
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+/**
+ * @ingroup FN_LIBRARY_VERSION
+ *
+ * libbladeRF API version
+ *
+ * As of libbladeRF v1.5.0, this macro is defined to assist with feature
+ * detection. Generally, this will be used as follows:
+ *
+ * @code
+ * #if defined(LIBBLADERF_API_VERSION) && (LIBBLADERF_API_VERSION >= 0x01050000)
+ * // ... Use features added in libbladeRF v1.5.0 ...
+ * #endif
+ * @endcode
+ *
+ * This value is defined as follows:
+ * `(major << 24) | (minor << 16) | (patch << 8) | (reserved << 0)`
+ *
+ * The reserved field may be used at a later date to denote additions between
+ * releases. It will be set to zero when not used.
+ *
+ * This value is intended to track the values returned by bladerf_version().
+ * Fields are updated per the scheme defined here:
+ *
+ * https://github.com/Nuand/bladeRF/blob/master/doc/development/versioning.md
+ */
+#define LIBBLADERF_API_VERSION (0x02050100)
+
+#ifdef __cplusplus
+extern "C" {
+#else
+/**
+ * stdbool.h is not applicable for C++ programs, as the language inherently
+ * provides the bool type.
+ *
+ * Users of Visual Studio 2012 and earlier will need to supply a stdbool.h
+ * implementation, as it is not included with the toolchain. One is provided
+ * with the bladeRF source code. Visual Studio 2013 onward supplies this header.
+ */
+#include <stdbool.h>
+#endif
+
+// clang-format off
+#if defined _WIN32 || defined __CYGWIN__
+# include <windows.h>
+# define CALL_CONV __cdecl
+# ifdef __GNUC__
+# define API_EXPORT __attribute__ ((dllexport))
+# else
+# define API_EXPORT __declspec(dllexport)
+# endif
+#elif defined _DOXYGEN_ONLY_ || defined MATLAB_LINUX_THUNK_BUILD_
+ /** Marks an API routine to be made visible to the dynamic loader.
+ * This is OS and/or compiler-specific. */
+# define API_EXPORT
+ /** Specifies calling convention, if necessary.
+ * This is OS and/or compiler-specific. */
+# define CALL_CONV
+#else
+# define API_EXPORT __attribute__ ((visibility ("default")))
+# define CALL_CONV
+#endif
+// clang-format on
+
+/**
+ * @defgroup FN_INIT Initialization
+ *
+ * The functions in this section provide the ability query and inspect available
+ * devices, initialize them, and deinitialize them.
+ *
+ * See the \link boilerplate.html Device configuration boilerplate\endlink
+ * page for an overview on how to open and configure a device.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/** This structure is an opaque device handle */
+struct bladerf;
+
+/**
+ * Backend by which the host communicates with the device
+ */
+typedef enum {
+ BLADERF_BACKEND_ANY, /**< "Don't Care" -- use any available
+ * backend */
+ BLADERF_BACKEND_LINUX, /**< Linux kernel driver */
+ BLADERF_BACKEND_LIBUSB, /**< libusb */
+ BLADERF_BACKEND_CYPRESS, /**< CyAPI */
+ BLADERF_BACKEND_DUMMY = 100, /**< Dummy used for development purposes */
+} bladerf_backend;
+
+/** Length of device description string, including NUL-terminator */
+#define BLADERF_DESCRIPTION_LENGTH 33
+
+/** Length of device serial number string, including NUL-terminator */
+#define BLADERF_SERIAL_LENGTH 33
+
+/**
+ * Information about a bladeRF attached to the system
+ */
+struct bladerf_devinfo {
+ bladerf_backend backend; /**< Backend to use when connecting to
+ * device */
+ char serial[BLADERF_SERIAL_LENGTH]; /**< Device serial number string */
+ uint8_t usb_bus; /**< Bus # device is attached to */
+ uint8_t usb_addr; /**< Device address on bus */
+ unsigned int instance; /**< Device instance or ID */
+
+ /** Manufacturer description string */
+ char manufacturer[BLADERF_DESCRIPTION_LENGTH];
+ char product[BLADERF_DESCRIPTION_LENGTH]; /**< Product description string */
+};
+
+/**
+ * Information about a bladeRF attached to the system
+ */
+struct bladerf_backendinfo {
+ int handle_count; /**< Backend handle count */
+ void *handle; /**< Backend handle for device */
+ int lock_count; /**< Backend lock count */
+ void *lock; /**< Backend lock for device */
+};
+
+/**
+ * Open specified device using a device identifier string. See
+ * bladerf_open_with_devinfo() if a device identifier string is not readily
+ * available.
+ *
+ * The general form of the device identifier string is;
+ * @code{.txt}
+ * <backend>:[device=<bus>:<addr>] [instance=<n>] [serial=<serial>]
+ * @endcode
+ *
+ * An empty ("") or NULL device identifier will result in the first
+ * encountered device being opened (using the first discovered backend)
+ *
+ * The 'backend' describes the mechanism used to communicate with the device,
+ * and may be one of the following:
+ * - *: Any available backend
+ * - libusb: libusb (See libusb changelog notes for required version, given
+ * your OS and controller)
+ * - cypress: Cypress CyUSB/CyAPI backend (Windows only)
+ *
+ * If no arguments are provided after the backend, the first encountered
+ * device on the specified backend will be opened. Note that a backend is
+ * required, if any arguments are to be provided.
+ *
+ * Next, any provided arguments are provide as used to find the desired device.
+ * Be sure not to over constrain the search. Generally, only one of the above
+ * is required -- providing all of these may over constrain the search for the
+ * desired device (e.g., if a serial number matches, but not on the specified
+ * bus and address.)
+ *
+ * - device=\<bus\>:\<addr\>
+ * - Specifies USB bus and address. Decimal or hex prefixed by '0x' is
+ * permitted.
+ * - instance=\<n\>
+ * - Nth instance encountered, 0-indexed
+ * - serial=\<serial\>
+ * - Device's serial number.
+ *
+ * Below is an example of how to open a device with a specific serial
+ * number, using any avaiable backend supported by libbladeRF:
+ *
+ * @code {.c}
+ * struct bladerf *dev;
+ * int status = bladerf_open(&dev, "*:serial=f12ce1037830a1b27f3ceeba1f521413");
+ * if (status != 0) {
+ * fprintf(stderr, "Unable to open device: %s\n",
+ * bladerf_strerror(status));
+ * return status;
+ * }
+ * @endcode
+ *
+ * @param[out] device Update with device handle on success
+ * @param[in] device_identifier Device identifier, formatted as described
+ * above
+ *
+ * @return 0 on success, or value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_open(struct bladerf **device,
+ const char *device_identifier);
+
+/**
+ * Close device
+ *
+ * @note Failing to close a device will result in memory leaks.
+ *
+ * @post `device` is deallocated and may no longer be used.
+ *
+ * @param device Device handle previously obtained by bladerf_open(). This
+ * function does nothing if device is NULL.
+ */
+API_EXPORT
+void CALL_CONV bladerf_close(struct bladerf *device);
+
+/**
+ * Opens device specified by provided bladerf_devinfo structure
+ *
+ * This function is generally preferred over bladerf_open() when a device
+ * identifier string is not already provided.
+ *
+ * The most common uses of this function are to:
+ * - Open a device based upon the results of bladerf_get_device_list()
+ * - Open a specific device based upon its serial number
+ *
+ * Below is an example of how to use this function to open a device with a
+ * specific serial number:
+ *
+ * @snippet open_via_serial.c example_snippet
+ *
+ * @param[out] device Update with device handle on success
+ * @param[in] devinfo Device specification. If NULL, any available
+ * device will be opened.
+ *
+ * @return 0 on success, or value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_open_with_devinfo(struct bladerf **device,
+ struct bladerf_devinfo *devinfo);
+
+/**
+ * Obtain a list of bladeRF devices attached to the system
+ *
+ * @param[out] devices
+ *
+ * @return number of items in returned device list, or value from
+ * \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_device_list(struct bladerf_devinfo **devices);
+
+/**
+ * Free device list returned by bladerf_get_device_list()
+ *
+ * @param[inout] devices List of available devices
+ */
+API_EXPORT
+void CALL_CONV bladerf_free_device_list(struct bladerf_devinfo *devices);
+
+/**
+ * Initialize a device identifier information structure to a "wildcard" state.
+ *
+ * The values in each field will match any value for that field.
+ *
+ * @note Passing a bladerf_devinfo initialized with this function to
+ * bladerf_open_with_devinfo() will match the first device found.
+ */
+API_EXPORT
+void CALL_CONV bladerf_init_devinfo(struct bladerf_devinfo *info);
+
+/**
+ * Fill out a provided bladerf_devinfo structure, given an open device handle.
+ *
+ * @pre `dev` must be a valid device handle.
+ *
+ * @param dev Device handle previously obtained with bladerf_open()
+ * @param[out] info Device information populated by this function
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_devinfo(struct bladerf *dev,
+ struct bladerf_devinfo *info);
+
+
+/**
+ * Fill out a provided bladerf_backendinfo structure, given an open device handle.
+ *
+ * @pre `dev` must be a valid device handle.
+ *
+ * @param dev Device handle previously obtained with bladerf_open()
+ * @param[out] info Backend information populated by this function
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_backendinfo(struct bladerf *dev,
+ struct bladerf_backendinfo *info);
+/**
+ * Populate a device identifier information structure using the provided
+ * device identifier string.
+ *
+ * @param[in] devstr Device identifier string, formated as described
+ * in the bladerf_open() documentation
+ * @param[out] info Upon success, this will be filled out according to the
+ * provided device identifier string, with wildcards for
+ * any fields that were not provided.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_devinfo_from_str(const char *devstr,
+ struct bladerf_devinfo *info);
+
+/**
+ * Test whether two device identifier information structures match, taking
+ * wildcard values into account.
+ *
+ * @param[in] a the first bladerf_devinfo struct
+ * @param[in] b the second bladerf_devinfo struct
+ */
+API_EXPORT
+bool CALL_CONV bladerf_devinfo_matches(const struct bladerf_devinfo *a,
+ const struct bladerf_devinfo *b);
+
+/**
+ * Test whether a provided device string matches a device described by
+ * the provided bladerf_devinfo structure
+ *
+ * @param[in] dev_str Devices string, formated as described in the
+ * the documentation of bladerf_open
+ * @param[in] info Device info to compare with
+ *
+ * @return true upon a match, false otherwise
+ */
+API_EXPORT
+bool CALL_CONV bladerf_devstr_matches(const char *dev_str,
+ struct bladerf_devinfo *info);
+
+/**
+ * Retrieve the backend string associated with the specified
+ * backend enumeration value.
+ *
+ * @return A string that can used to specify the `backend` portion of a device
+ * identifier string. (See bladerf_open().)
+ */
+API_EXPORT
+const char *CALL_CONV bladerf_backend_str(bladerf_backend backend);
+
+/**
+ * Enable or disable USB device reset operation upon opening a device for
+ * future bladerf_open() and bladerf_open_with_devinfo() calls.
+ *
+ * This operation has been found to be necessary on Linux-based systems for
+ * some USB 3.0 controllers on Linux.
+ *
+ * This <b>does not</b> reset the state of the device in terms of its frequency,
+ * gain, sample rate, etc. settings.
+ *
+ * @param[in] enabled Set true to enable the use of the USB device reset,
+ * and false otherwise.
+ */
+API_EXPORT
+void CALL_CONV bladerf_set_usb_reset_on_open(bool enabled);
+
+/** @} (End FN_INIT) */
+
+/**
+ * @defgroup FN_INFO Device properties
+ *
+ * These functions provide the ability to query various pieces of information
+ * from an attached device.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * Range structure
+ */
+struct bladerf_range {
+ int64_t min; /**< Minimum value */
+ int64_t max; /**< Maximum value */
+ int64_t step; /**< Step of value */
+ float scale; /**< Unit scale */
+};
+
+/**
+ * Serial number structure
+ */
+struct bladerf_serial {
+ char serial[BLADERF_SERIAL_LENGTH]; /**< Device serial number string */
+};
+
+/**
+ * Version structure for FPGA, firmware, libbladeRF, and associated utilities
+ */
+struct bladerf_version {
+ uint16_t major; /**< Major version */
+ uint16_t minor; /**< Minor version */
+ uint16_t patch; /**< Patch version */
+ const char *describe; /**< Version string with any additional suffix
+ * information.
+ *
+ * @warning Do not attempt to modify or free()
+ * this string. */
+};
+
+/**
+ * FPGA device variant (size)
+ */
+typedef enum {
+ BLADERF_FPGA_UNKNOWN = 0, /**< Unable to determine FPGA variant */
+ BLADERF_FPGA_40KLE = 40, /**< 40 kLE FPGA */
+ BLADERF_FPGA_115KLE = 115, /**< 115 kLE FPGA */
+ BLADERF_FPGA_A4 = 49, /**< 49 kLE FPGA (A4) */
+ BLADERF_FPGA_A5 = 77, /**< 77 kLE FPGA (A5) */
+ BLADERF_FPGA_A9 = 301 /**< 301 kLE FPGA (A9) */
+} bladerf_fpga_size;
+
+/**
+ * This enum describes the USB Speed at which the bladeRF is connected.
+ * Speeds not listed here are not supported.
+ */
+typedef enum {
+ BLADERF_DEVICE_SPEED_UNKNOWN,
+ BLADERF_DEVICE_SPEED_HIGH,
+ BLADERF_DEVICE_SPEED_SUPER
+} bladerf_dev_speed;
+
+/**
+ * FPGA configuration source
+ *
+ * Note: the numbering of this enum must match NuandFpgaConfigSource in
+ * firmware_common/bladeRF.h
+ */
+typedef enum {
+ BLADERF_FPGA_SOURCE_UNKNOWN = 0, /**< Uninitialized/invalid */
+ BLADERF_FPGA_SOURCE_FLASH = 1, /**< Last FPGA load was from flash */
+ BLADERF_FPGA_SOURCE_HOST = 2 /**< Last FPGA load was from host */
+} bladerf_fpga_source;
+
+/**
+ * Query a device's serial number (deprecated)
+ *
+ * @param dev Device handle
+ * @param[out] serial This user-supplied buffer, which <b>must be at least
+ * ::BLADERF_SERIAL_LENGTH bytes</b>, will be updated to
+ * contain a NUL-terminated serial number string. If an
+ * error occurs (as indicated by a non-zero return value),
+ * no data will be written to this buffer.
+ *
+ * @deprecated New code should use ::bladerf_get_serial_struct instead.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_serial(struct bladerf *dev, char *serial);
+
+/**
+ * Query a device's serial number
+ *
+ * @param dev Device handle
+ * @param[out] serial Pointer to a bladerf_serial structure, which will be
+ * populated with a `serial` string on success.
+ *
+ * Example code:
+ *
+ * @code
+ * struct bladerf_serial sn;
+ *
+ * status = bladerf_get_serial_struct(dev, &sn);
+ * if (status < 0) {
+ * // error handling here
+ * }
+ *
+ * printf("Serial number: %s\n", sn.serial);
+ * @endcode
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_serial_struct(struct bladerf *dev,
+ struct bladerf_serial *serial);
+
+/**
+ * Query a device's FPGA size
+ *
+ * @param dev Device handle
+ * @param[out] size Will be updated with the on-board FPGA's size. If an
+ * error occurs, no data will be written to this pointer.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_fpga_size(struct bladerf *dev,
+ bladerf_fpga_size *size);
+
+/**
+ * Query a device's expected FPGA bitstream length, in bytes
+ *
+ * @param dev Device handle
+ * @param[out] size Will be updated with expected bitstream length. If an
+ * error occurs, no data will be written to this pointer.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_fpga_bytes(struct bladerf *dev, size_t *size);
+
+/**
+ * Query a device's Flash size
+ *
+ * @param dev Device handle
+ * @param[out] size Will be updated with the size of the onboard flash,
+ * in bytes. If an error occurs, no data will be written
+ * to this pointer.
+ * @param[out] is_guess True if the flash size is a guess (using FPGA size).
+ * False if the flash ID was queried and its size
+ * was successfully decoded.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_flash_size(struct bladerf *dev,
+ uint32_t *size,
+ bool *is_guess);
+
+/**
+ * Query firmware version
+ *
+ * @param dev Device handle
+ * @param[out] version Updated to contain firmware version
+ *
+ * @return 0 on success, value from \ref RETCODES list upon failing to retrieve
+ * this information from the device.
+ */
+API_EXPORT
+int CALL_CONV bladerf_fw_version(struct bladerf *dev,
+ struct bladerf_version *version);
+
+/**
+ * Check FPGA configuration status
+ *
+ * @param dev Device handle
+ *
+ * @return 1 if FPGA is configured, 0 if it is not,
+ * and value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_is_fpga_configured(struct bladerf *dev);
+
+/**
+ * Query FPGA version
+ *
+ * @param dev Device handle
+ * @param[out] version Updated to contain firmware version
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_fpga_version(struct bladerf *dev,
+ struct bladerf_version *version);
+
+/**
+ * Query FPGA configuration source
+ *
+ * Determine whether the FPGA image was loaded from flash, or if it was
+ * loaded from the host, by asking the firmware for the last-known FPGA
+ * configuration source.
+ *
+ * @param dev Device handle
+ * @param[out] source Source of the configuration
+ *
+ * @return 0 on success, ::BLADERF_ERR_UNSUPPORTED if the
+ * BLADERF_CAP_FW_FPGA_SOURCE capability is not present, value from \ref
+ * RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_fpga_source(struct bladerf *dev,
+ bladerf_fpga_source *source);
+
+/**
+ * Obtain the bus speed at which the device is operating
+ *
+ * @param dev Device handle
+ *
+ * @return Device speed enumeration
+ */
+API_EXPORT
+bladerf_dev_speed CALL_CONV bladerf_device_speed(struct bladerf *dev);
+
+/**
+ * Get the board name
+ *
+ * @param dev Device handle
+ *
+ * @return Pointer to C string with the board's model name, either `bladerf1`
+ * for a bladeRF x40/x115, or `bladerf2` for a bladeRF Micro.
+ */
+API_EXPORT
+const char *CALL_CONV bladerf_get_board_name(struct bladerf *dev);
+
+/** @} (End FN_INFO) */
+
+/**
+ * @defgroup FN_CHANNEL Channel control
+ *
+ * The RX and TX channels are independently configurable. As such, many
+ * libbladeRF functions require a ::bladerf_channel parameter to specify the
+ * desired channel.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * Channel type
+ *
+ * Example usage:
+ *
+ * @code{.c}
+ * // RX Channel 0
+ * bladerf_channel ch = BLADERF_CHANNEL_RX(0);
+ *
+ * // RX Channel 1
+ * bladerf_channel ch = BLADERF_CHANNEL_RX(1);
+ *
+ * // TX Channel 0
+ * bladerf_channel ch = BLADERF_CHANNEL_TX(0);
+ *
+ * // TX Channel 1
+ * bladerf_channel ch = BLADERF_CHANNEL_TX(1);
+ * @endcode
+ */
+typedef int bladerf_channel;
+
+/**
+ * RX Channel Macro
+ *
+ * Example usage:
+ *
+ * @code{.c}
+ * // RX Channel 0
+ * bladerf_channel ch = BLADERF_CHANNEL_RX(0);
+ *
+ * // RX Channel 1
+ * bladerf_channel ch = BLADERF_CHANNEL_RX(1);
+ * @endcode
+ */
+#define BLADERF_CHANNEL_RX(ch) (bladerf_channel)(((ch) << 1) | 0x0)
+
+/**
+ * TX Channel Macro
+ *
+ * Example usage:
+ *
+ * @code{.c}
+ * // TX Channel 0
+ * bladerf_channel ch = BLADERF_CHANNEL_TX(0);
+ *
+ * // TX Channel 1
+ * bladerf_channel ch = BLADERF_CHANNEL_TX(1);
+ * @endcode
+ */
+#define BLADERF_CHANNEL_TX(ch) (bladerf_channel)(((ch) << 1) | 0x1)
+
+/**
+ * Invalid channel
+ */
+#define BLADERF_CHANNEL_INVALID (bladerf_channel)(-1)
+
+/** @cond IGNORE */
+#define BLADERF_DIRECTION_MASK (0x1)
+/** @endcond */
+
+/** @cond IGNORE */
+/* Backwards compatible mapping to `bladerf_module`. */
+typedef bladerf_channel bladerf_module;
+#define BLADERF_MODULE_INVALID BLADERF_CHANNEL_INVALID
+#define BLADERF_MODULE_RX BLADERF_CHANNEL_RX(0)
+#define BLADERF_MODULE_TX BLADERF_CHANNEL_TX(0)
+/** @endcond */
+
+/**
+ * Convenience macro: true if argument is a TX channel
+ */
+#define BLADERF_CHANNEL_IS_TX(ch) (ch & BLADERF_TX)
+
+/**
+ * Stream direction
+ */
+typedef enum {
+ BLADERF_RX = 0, /**< Receive direction */
+ BLADERF_TX = 1, /**< Transmit direction */
+} bladerf_direction;
+
+/**
+ * Stream channel layout
+ */
+typedef enum {
+ BLADERF_RX_X1 = 0, /**< x1 RX (SISO) */
+ BLADERF_TX_X1 = 1, /**< x1 TX (SISO) */
+ BLADERF_RX_X2 = 2, /**< x2 RX (MIMO) */
+ BLADERF_TX_X2 = 3, /**< x2 TX (MIMO) */
+} bladerf_channel_layout;
+
+/**
+ * Get the number of RX or TX channels supported by the given device
+ *
+ * @param dev Device handle
+ * @param[in] dir Stream direction
+ *
+ * @return Number of channels
+ */
+API_EXPORT
+size_t CALL_CONV bladerf_get_channel_count(struct bladerf *dev,
+ bladerf_direction dir);
+
+/**
+ * @defgroup FN_GAIN Gain
+ *
+ * These functions provide control over the device's RX and TX gain stages.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * Gain value, in decibels (dB)
+ *
+ * May be positive or negative.
+ */
+typedef int bladerf_gain;
+
+/**
+ * Gain control modes
+ *
+ * In general, the default mode is automatic gain control. This will
+ * continuously adjust the gain to maximize dynamic range and minimize clipping.
+ *
+ * @note Implementers are encouraged to simply present a boolean choice between
+ * "AGC On" (::BLADERF_GAIN_DEFAULT) and "AGC Off" (::BLADERF_GAIN_MGC).
+ * The remaining choices are for advanced use cases.
+ */
+typedef enum {
+ /** Device-specific default (automatic, when available)
+ *
+ * On the bladeRF x40 and x115 with FPGA versions >= v0.7.0, this is
+ * automatic gain control.
+ *
+ * On the bladeRF 2.0 Micro, this is BLADERF_GAIN_SLOWATTACK_AGC with
+ * reasonable default settings.
+ */
+ BLADERF_GAIN_DEFAULT,
+
+ /** Manual gain control
+ *
+ * Available on all bladeRF models.
+ */
+ BLADERF_GAIN_MGC,
+
+ /** Automatic gain control, fast attack (advanced)
+ *
+ * Only available on the bladeRF 2.0 Micro. This is an advanced option, and
+ * typically requires additional configuration for ideal performance.
+ */
+ BLADERF_GAIN_FASTATTACK_AGC,
+
+ /** Automatic gain control, slow attack (advanced)
+ *
+ * Only available on the bladeRF 2.0 Micro. This is an advanced option, and
+ * typically requires additional configuration for ideal performance.
+ */
+ BLADERF_GAIN_SLOWATTACK_AGC,
+
+ /** Automatic gain control, hybrid attack (advanced)
+ *
+ * Only available on the bladeRF 2.0 Micro. This is an advanced option, and
+ * typically requires additional configuration for ideal performance.
+ */
+ BLADERF_GAIN_HYBRID_AGC,
+} bladerf_gain_mode;
+
+/** Default AGC mode (for backwards compatibility with libbladeRF 1.x) */
+#define BLADERF_GAIN_AUTOMATIC BLADERF_GAIN_DEFAULT
+/** Manual gain control (for backwards compatibility with libbladeRF 1.x) */
+#define BLADERF_GAIN_MANUAL BLADERF_GAIN_MGC
+
+/**
+ * Mapping between C string description of gain modes and bladerf_gain_mode
+ */
+struct bladerf_gain_modes {
+ const char *name; /**< Name of gain mode */
+ bladerf_gain_mode mode; /**< Gain mode enumeration */
+};
+
+/**
+ * Set overall system gain
+ *
+ * This sets an overall system gain, optimally proportioning the gain between
+ * multiple gain stages if applicable.
+ *
+ * @see Use bladerf_get_gain_range() to determine the range of system gain.
+ *
+ * On receive channels, 60 dB is the maximum gain level.
+ *
+ * On transmit channels, 60 dB is defined as approximately 0 dBm. Note that
+ * this is not a calibrated value, and the actual output power will vary based
+ * on a multitude of factors.
+ *
+ * @todo The gain ranges are not shifted to account for external accessories,
+ * such as amplifiers and LNAs.
+ *
+ * @note Values outside the valid gain range will be clamped.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] gain Desired gain, in dB
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_gain(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain gain);
+
+/**
+ * Get overall system gain
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] gain Gain, in dB
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_gain(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain *gain);
+
+/**
+ * Set gain control mode
+ *
+ * Sets the mode for hardware AGC. Not all channels or boards will support
+ * all possible values (e.g. transmit channels); invalid combinations will
+ * return ::BLADERF_ERR_UNSUPPORTED.
+ *
+ * The special value of ::BLADERF_GAIN_DEFAULT will return hardware AGC to
+ * its default value at initialization.
+ *
+ * @see bladerf_gain_mode for implementation guidance
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] mode Desired gain mode
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_gain_mode(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode mode);
+
+/**
+ * Get gain control mode
+ *
+ * Gets the current mode for hardware AGC. If the channel or board does not
+ * meaningfully have a gain mode (e.g. transmit channels), mode will be
+ * set to ::BLADERF_GAIN_DEFAULT and `0` will be returned.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] mode Gain mode
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_gain_mode(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode *mode);
+
+/**
+ * Get available gain control modes
+ *
+ * Populates `modes` with a pointer to an array of structs containing the
+ * supported gain modes.
+ *
+ * This function may be called with `NULL` for `modes` to determine the number
+ * of gain modes supported.
+ *
+ * @see bladerf_gain_mode for implementation guidance
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] modes Supported gain modes
+ *
+ * @return Number of gain modes on success, value from \ref RETCODES list on
+ * failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_gain_modes(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_gain_modes **modes);
+
+/**
+ * Get range of overall system gain
+ *
+ * @note This may vary depending on the configured frequency, so it should be
+ * checked after setting the desired frequency.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] range Gain range
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_gain_range(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range);
+
+/**
+ * Set the gain for a specific gain stage
+ *
+ * @note Values outside the valid gain range will be clipped.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] stage Gain stage name
+ * @param[in] gain Desired gain
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *stage,
+ bladerf_gain gain);
+
+/**
+ * Set the gain for a specific gain stage
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] stage Gain stage name
+ * @param[out] gain Gain
+ *
+ * Note that, in some cases, gain may be negative (e.g. transmit channels).
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *stage,
+ bladerf_gain *gain);
+
+/**
+ * Get gain range of a specific gain stage
+ *
+ * @note This may vary depending on the configured frequency, so it should be
+ * checked after setting the desired frequency.
+ *
+ * This function may be called with `NULL` for `range` to test if a given gain
+ * range exists.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] stage Gain stage name
+ * @param[out] range Gain range
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_gain_stage_range(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *stage,
+ const struct bladerf_range **range);
+
+/**
+ * Get a list of available gain stages
+ *
+ * This function may be called with `NULL` for `stages`, or 0 for `count`, to
+ * determine the number of gain stages.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] stages Gain stage names
+ * @param[out] count Number to populate
+ *
+ * @return Number of gain stages on success, value from \ref RETCODES list on
+ * failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_gain_stages(struct bladerf *dev,
+ bladerf_channel ch,
+ const char **stages,
+ size_t count);
+
+/** @} (End of FN_GAIN) */
+
+/**
+ * @defgroup FN_SAMPLING Sample rate
+ *
+ * This section presents functionality pertaining to configuring the
+ * sample rate and mode of the device's RX and TX channels.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * Sample rate, in samples per second (sps)
+ */
+typedef unsigned int bladerf_sample_rate;
+
+/**
+ * Rational sample rate representation
+ *
+ * Sample rates are in the form of
+ * @f[
+ * rate = integer + \frac{num}{den}
+ * @f]
+ */
+struct bladerf_rational_rate {
+ uint64_t integer; /**< Integer portion */
+ uint64_t num; /**< Numerator in fractional portion */
+ uint64_t den; /**< Denominator in fractional portion. This must be
+ * greater than 0. */
+};
+
+/**
+ * Configure the channel's sample rate to the specified rate in Hz.
+ *
+ * @note This requires the sample rate is an integer value of Hz. Use
+ * bladerf_set_rational_sample_rate() for more arbitrary values.
+ *
+ * @see Use bladerf_get_sample_rate_range() to determine the range of supported
+ * sample rates.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] rate Sample rate
+ * @param[out] actual If non-NULL, this is written with the actual
+ * sample rate achieved.
+ *
+ * @return 0 on success, value from \ref RETCODES list upon failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate rate,
+ bladerf_sample_rate *actual);
+
+/**
+ * Configure the channel's sample rate as a rational fraction of Hz.
+ *
+ * @see Use bladerf_get_sample_rate_range() to determine the range of supported
+ * sample rates.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel to change
+ * @param[in] rate Rational sample rate
+ * @param[out] actual If non-NULL, this is written with the actual
+ * rational sample rate achieved.
+ *
+ * @return 0 on success, value from \ref RETCODES list upon failure
+ */
+API_EXPORT
+int CALL_CONV
+ bladerf_set_rational_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_rational_rate *rate,
+ struct bladerf_rational_rate *actual);
+/**
+ * Get the channel's current sample rate in Hz
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] rate Current sample rate
+ *
+ * @return 0 on success, value from \ref RETCODES list upon failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate *rate);
+
+/**
+ * Get the channel's supported range of sample rates
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] range Sample rate range
+ *
+ * @return 0 on success, value from \ref RETCODES list upon failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_sample_rate_range(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range);
+
+/**
+ * Get the channel's sample rate in rational Hz
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] rate Current rational sample rate
+ *
+ * @return 0 on success, value from \ref RETCODES list upon failure
+ */
+API_EXPORT
+int CALL_CONV
+ bladerf_get_rational_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_rational_rate *rate);
+
+/** @} (End of FN_SAMPLING) */
+
+/**
+ * @defgroup FN_BANDWIDTH Bandwidth
+ *
+ * This section defines functionality for configuring a channel's bandwidth. In
+ * most cases, one should define the bandwidth to be less than the sample rate
+ * to minimize the impact of aliasing.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * Bandwidth, in hertz (Hz)
+ */
+typedef unsigned int bladerf_bandwidth;
+
+/**
+ * Set the bandwidth of the channel to the specified value in Hz
+ *
+ * The underlying device is capable of a discrete set of bandwidth values. The
+ * caller should check the `actual` parameter to determine which of these
+ * discrete bandwidth values is actually used for the requested bandwidth.
+ *
+ * @see Use bladerf_get_bandwidth_range() to determine the range of supported
+ * bandwidths.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] bandwidth Desired bandwidth
+ * @param[out] actual If non-NULL, written with the actual bandwidth that
+ * the device was able to achieve.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth bandwidth,
+ bladerf_bandwidth *actual);
+
+/**
+ * Get the bandwidth of the channel
+ *
+ * @param dev Device Handle
+ * @param[in] ch Channel
+ * @param[out] bandwidth Actual bandwidth in Hz
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth *bandwidth);
+
+/**
+ * Get the supported range of bandwidths for a channel
+ *
+ * @param dev Device Handle
+ * @param[in] ch Channel
+ * @param[out] range Bandwidth range
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_bandwidth_range(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range);
+
+/** @} (End of FN_BANDWIDTH) */
+
+/**
+ * @defgroup FN_TUNING Frequency
+ *
+ * These functions provide the ability to tune the RX and TX channels.
+ *
+ * See \link tuning.html this page\endlink for more detailed information about
+ * how the API performs this tuning, and for example code snippets.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * RF center frequency, in hertz (Hz)
+ *
+ * @see Format macros for fprintf() and fscanf(): `BLADERF_PRIuFREQ`,
+ * `BLADERF_PRIxFREQ`, `BLADERF_SCNuFREQ`, `BLADERF_SCNxFREQ`
+ *
+ * @remark Prior to libbladeRF 2.0.0, frequencies were specified as
+ * `unsigned int`.
+ */
+typedef uint64_t bladerf_frequency;
+
+/** printf format for frequencies in unsigned decimal */
+#define BLADERF_PRIuFREQ PRIu64
+/** printf format for frequencies in hexadecimal */
+#define BLADERF_PRIxFREQ PRIx64
+/** scanf format for frequencies in unsigned decimal */
+#define BLADERF_SCNuFREQ SCNu64
+/** scanf format for frequencies in hexadecimal */
+#define BLADERF_SCNxFREQ SCNx64
+
+/**
+ * Select the appropriate band path given a frequency in Hz.
+ *
+ * @note Most API users will not need to use this function, as
+ * bladerf_set_frequency() calls this internally after tuning the device.
+ *
+ * The high band is used for `frequency` above 1.5 GHz on bladeRF1 and above
+ * 3.0 GHz on bladeRF2. Otherwise, the low band is used.
+ *
+ * @see bladerf_get_frequency_range() to determine the range of supported
+ * frequencies.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] frequency Tuned frequency
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_select_band(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency);
+
+/**
+ * Set channel's frequency in Hz.
+ *
+ * @note On the bladeRF1 platform, it is recommended to keep the RX and TX
+ * frequencies at least 1 MHz apart, and to digitally mix on the RX side
+ * if reception closer to the TX frequency is required.
+ *
+ * @note On the bladeRF2, there is one oscillator for all RX channels and one
+ * oscillator for all TX channels. Therefore, changing one channel will
+ * change the frequency of all channels in that direction.
+ *
+ * This function calls bladerf_select_band() internally, and performs all
+ * other tasks required to prepare the channel for the given frequency.
+ *
+ * @see bladerf_get_frequency_range() to determine the range of supported
+ * frequencies.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] frequency Desired frequency
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency);
+/**
+ * Get channel's current frequency in Hz
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] frequency Current frequency
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency *frequency);
+
+/**
+ * Get the supported range of frequencies for a channel
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] range Frequency range
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_frequency_range(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range);
+
+/** @} (End of FN_TUNING) */
+
+/**
+ * @defgroup FN_LOOPBACK Internal loopback
+ *
+ * The bladeRF provides a variety of loopback modes to aid in development and
+ * testing.
+ *
+ * In general, the digital or baseband loopback modes provide the most "ideal"
+ * operating conditions, while the internal RF loopback modes introduce more of
+ * the typical nonidealities of analog systems.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * Loopback options
+ */
+typedef enum {
+ /** Disables loopback and returns to normal operation. */
+ BLADERF_LB_NONE = 0,
+
+ /** Firmware loopback inside of the FX3 */
+ BLADERF_LB_FIRMWARE,
+
+ /** Baseband loopback. TXLPF output is connected to the RXVGA2 input. */
+ BLADERF_LB_BB_TXLPF_RXVGA2,
+
+ /** Baseband loopback. TXVGA1 output is connected to the RXVGA2 input. */
+ BLADERF_LB_BB_TXVGA1_RXVGA2,
+
+ /** Baseband loopback. TXLPF output is connected to the RXLPF input. */
+ BLADERF_LB_BB_TXLPF_RXLPF,
+
+ /** Baseband loopback. TXVGA1 output is connected to RXLPF input. */
+ BLADERF_LB_BB_TXVGA1_RXLPF,
+
+ /**
+ * RF loopback. The TXMIX output, through the AUX PA, is connected to the
+ * output of LNA1.
+ */
+ BLADERF_LB_RF_LNA1,
+
+ /**
+ * RF loopback. The TXMIX output, through the AUX PA, is connected to the
+ * output of LNA2.
+ */
+ BLADERF_LB_RF_LNA2,
+
+ /**
+ * RF loopback. The TXMIX output, through the AUX PA, is connected to the
+ * output of LNA3.
+ */
+ BLADERF_LB_RF_LNA3,
+
+ /** RFIC digital loopback (built-in self-test) */
+ BLADERF_LB_RFIC_BIST,
+} bladerf_loopback;
+
+/**
+ * Mapping of human-readable names to loopback modes
+ */
+struct bladerf_loopback_modes {
+ const char *name; /**< Name of loopback mode */
+ bladerf_loopback mode; /**< Loopback mode enumeration */
+};
+
+/**
+ * Get loopback modes
+ *
+ * Populates `modes` with a pointer to an array of structs containing the
+ * supported loopback modes.
+ *
+ * This function may be called with `NULL` for `modes` to determine the number
+ * of loopback modes supported.
+ *
+ * @param dev Device handle
+ * @param[out] modes Supported loopback modes
+ *
+ * @return Number of loopback modes on success, value from \ref RETCODES list
+ * on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_loopback_modes(
+ struct bladerf *dev, const struct bladerf_loopback_modes **modes);
+
+/**
+ * Test if a given loopback mode is supported on this device.
+ *
+ * @param dev Device handle
+ * @param[in] mode bladerf_loopback enum to check
+ *
+ * @return true if supported, false if not (or on error)
+ */
+API_EXPORT bool CALL_CONV bladerf_is_loopback_mode_supported(
+ struct bladerf *dev, bladerf_loopback mode);
+
+/**
+ * Apply specified loopback mode
+ *
+ * @note Loopback modes should only be enabled or disabled while the RX and TX
+ * channels are both disabled (and therefore, when no samples are being
+ * actively streamed). Otherwise, unexpected behavior may occur.
+ *
+ * @param dev Device handle
+ * @param[in] lb Loopback mode. Note that BLADERF_LB_NONE disables the
+ * use of loopback functionality.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_loopback(struct bladerf *dev, bladerf_loopback lb);
+
+/**
+ * Get current loopback mode
+ *
+ * @param dev Device handle
+ * @param[out] lb Current loopback mode
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_loopback(struct bladerf *dev, bladerf_loopback *lb);
+
+/** @} (End of FN_LOOPBACK) */
+
+/**
+ * @defgroup FN_TRIG Triggers
+ *
+ * Trigger functionality introduced in bladeRF FPGA v0.6.0 allows TX and/or RX
+ * samples to be gated via a trigger signal. This allows multiple devices to
+ * synchronize their TX/RX operations upon the reception of a trigger event.
+ *
+ * The set of functions presented in this section of the API provides control
+ * over this triggering functionality. It is intended that these functions be
+ * used \b prior to starting sample streams. Attempting to use these functions
+ * while streaming may yield undefined and undesirable behavior.
+ *
+ * These functions are thread-safe.
+ *
+ * For devices running at the same sample rate, the trigger event should
+ * achieve synchronization within +/- 1 sample on each device in the chain.
+ *
+ * @note As of FPGA v0.6.0, `mini_exp[1]` has been allocated as the trigger
+ * signal. However, this API section is designed to allow future signals
+ * to be added, including users' software and hardware customizations.
+ *
+ * @note <b>Important</b>: Ensure that you disarm triggers <b>before</b>
+ * stopping sample streams (i.e., calling bladerf_enable_module() with
+ * `enable = false`). Otherwise, the operation of shutting down streams
+ * will block for the entire duration of the stream timeout (or infinitely
+ * if the timeouts were set to 0).
+ *
+ * These functions are thread-safe.
+ *
+ * The standard usage of these functions is shown below. This example assumes:
+ *
+ * - The two devices are connected such they share a common ground and their
+ * `mini_exp[1]` pins are connected. `mini_exp[1]` is J71-4 on bladeRF
+ * x40/x115, and J51-1 on bladeRF xA4/xA5/xA9.
+ *
+ * - Both devices are already configured to utilize a common clock signal via
+ * the external SMB connection. Generally, this will consist of one device
+ * to outputting its reference clock via the SMB clock port, and
+ * configuring the other device(s) to use the SMB clock port as a reference
+ * clock input. This may be achieved using the bladerf_set_smb_mode()
+ * function, found in the \ref FN_SMB_CLOCK section.
+ *
+ *
+ * @code{.c}
+ *
+ * int status;
+ * bladerf_channel channel = BLADERF_CHANNEL_RX(0);
+ * bladerf_trigger_signal signal = BLADERF_TRIGGER_J71_4;
+ *
+ * // Allocate and initialize a bladerf_trigger structure for each
+ * // trigger in the system.
+ * struct bladerf_trigger trig_master, trig_slave;
+ *
+ * status = bladerf_trigger_init(dev_master, channel, signal, &trig_master);
+ * if (status == 0) {
+ * trig_master.role = BLADERF_TRIGGER_ROLE_MASTER;
+ * } else {
+ * goto handle_error;
+ * }
+ *
+ * status = bladerf_trigger_init(dev_slave1, channel, signal, &trig_slave);
+ * if (status == 0) {
+ * master_rx.role = BLADERF_TRIGGER_ROLE_SLAVE;
+ * } else {
+ * goto handle_error;
+ * }
+ *
+ * // Arm the triggering functionality on each device
+ * status = bladerf_trigger_arm(dev_master, &trig_master, true, 0, 0);
+ * if (status != 0) {
+ * goto handle_error;
+ * }
+ *
+ * status = bladerf_trigger_arm(dev_slave, &trig_slave, true, 0, 0);
+ * if (status != 0) {
+ * goto handle_error;
+ * }
+ *
+ * // Call bladerf_sync_config() and bladerf_sync_rx() on each device.
+ * // Ensure the timeout parameters used are long enough to accommodate
+ * // the expected time until the trigger will be fired.
+ * status = start_rx_streams(dev_master, dev_slave);
+ * if (status != 0) {
+ * goto handle_error;
+ * }
+ *
+ * // Fire the trigger signal
+ * status = bladerf_trigger_fire(dev_master, &trig_master);
+ * if (status != 0) {
+ * goto handle_error;
+ * }
+ *
+ * // Handle RX signals and then shut down streams.
+ * // Synchronized samples should now be reaching the host following the
+ * // reception of the external trigger signal.
+ * status = handle_rx_operations(dev_master, dev_slave);
+ * if (status != 0) {
+ * goto handle_error;
+ * }
+ *
+ * // Disable triggering on all devices to restore normal RX operation
+ * trig_master.role = BLADERF_TRIGGER_ROLE_DISABLED;
+ * status = bladerf_trigger_arm(dev_master, &trig_master, false, 0, 0);
+ * if (status != 0) {
+ * goto handle_error;
+ * }
+ *
+ * trig_slave.role = BLADERF_TRIGGER_ROLE_DISABLED;
+ * status = bladerf_trigger_arm(dev_master, &trig_slave, false, 0, 0);
+ * if (status != 0) {
+ * goto handle_error;
+ * }
+ *
+ * @endcode
+ *
+ * @{
+ */
+
+/**
+ * This value denotes the role of a device in a trigger chain.
+ */
+typedef enum {
+ /** Invalid role selection */
+ BLADERF_TRIGGER_ROLE_INVALID = -1,
+
+ /**
+ * Triggering functionality is disabled on this device. Samples are not
+ * gated and the trigger signal is an input.
+ */
+ BLADERF_TRIGGER_ROLE_DISABLED,
+
+ /**
+ * This device is the trigger master. Its trigger signal will be an output
+ * and this device will determine when all devices shall trigger.
+ */
+ BLADERF_TRIGGER_ROLE_MASTER,
+
+ /**
+ * This device is the trigger slave. This device's trigger signal will be an
+ * input and this devices will wait for the master's trigger signal
+ * assertion.
+ */
+ BLADERF_TRIGGER_ROLE_SLAVE,
+} bladerf_trigger_role;
+
+/**
+ * Trigger signal selection
+ *
+ * This selects pin or signal used for the trigger.
+ *
+ * @note ::BLADERF_TRIGGER_J71_4, ::BLADERF_TRIGGER_J51_1, and
+ * ::BLADERF_TRIGGER_MINI_EXP_1 are the only valid options as of FPGA
+ * v0.6.0. All three values have the same behavior and may be used
+ * interchangably.
+ *
+ * The `BLADERF_TRIGGER_USER_*` values have been added to allow users to modify
+ * both hardware and software implementations to add custom triggers, while
+ * maintaining libbladeRF API compatibility. Official bladeRF releases will
+ * not utilize these user signal IDs.
+ */
+typedef enum {
+ BLADERF_TRIGGER_INVALID = -1, /**< Invalid selection */
+ BLADERF_TRIGGER_J71_4, /**< J71 pin 4, mini_exp[1] on x40/x115 */
+ BLADERF_TRIGGER_J51_1, /**< J51 pin 1, mini_exp[1] on xA4/xA5/xA9 */
+ BLADERF_TRIGGER_MINI_EXP_1, /**< mini_exp[1], hardware-independent */
+
+ BLADERF_TRIGGER_USER_0 = 128, /**< Reserved for user SW/HW customizations */
+ BLADERF_TRIGGER_USER_1, /**< Reserved for user SW/HW customizations */
+ BLADERF_TRIGGER_USER_2, /**< Reserved for user SW/HW customizations */
+ BLADERF_TRIGGER_USER_3, /**< Reserved for user SW/HW customizations */
+ BLADERF_TRIGGER_USER_4, /**< Reserved for user SW/HW customizations */
+ BLADERF_TRIGGER_USER_5, /**< Reserved for user SW/HW customizations */
+ BLADERF_TRIGGER_USER_6, /**< Reserved for user SW/HW customizations */
+ BLADERF_TRIGGER_USER_7, /**< Reserved for user SW/HW customizations */
+} bladerf_trigger_signal;
+
+/**
+ * Trigger configuration
+ *
+ * It is <b>highly recommended</b> to keep a 1:1 relationship between triggers
+ * in the physical setup and instances of this structure. (i.e., do not re-use
+ * and change the same bladerf_trigger) for multiple triggers.)
+ */
+struct bladerf_trigger {
+ bladerf_channel channel; /**< RX/TX channel associated with trigger */
+ bladerf_trigger_role role; /**< Role of the device in a trigger chain */
+ bladerf_trigger_signal signal; /**< Pin or signal being used */
+ uint64_t options; /**< Reserved field for future options. This
+ * is unused and should be set to 0. */
+};
+
+/**
+ * Initialize a bladerf_trigger structure based upon the current configuration
+ * of the specified trigger signal.
+ *
+ * While it is possible to simply declare and manually fill in a bladerf_trigger
+ * structure, it is recommended to use this function to retrieve the current
+ * `role` and `options` values.
+ *
+ * @param dev Device to query
+ * @param[in] ch Channel
+ * @param[in] signal Trigger signal to query
+ * @param[out] trigger Updated to describe trigger
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_trigger_init(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal signal,
+ struct bladerf_trigger *trigger);
+
+/**
+ * Configure and (dis)arm a trigger on the specified device.
+ *
+ * @note If trigger->role is set to ::BLADERF_TRIGGER_ROLE_DISABLED, this will
+ * inherently disarm an armed trigger and clear any fire requests,
+ * regardless of the value of `arm`.
+ *
+ * @param dev Device to configure
+ * @param[in] trigger Trigger configure
+ * @param[in] arm (Re)Arm trigger if true, disarm if false
+ * @param[in] resv1 Reserved for future use. Set to 0.
+ * @param[in] resv2 Reserved for future use. Set to 0.
+ *
+ * @warning Configuring two devices in the trigger chain (or both RX and TX on a
+ * single device) as masters can damage the associated FPGA pins, as
+ * this would cause contention over the trigger signal. <b>Ensure only
+ * one device in the chain is configured as the master!</b>
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_trigger_arm(struct bladerf *dev,
+ const struct bladerf_trigger *trigger,
+ bool arm,
+ uint64_t resv1,
+ uint64_t resv2);
+
+/**
+ * Fire a trigger event.
+ *
+ * Calling this functiona with a trigger whose role is anything other than
+ * ::BLADERF_TRIGGER_REG_MASTER will yield a BLADERF_ERR_INVAL return value.
+ *
+ * @param dev Device handle
+ * @param[in] trigger Trigger to assert
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_trigger_fire(struct bladerf *dev,
+ const struct bladerf_trigger *trigger);
+
+/**
+ * Query the fire request status of a master trigger
+ *
+ * @param dev Device handle
+ * @param[in] trigger Trigger to query
+ * @param[out] is_armed Set to true if the trigger is armed, and false
+ * otherwise. May be NULL.
+ * @param[out] has_fired Set to true if the trigger has fired, and false
+ * otherwise. May be NULL.
+ * @param[out] fire_requested Only applicable to a trigger master.
+ * Set to true if a fire request has been
+ * previously submitted. May be NULL.
+ * @param[out] resv1 Reserved for future use.
+ * This field is written as 0 if not set to NULL.
+ * @param[out] resv2 Reserved for future use.
+ * This field is written as 0 if not set to NULL.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_trigger_state(struct bladerf *dev,
+ const struct bladerf_trigger *trigger,
+ bool *is_armed,
+ bool *has_fired,
+ bool *fire_requested,
+ uint64_t *resv1,
+ uint64_t *resv2);
+
+/** @} (End of FN_TRIG) */
+
+/**
+ * @defgroup FN_RECEIVE_MUX Receive Mux
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * RX Mux modes
+ *
+ * These values describe the source of samples to the RX FIFOs in the FPGA.
+ * They map directly to rx_mux_mode_t inside the FPGA's source code.
+ */
+typedef enum {
+ /** Invalid RX Mux mode selection */
+ BLADERF_RX_MUX_INVALID = -1,
+
+ /** Read baseband samples. This is the default mode of operation. */
+ BLADERF_RX_MUX_BASEBAND = 0x0,
+
+ /**
+ * Read samples from 12 bit counters.
+ *
+ * The I channel counts up while the Q channel counts down.
+ */
+ BLADERF_RX_MUX_12BIT_COUNTER = 0x1,
+
+ /**
+ * Read samples from a 32 bit up-counter.
+ *
+ * I and Q form a little-endian value.
+ */
+ BLADERF_RX_MUX_32BIT_COUNTER = 0x2,
+
+ /* RX_MUX setting 0x3 is reserved for future use */
+
+ /** Read samples from the baseband TX input to the FPGA (from the host) */
+ BLADERF_RX_MUX_DIGITAL_LOOPBACK = 0x4,
+} bladerf_rx_mux;
+
+/** @cond IGNORE */
+/* Backwards compatible mapping for `bladerf_rx_mux`. */
+#define BLADERF_RX_MUX_BASEBAND_LMS BLADERF_RX_MUX_BASEBAND
+/** @endcond */
+
+/**
+ * Set the current RX Mux mode
+ *
+ * @param dev Device handle
+ * @param[in] mux Mux mode.
+ *
+ * @returns 0 on success, value from \ref RETCODES list on failure.
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_rx_mux(struct bladerf *dev, bladerf_rx_mux mux);
+
+/**
+ * Gets the current RX Mux mode
+ *
+ * @param dev Device handle
+ * @param[out] mode Current RX Mux mode
+ *
+ * @returns 0 on success, value from \ref RETCODES list on failure.
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_rx_mux(struct bladerf *dev, bladerf_rx_mux *mode);
+
+/** @} (End of FN_RECEIVE_MUX) */
+
+/**
+ * @defgroup FN_SCHEDULED_TUNING Scheduled Tuning
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * @ingroup STREAMING
+ *
+ * Timestamp, in ticks
+ *
+ * A channel's timestamp typically increments at the sample rate.
+ *
+ * @see Format macros for fprintf() and fscanf(): `BLADERF_PRIuTS`,
+ * `BLADERF_PRIxTS`, `BLADERF_SCNuTS`, `BLADERF_SCNxTS`
+ */
+typedef uint64_t bladerf_timestamp;
+
+/**
+ * Specifies that scheduled retune should occur immediately when using
+ * bladerf_schedule_retune().
+ */
+#define BLADERF_RETUNE_NOW (bladerf_timestamp)0
+
+/**
+ * Quick Re-tune parameters.
+ *
+ * @note These parameters, which are associated with the RFIC's register values,
+ * are sensitive to changes in the operating environment (e.g.,
+ * temperature).
+ *
+ * This structure should be filled in via bladerf_get_quick_tune().
+ */
+struct bladerf_quick_tune {
+ union {
+ /* bladeRF1 quick tune parameters */
+ struct {
+ uint8_t freqsel; /**< Choice of VCO and VCO division factor */
+ uint8_t vcocap; /**< VCOCAP value */
+ uint16_t nint; /**< Integer portion of LO frequency value */
+ uint32_t nfrac; /**< Fractional portion of LO frequency value */
+ uint8_t flags; /**< Flag bits used internally by libbladeRF */
+ uint8_t xb_gpio; /**< Flag bits used to configure XB */
+ };
+ /* bladeRF2 quick tune parameters */
+ struct {
+ uint16_t nios_profile; /**< Profile number in Nios */
+ uint8_t rffe_profile; /**< Profile number in RFFE */
+ uint8_t port; /**< RFFE port settings */
+ uint8_t spdt; /**< External SPDT settings */
+ };
+ };
+};
+
+/**
+ * Schedule a frequency retune to occur at specified sample timestamp value.
+ *
+ * @pre bladerf_sync_config() must have been called with the
+ * \ref BLADERF_FORMAT_SC16_Q11_META format for the associated channel in
+ * order to enable timestamps. (The timestamped metadata format must be
+ * enabled in order to use this function.)
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] timestamp Channel's sample timestamp to perform the
+ * retune operation. If this value is in the past,
+ * the retune will occur immediately. To perform
+ * the retune immediately, specify
+ * ::BLADERF_RETUNE_NOW.
+ * @param[in] frequency Desired frequency, in Hz.
+ * @param[in] quick_tune If non-NULL, the provided "quick retune" values
+ * will be applied to the transceiver to tune it
+ * according to a previous state retrieved via
+ * bladerf_get_quick_tune().
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure.
+ *
+ * @note If the underlying queue of scheduled retune requests becomes full, \ref
+ * BLADERF_ERR_QUEUE_FULL will be returned. In this case, it should be
+ * possible to schedule a retune after the timestamp of one of the earlier
+ * requests occurs.
+
+ * @note NULL quick_tune parameters are not supported by the bladeRF 2.0 micro.
+ */
+API_EXPORT
+int CALL_CONV bladerf_schedule_retune(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_timestamp timestamp,
+ bladerf_frequency frequency,
+ struct bladerf_quick_tune *quick_tune);
+
+/**
+ * Cancel all pending scheduled retune operations for the specified channel.
+ *
+ * This will be done automatically during bladerf_close() to ensure that
+ * previously queued retunes do not continue to occur after closing and then
+ * later re-opening a device.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure.
+ */
+API_EXPORT
+int CALL_CONV bladerf_cancel_scheduled_retunes(struct bladerf *dev,
+ bladerf_channel ch);
+
+/**
+ * Fetch parameters used to tune the transceiver to the current frequency for
+ * use with bladerf_schedule_retune() to perform a "quick retune."
+ *
+ * This allows for a faster retune, with a potential trade off of increased
+ * phase noise.
+ *
+ * @note These parameters are sensitive to changes in the operating environment,
+ * and should be "refreshed" if planning to use the "quick retune"
+ * functionality over a long period of time.
+ *
+ * @pre bladerf_set_frequency() or bladerf_schedule_retune() have previously
+ * been used to retune to the desired frequency.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] quick_tune Quick retune parameters
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_quick_tune(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_quick_tune *quick_tune);
+
+/**
+ * @brief Prints the quick retune parameters for a bladeRF device.
+ *
+ * Outputs the current quick retune parameters of a bladeRF device
+ * to standard output. It is useful for debugging and verifying the
+ * configuration of quick tune settings.
+ *
+ * @note supported devices: bladeRF1, bladeRF2
+ *
+ * @param[in] dev Device handle. Must not be NULL.
+ * @param[in] qt Pointer to the `bladerf_quick_tune` structure containing
+ * the quick retune parameters. Must not be NULL.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int bladerf_print_quick_tune(struct bladerf *dev,
+ const struct bladerf_quick_tune *qt);
+
+/** @} (End of FN_SCHEDULED_TUNING) */
+
+/**
+ * @defgroup FN_CORR Correction
+ *
+ * This group provides routines for applying manual offset, gain, and phase
+ * corrections.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * Correction value, in arbitrary units
+ *
+ * @see ::bladerf_correction
+ * @see bladerf_get_correction()
+ * @see bladerf_set_correction()
+ */
+typedef int16_t bladerf_correction_value;
+
+/**
+ * Correction parameter selection
+ *
+ * These values specify the correction parameter to modify or query when calling
+ * bladerf_set_correction() or bladerf_get_correction(). Note that the meaning
+ * of the `value` parameter to these functions depends upon the correction
+ * parameter.
+ *
+ */
+typedef enum {
+ /**
+ * Adjusts the in-phase DC offset. Valid values are [-2048, 2048], which are
+ * scaled to the available control bits.
+ */
+ BLADERF_CORR_DCOFF_I,
+
+ /**
+ * Adjusts the quadrature DC offset. Valid values are [-2048, 2048], which
+ * are scaled to the available control bits.
+ */
+ BLADERF_CORR_DCOFF_Q,
+
+ /**
+ * Adjusts phase correction of [-10, 10] degrees, via a provided count value
+ * of [-4096, 4096].
+ */
+ BLADERF_CORR_PHASE,
+
+ /**
+ * Adjusts gain correction value in [-1.0, 1.0], via provided values in the
+ * range of [-4096, 4096].
+ */
+ BLADERF_CORR_GAIN
+} bladerf_correction;
+
+/** @cond IGNORE */
+/* Backwards compatible mapping to `bladerf_correction`. */
+#define BLADERF_CORR_LMS_DCOFF_I BLADERF_CORR_DCOFF_I
+#define BLADERF_CORR_LMS_DCOFF_Q BLADERF_CORR_DCOFF_Q
+#define BLADERF_CORR_FPGA_PHASE BLADERF_CORR_PHASE
+#define BLADERF_CORR_FPGA_GAIN BLADERF_CORR_GAIN
+/** @endcond */
+
+/**
+ * Set the value of the specified configuration parameter
+ *
+ * @see The ::bladerf_correction description for the valid ranges of the `value`
+ * parameter.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] corr Correction type
+ * @param[in] value Value to apply
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_correction corr,
+ bladerf_correction_value value);
+
+/**
+ * Obtain the current value of the specified configuration parameter
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] corr Correction type
+ * @param[out] value Current value
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_correction corr,
+ bladerf_correction_value *value);
+
+/** @} (End of FN_CORR) */
+
+/** @} (End of FN_CHANNEL) */
+
+/**
+ * @defgroup STREAMING Streaming
+ *
+ * This section defines the streaming APIs.
+ *
+ * @{
+ */
+
+/** printf format for timestamps in unsigned decimal */
+#define BLADERF_PRIuTS PRIu64
+/** printf format for timestamps in hexadecimal */
+#define BLADERF_PRIxTS PRIx64
+/** scanf format for timestamps in unsigned decimal */
+#define BLADERF_SCNuTS SCNu64
+/** scanf format for timestamps in hexadecimal */
+#define BLADERF_SCNxTS SCNx64
+
+/**
+ * @defgroup STREAMING_FORMAT Formats
+ *
+ * This section defines the available sample formats and metadata flags.
+ *
+ * @{
+ */
+
+/**
+ * Sample format
+ */
+typedef enum {
+ /**
+ * Signed, Complex 16-bit Q11. This is the native format of the DAC data.
+ *
+ * Values in the range [-2048, 2048) are used to represent [-1.0, 1.0).
+ * Note that the lower bound here is inclusive, and the upper bound is
+ * exclusive. Ensure that provided samples stay within [-2048, 2047].
+ *
+ * Samples consist of interleaved IQ value pairs, with I being the first
+ * value in the pair. Each value in the pair is a right-aligned,
+ * little-endian int16_t. The FPGA ensures that these values are
+ * sign-extended.
+ *
+ * <pre>
+ * .--------------.--------------.
+ * | Bits 31...16 | Bits 15...0 |
+ * +--------------+--------------+
+ * | Q[15..0] | I[15..0] |
+ * `--------------`--------------`
+ * </pre>
+ *
+ * When using this format the minimum required buffer size, in bytes, is:
+ *
+ * \f$
+ * buffer\_size\_min = (2 \times num\_samples \times num\_channels \times
+ * sizeof(int16\_t))
+ * \f$
+ *
+ * For example, to hold 2048 samples for one channel, a buffer must be at
+ * least 8192 bytes large.
+ *
+ * When a multi-channel ::bladerf_channel_layout is selected, samples
+ * will be interleaved per channel. For example, with ::BLADERF_RX_X2
+ * or ::BLADERF_TX_X2 (x2 MIMO), the buffer is structured like:
+ *
+ * <pre>
+ * .-------------.--------------.--------------.------------------.
+ * | Byte offset | Bits 31...16 | Bits 15...0 | Description |
+ * +-------------+--------------+--------------+------------------+
+ * | 0x00 | Q0[0] | I0[0] | Ch 0, sample 0 |
+ * | 0x04 | Q1[0] | I1[0] | Ch 1, sample 0 |
+ * | 0x08 | Q0[1] | I0[1] | Ch 0, sample 1 |
+ * | 0x0c | Q1[1] | I1[1] | Ch 1, sample 1 |
+ * | ... | ... | ... | ... |
+ * | 0xxx | Q0[n] | I0[n] | Ch 0, sample n |
+ * | 0xxx | Q1[n] | I1[n] | Ch 1, sample n |
+ * `-------------`--------------`--------------`------------------`
+ * </pre>
+ *
+ * Per the `buffer_size_min` formula above, 2048 samples for two channels
+ * will generate 4096 total samples, and require at least 16384 bytes.
+ *
+ * Implementors may use the interleaved buffers directly, or may use
+ * bladerf_deinterleave_stream_buffer() / bladerf_interleave_stream_buffer()
+ * if contiguous blocks of samples are desired.
+ */
+ BLADERF_FORMAT_SC16_Q11,
+
+ /**
+ * This format is the same as the ::BLADERF_FORMAT_SC16_Q11 format, except
+ * the first 4 samples in every <i>block*</i> of samples are replaced with
+ * metadata organized as follows. All fields are little-endian byte order.
+ *
+ * <pre>
+ * .-------------.------------.----------------------------------.
+ * | Byte offset | Type | Description |
+ * +-------------+------------+----------------------------------+
+ * | 0x00 | uint16_t | Reserved |
+ * | 0x02 | uint8_t | Stream flags |
+ * | 0x03 | uint8_t | Meta version ID |
+ * | 0x04 | uint64_t | 64-bit Timestamp |
+ * | 0x0c | uint32_t | BLADERF_META_FLAG_* flags |
+ * | 0x10..end | | Payload |
+ * `-------------`------------`----------------------------------`
+ * </pre>
+ *
+ * For IQ sample meta mode, the Meta version ID and Stream flags should
+ * currently be set to values 0x00 and 0x00, respectively.
+ *
+ * <i>*</i>The number of samples in a <i>block</i> is dependent upon
+ * the USB speed being used:
+ * - USB 2.0 Hi-Speed: 256 samples
+ * - USB 3.0 SuperSpeed: 512 samples
+ *
+ * When using the bladerf_sync_rx() and bladerf_sync_tx() functions, the
+ * above details are entirely transparent; the caller need not be concerned
+ * with these details. These functions take care of packing/unpacking the
+ * metadata into/from the underlying stream and convey this information
+ * through the ::bladerf_metadata structure.
+ *
+ * However, when using the \ref FN_STREAMING_ASYNC interface, the user is
+ * responsible for manually packing/unpacking the above metadata into/from
+ * their samples.
+ *
+ * @see STREAMING_FORMAT_METADATA
+ * @see The `src/streaming/metadata.h` header in the libbladeRF codebase.
+ */
+ BLADERF_FORMAT_SC16_Q11_META,
+
+ /**
+ * This format is for exchanging packets containing digital payloads with
+ * the FPGA. A packet is generall a digital payload, that the FPGA then
+ * processes to either modulate, demodulate, filter, etc.
+ *
+ * All fields are little-endian byte order.
+ *
+ * <pre>
+ * .-------------.------------.----------------------------------.
+ * | Byte offset | Type | Description |
+ * +-------------+------------+----------------------------------+
+ * | 0x00 | uint16_t | Packet length (in 32bit DWORDs) |
+ * | 0x02 | uint8_t | Packet flags |
+ * | 0x03 | uint8_t | Packet core ID |
+ * | 0x04 | uint64_t | 64-bit Timestamp |
+ * | 0x0c | uint32_t | BLADERF_META_FLAG_* flags |
+ * | 0x10..end | | Payload |
+ * `-------------`------------`----------------------------------`
+ * </pre>
+ *
+ * A target core (for example a modem) must be specified when calling the
+ * bladerf_sync_rx() and bladerf_sync_tx() functions.
+ *
+ * When in packet mode, lengths for all functions and data formats are
+ * expressed in number of 32-bit DWORDs. As an example, a 12 byte packet
+ * is considered to be 3 32-bit DWORDs long.
+ *
+ * This packet format does not send or receive raw IQ samples. The digital
+ * payloads contain configurations, and digital payloads that are specific
+ * to the digital core to which they are addressed. It is the FPGA core
+ * that should generate, interpret, and process the digital payloads.
+ *
+ * With the exception of packet lenghts, no difference should exist between
+ * USB 2.0 Hi-Speed or USB 3.0 SuperSpeed for packets for this streaming
+ * format.
+ *
+ * @see STREAMING_FORMAT_METADATA
+ * @see The `src/streaming/metadata.h` header in the libbladeRF codebase.
+ */
+ BLADERF_FORMAT_PACKET_META,
+
+ /**
+ * Signed, Complex 8-bit Q8. This is the native format of the DAC data.
+ *
+ * Values in the range [-128, 128) are used to represent [-1.0, 1.0).
+ * Note that the lower bound here is inclusive, and the upper bound is
+ * exclusive. Ensure that provided samples stay within [-128, 127].
+ *
+ * Samples consist of interleaved IQ value pairs, with I being the first
+ * value in the pair. Each value in the pair is a right-aligned int8_t.
+ * The FPGA ensures that these values are sign-extended.
+ *
+ * <pre>
+ * .--------------.--------------.
+ * | Bits 15...8 | Bits 7...0 |
+ * +--------------+--------------+
+ * | Q[7..0] | I[7..0] |
+ * `--------------`--------------`
+ * </pre>
+ *
+ * When using this format the minimum required buffer size, in bytes, is:
+ *
+ * \f$
+ * buffer\_size\_min = (2 \times num\_samples \times num\_channels \times
+ * sizeof(int8\_t))
+ * \f$
+ *
+ * For example, to hold 2048 samples for one channel, a buffer must be at
+ * least 4096 bytes large.
+ *
+ * When a multi-channel ::bladerf_channel_layout is selected, samples
+ * will be interleaved per channel. For example, with ::BLADERF_RX_X2
+ * or ::BLADERF_TX_X2 (x2 MIMO), the buffer is structured like:
+ *
+ * <pre>
+ * .-------------.--------------.--------------.------------------.
+ * | Byte offset | Bits 15...8 | Bits 7...0 | Description |
+ * +-------------+--------------+--------------+------------------+
+ * | 0x00 | Q0[0] | I0[0] | Ch 0, sample 0 |
+ * | 0x02 | Q1[0] | I1[0] | Ch 1, sample 0 |
+ * | 0x04 | Q0[1] | I0[1] | Ch 0, sample 1 |
+ * | 0x06 | Q1[1] | I1[1] | Ch 1, sample 1 |
+ * | ... | ... | ... | ... |
+ * | 0xxx | Q0[n] | I0[n] | Ch 0, sample n |
+ * | 0xxx | Q1[n] | I1[n] | Ch 1, sample n |
+ * `-------------`--------------`--------------`------------------`
+ * </pre>
+ *
+ * Per the `buffer_size_min` formula above, 2048 samples for two channels
+ * will generate 4096 total samples, and require at least 8192 bytes.
+ *
+ * Implementors may use the interleaved buffers directly, or may use
+ * bladerf_deinterleave_stream_buffer() / bladerf_interleave_stream_buffer()
+ * if contiguous blocks of samples are desired.
+ */
+ BLADERF_FORMAT_SC8_Q7,
+
+ /**
+ * This format is the same as the ::BLADERF_FORMAT_SC8_Q7 format, except
+ * the first 4 samples in every <i>block*</i> of samples are replaced with
+ * metadata organized as follows. All fields are little-endian byte order.
+ *
+ * <pre>
+ * .-------------.------------.----------------------------------.
+ * | Byte offset | Type | Description |
+ * +-------------+------------+----------------------------------+
+ * | 0x00 | uint16_t | Reserved |
+ * | 0x02 | uint8_t | Stream flags |
+ * | 0x03 | uint8_t | Meta version ID |
+ * | 0x04 | uint64_t | 64-bit Timestamp |
+ * | 0x0c | uint32_t | BLADERF_META_FLAG_* flags |
+ * | 0x10..end | | Payload |
+ * `-------------`------------`----------------------------------`
+ * </pre>
+ *
+ * For IQ sample meta mode, the Meta version ID and Stream flags should
+ * currently be set to values 0x00 and 0x00, respectively.
+ *
+ * <i>*</i>The number of samples in a <i>block</i> is dependent upon
+ * the USB speed being used:
+ * - USB 2.0 Hi-Speed: 256 samples
+ * - USB 3.0 SuperSpeed: 512 samples
+ *
+ * When using the bladerf_sync_rx() and bladerf_sync_tx() functions, the
+ * above details are entirely transparent; the caller need not be concerned
+ * with these details. These functions take care of packing/unpacking the
+ * metadata into/from the underlying stream and convey this information
+ * through the ::bladerf_metadata structure.
+ *
+ * However, when using the \ref FN_STREAMING_ASYNC interface, the user is
+ * responsible for manually packing/unpacking the above metadata into/from
+ * their samples.
+ *
+ * @see STREAMING_FORMAT_METADATA
+ * @see The `src/streaming/metadata.h` header in the libbladeRF codebase.
+ */
+ BLADERF_FORMAT_SC8_Q7_META,
+} bladerf_format;
+
+/**
+ * @defgroup STREAMING_FORMAT_METADATA Metadata structure and flags
+ *
+ * @{
+ */
+
+/*
+ * Metadata status bits
+ *
+ * These are used in conjunction with the bladerf_metadata structure's `status`
+ * field.
+ */
+
+/**
+ * A sample overrun has occurred.
+ *
+ * This indicates that either the host (more likely) or the FPGA is not keeping
+ * up with the incoming samples.
+ */
+#define BLADERF_META_STATUS_OVERRUN (1 << 0)
+
+/**
+ * A sample underrun has occurred.
+ *
+ * This generally only occurs on the TX channel when the FPGA is starved of
+ * samples.
+ *
+ * @note libbladeRF does not report this status. It is here for future use.
+ */
+#define BLADERF_META_STATUS_UNDERRUN (1 << 1)
+
+/*
+ * Metadata flags
+ *
+ * These are used in conjunction with the bladerf_metadata structure's `flags`
+ * field.
+ */
+
+/**
+ * Mark the associated buffer as the start of a burst transmission.
+ *
+ * @note This is only used for the bladerf_sync_tx() call.
+ *
+ * When using this flag, the bladerf_metadata::timestamp field should contain
+ * the timestamp at which samples should be sent.
+ *
+ * Between specifying the ::BLADERF_META_FLAG_TX_BURST_START and
+ * ::BLADERF_META_FLAG_TX_BURST_END flags, there is no need for the user to the
+ * bladerf_metadata::timestamp field because the library will ensure the
+ * correct value is used, based upon the timestamp initially provided and
+ * the number of samples that have been sent.
+ */
+#define BLADERF_META_FLAG_TX_BURST_START (1 << 0)
+
+/**
+ * Mark the associated buffer as the end of a burst transmission. This will
+ * flush the remainder of the sync interface's current working buffer and
+ * enqueue samples into the hardware's transmit FIFO.
+ *
+ * As of libbladeRF v1.3.0, it is no longer necessary for the API user to ensure
+ * that the final 3 samples of a burst are \f$0 + 0 j\f$. libbladeRF now ensures
+ * this hardware requirement is upheld.
+ *
+ * Specifying this flag and flushing the sync interface's working buffer implies
+ * that the next timestamp that can be transmitted is the current timestamp plus
+ * the duration of the burst that this flag is ending <b>and</b> the remaining
+ * length of the remaining buffer that is flushed. (The buffer size, in this
+ * case, is the `buffer_size` value passed to the previous bladerf_sync_config()
+ * call.)
+ *
+ * Rather than attempting to keep track of the number of samples sent with
+ * respect to buffer sizes, it is easiest to always assume 1 buffer's worth of
+ * time is required between bursts. In this case "buffer" refers to the
+ * `buffer_size` parameter provided to bladerf_sync_config().) If this is too
+ * much time, consider using the ::BLADERF_META_FLAG_TX_UPDATE_TIMESTAMP
+ * flag.
+ *
+ * @note This is only used for the bladerf_sync_tx() call. It is ignored by the
+ * bladerf_sync_rx() call.
+ */
+#define BLADERF_META_FLAG_TX_BURST_END (1 << 1)
+
+/**
+ * Use this flag in conjunction with ::BLADERF_META_FLAG_TX_BURST_START to
+ * indicate that the burst should be transmitted as soon as possible, as opposed
+ * to waiting for a specific timestamp.
+ *
+ * When this flag is used, there is no need to set the
+ * bladerf_metadata::timestamp field.
+ */
+#define BLADERF_META_FLAG_TX_NOW (1 << 2)
+
+/**
+ * Use this flag within a burst (i.e., between the use of
+ * ::BLADERF_META_FLAG_TX_BURST_START and ::BLADERF_META_FLAG_TX_BURST_END) to
+ * specify that bladerf_sync_tx() should read the bladerf_metadata::timestamp
+ * field and zero-pad samples up to the specified timestamp. The provided
+ * samples will then be transmitted at that timestamp.
+ *
+ * Use this flag when potentially flushing an entire buffer via the
+ * ::BLADERF_META_FLAG_TX_BURST_END would yield an unacceptably large gap in the
+ * transmitted samples.
+ *
+ * In some applications where a transmitter is constantly transmitting with
+ * extremely small gaps (less than a buffer), users may end up using a single
+ * ::BLADERF_META_FLAG_TX_BURST_START, and then numerous calls to
+ * bladerf_sync_tx() with the ::BLADERF_META_FLAG_TX_UPDATE_TIMESTAMP flag set.
+ * The ::BLADERF_META_FLAG_TX_BURST_END would only be used to end the stream
+ * when shutting down.
+ */
+#define BLADERF_META_FLAG_TX_UPDATE_TIMESTAMP (1 << 3)
+
+/**
+ * This flag indicates that calls to bladerf_sync_rx should return any available
+ * samples, rather than wait until the timestamp indicated in the
+ * bladerf_metadata timestamp field.
+ */
+#define BLADERF_META_FLAG_RX_NOW (1 << 31)
+
+/**
+ * This flag is asserted in bladerf_metadata.status by the hardware when an
+ * underflow is detected in the sample buffering system on the device.
+ */
+#define BLADERF_META_FLAG_RX_HW_UNDERFLOW (1 << 0)
+
+/**
+ * This flag is asserted in bladerf_metadata.status by the hardware if mini
+ * expansion IO pin 1 is asserted.
+ */
+#define BLADERF_META_FLAG_RX_HW_MINIEXP1 (1 << 16)
+
+/**
+ * This flag is asserted in bladerf_metadata.status by the hardware if mini
+ * expansion IO pin 2 is asserted.
+ */
+#define BLADERF_META_FLAG_RX_HW_MINIEXP2 (1 << 17)
+
+/**
+ * Sample metadata
+ *
+ * This structure is used in conjunction with the ::BLADERF_FORMAT_SC16_Q11_META
+ * format to TX scheduled bursts or retrieve timestamp information about
+ * received samples.
+ */
+struct bladerf_metadata {
+ /**
+ * Free-running FPGA counter that monotonically increases at the sample rate
+ * of the associated channel.
+ */
+ bladerf_timestamp timestamp;
+
+ /**
+ * Input bit field to control the behavior of the call that the metadata
+ * structure is passed to. API calls read this field from the provided data
+ * structure, and do not modify it.
+ *
+ * Valid flags include
+ * ::BLADERF_META_FLAG_TX_BURST_START,
+ * ::BLADERF_META_FLAG_TX_BURST_END,
+ * ::BLADERF_META_FLAG_TX_NOW,
+ * ::BLADERF_META_FLAG_TX_UPDATE_TIMESTAMP, and
+ * ::BLADERF_META_FLAG_RX_NOW
+ */
+ uint32_t flags;
+
+ /**
+ * Output bit field to denoting the status of transmissions/receptions. API
+ * calls will write this field.
+ *
+ * Possible status flags include ::BLADERF_META_STATUS_OVERRUN and
+ * ::BLADERF_META_STATUS_UNDERRUN.
+ */
+ uint32_t status;
+
+ /**
+ * This output parameter is updated to reflect the actual number of
+ * contiguous samples that have been populated in an RX buffer during a
+ * bladerf_sync_rx() call.
+ *
+ * This will not be equal to the requested count in the event of a
+ * discontinuity (i.e., when the status field has the
+ * ::BLADERF_META_STATUS_OVERRUN flag set). When an overrun occurs, it is
+ * important not to read past the number of samples specified by this value,
+ * as the remaining contents of the buffer are undefined.
+ *
+ * @note This parameter is not currently used by bladerf_sync_tx().
+ */
+ unsigned int actual_count;
+
+ /**
+ * Reserved for future use. This is not used by any functions. It is
+ * recommended that users zero out this field.
+ */
+ uint8_t reserved[32];
+};
+
+/** @} (End of STREAMING_FORMAT_METADATA) */
+
+/**
+ * Interleaves contiguous blocks of samples in preparation for MIMO TX.
+ *
+ * Given a `buffer` loaded with data as such:
+ *
+ * <pre>
+ * .-------------------.--------------.--------------.------------------.
+ * | Byte offset | Bits 31...16 | Bits 15...0 | Description |
+ * +-------------------+--------------+--------------+------------------+
+ * | 0x00 + 0*chsize | Q0[0] | I0[0] | Ch 0, sample 0 |
+ * | 0x04 + 0*chsize | Q0[1] | I0[1] | Ch 0, sample 1 |
+ * | 0x08 + 0*chsize | Q0[2] | I0[2] | Ch 0, sample 2 |
+ * | 0x0c + 0*chsize | Q0[3] | I0[3] | Ch 0, sample 3 |
+ * | ... | ... | ... | ... |
+ * | 0x00 + 1*chsize | Q1[0] | I1[0] | Ch 1, sample 0 |
+ * | 0x04 + 1*chsize | Q1[1] | I1[1] | Ch 1, sample 1 |
+ * | 0x08 + 1*chsize | Q1[2] | I1[2] | Ch 1, sample 2 |
+ * | 0x0c + 1*chsize | Q1[3] | I1[3] | Ch 1, sample 3 |
+ * | ... | ... | ... | ... |
+ * `-------------------`--------------`--------------`------------------`
+ * </pre>
+ *
+ * where \f$chsize = \frac{sizeof(buffer)}{num\_channels}\f$.
+ *
+ * This function interleaves the samples in the manner described by the
+ * ::BLADERF_FORMAT_SC16_Q11 format, in place. Each channel must have
+ * \f$buffer\_size / num\_channels\f$ samples, and they must be concatenated in
+ * order.
+ *
+ * If the ::BLADERF_FORMAT_SC16_Q11_META format is specified, the first 16 bytes
+ * will skipped.
+ *
+ * This function's inverse is bladerf_deinterleave_stream_buffer().
+ *
+ * @param[in] layout Stream direction and layout
+ * @param[in] format Data format to use
+ * @param[in] buffer_size The size of the buffer, in samples. Note that this
+ * is the entire buffer, not just a single channel.
+ * @param samples Buffer to process. The user is responsible for
+ * ensuring this buffer contains exactly
+ * `buffer_size` samples.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_interleave_stream_buffer(bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int buffer_size,
+ void *samples);
+
+/**
+ * Deinterleaves samples into contiguous blocks after MIMO RX.
+ *
+ * This function deinterleaves a multi-channel interleaved buffer, as described
+ * by the ::BLADERF_FORMAT_SC16_Q11 format. The output is in the format
+ * described as the input to this function's inverse,
+ * bladerf_interleave_stream_buffer().
+ *
+ * If the ::BLADERF_FORMAT_SC16_Q11_META format is specified, the first 16 bytes
+ * will skipped.
+ *
+ * @param[in] layout Stream direction and layout
+ * @param[in] format Data format to use
+ * @param[in] buffer_size The size of the buffer, in samples. Note that
+ * this is the entire buffer, not just a single
+ * channel.
+ * @param samples Buffer to process. The user is responsible for
+ * ensuring this buffer contains exactly
+ * `buffer_size` samples.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_deinterleave_stream_buffer(bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int buffer_size,
+ void *samples);
+
+/** @} (End of STREAMING_FORMAT) */
+
+/**
+ * Enable or disable the RF front end of the specified direction.
+ *
+ * RF front ends must always be enabled prior to streaming samples on the
+ * associated interface.
+ *
+ * When a synchronous stream is associated with the specified channel, this will
+ * shut down the underlying asynchronous stream when `enable` = false.
+ *
+ * When transmitting samples, be sure to provide ample time for TX samples reach
+ * the RF front-end before calling this function with `enable` = false. (This
+ * can be achieved easily when using metadata, as shown on
+ * \link sync_tx_meta_bursts.html this page\endlink.)
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] enable true to enable, false to disable
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_enable_module(struct bladerf *dev,
+ bladerf_channel ch,
+ bool enable);
+
+/**
+ * Retrieve the specified stream's current timestamp counter value from the
+ * FPGA.
+ *
+ * This function is only intended to be used to retrieve a coarse estimate of
+ * the current timestamp when starting up a stream. It <b>should not</b> be used
+ * as a means to accurately retrieve the current timestamp of individual samples
+ * within a running stream. The reasons for this are:
+ * - The timestamp counter will have advanced during the time that the captured
+ * value is propagated back from the FPGA to the host
+ * - The value retrieved in this manner is not tightly-coupled with
+ * specific sample positions in the stream.
+ *
+ * When actively receiving a sample stream, instead use the
+ * ::bladerf_metadata::timestamp field (provided when using the
+ * ::BLADERF_FORMAT_SC16_Q11_META format) to retrieve the timestamp value
+ * associated with a block of samples. See the \link sync_rx_meta.html RX with
+ * metadata\endlink page for examples of this.
+ *
+ * An example use-case of this function is to schedule an initial TX burst in a
+ * set of bursts:
+ *
+ * - Configure and start a TX stream using the ::BLADERF_FORMAT_SC16_Q11_META
+ * format.
+ * - Retrieve timestamp \f$T\f$, a coarse estimate the TX's current timestamp
+ * via this function.
+ * - Schedule the first burst, \f$F\f$ to occur in the future: \f$F = T + N\f$.
+ * Generally, adding \f$N\f$ in tens to low hundreds of milliseconds is
+ * sufficient to account for timestamp retrieval overhead and stream
+ * startup.
+ * - Schedule additional bursts relative to the first burst \f$F\f$.
+ *
+ * Examples of the above are shown on the \link sync_tx_meta_bursts.html TX
+ * with metadata\endlink page.
+ *
+ * @param dev Device handle
+ * @param[in] dir Stream direction
+ * @param[out] timestamp Coarse timestamp value
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_timestamp(struct bladerf *dev,
+ bladerf_direction dir,
+ bladerf_timestamp *timestamp);
+
+/**
+ * @defgroup FN_STREAMING_SYNC Synchronous API
+ *
+ * This group of functions presents synchronous, blocking calls (with optional
+ * timeouts) for transmitting and receiving samples.
+ *
+ * The synchronous interface is built atop the asynchronous interface, and is
+ * generally less complex and easier to work with. It alleviates the need to
+ * explicitly spawn threads (it is done under the hood) and manually manage
+ * sample buffers.
+ *
+ * Under the hood, this interface spawns worker threads to handle an
+ * asynchronous stream and perform thread-safe buffer management.
+ *
+ * These functions are thread-safe.
+ *
+ * The following pages provide additional information and example usage:
+ *
+ * - \link sync_no_meta.html Basic usage without metadata\endlink
+ * - \link sync_rx_meta.html Synchronous RX with metadata\endlink
+ * - \link sync_tx_meta_bursts.html Synchronous TX with metadata\endlink
+ *
+ * @{
+ */
+
+/**
+ * (Re)Configure a device for synchronous transmission or reception
+ *
+ * This function sets up the device for the specified format and initializes
+ * the underlying asynchronous stream parameters
+ *
+ * This function does not call bladerf_enable_module(). The API user is
+ * responsible for enabling/disable streams when desired.
+ *
+ * Note that (re)configuring the TX direction does not affect the RX direction,
+ * and vice versa. This call configures each direction independently.
+ *
+ * Memory allocated by this function will be deallocated when bladerf_close()
+ * is called.
+ *
+ * @see The bladerf_init_stream() documentation for information on determining
+ * appropriate values for `buffers_size`, `num_transfers`, and
+ * `stream_timeout`.
+ *
+ * @note The `num_buffers` parameter should generally be increased as the amount
+ * of work done between bladerf_sync_rx() or bladerf_sync_tx() calls
+ * increases.
+ *
+ * @param dev Device to configure
+ * @param[in] layout Stream direction and layout
+ * @param[in] format Format to use in synchronous data transfers
+ * @param[in] num_buffers The number of buffers to use in the underlying
+ * data stream. This must be greater than the
+ * `num_xfers` parameter.
+ * @param[in] buffer_size The size of the underlying stream buffers, in
+ * samples. This value must be a multiple of 1024.
+ * Note that samples are only transferred when a
+ * buffer of this size is filled.
+ * @param[in] num_transfers The number of active USB transfers that may be
+ * in-flight at any given time. If unsure of what
+ * to use here, try values of 4, 8, or 16.
+ * @param[in] stream_timeout Timeout (milliseconds) for transfers in the
+ * underlying data stream.
+ *
+ * @return 0 on success,
+ * ::BLADERF_ERR_UNSUPPORTED if libbladeRF is not built with support
+ * for this functionality,
+ * or a value from \ref RETCODES list on failures.
+ */
+API_EXPORT
+int CALL_CONV bladerf_sync_config(struct bladerf *dev,
+ bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int num_buffers,
+ unsigned int buffer_size,
+ unsigned int num_transfers,
+ unsigned int stream_timeout);
+
+/**
+ * Transmit IQ samples.
+ *
+ * Under the hood, this call starts up an underlying asynchronous stream as
+ * needed. This stream can be stopped by disabling the TX channel. (See
+ * bladerf_enable_module for more details.)
+ *
+ * Samples will only be sent to the FPGA when a buffer have been filled. The
+ * number of samples required to fill a buffer corresponds to the `buffer_size`
+ * parameter passed to bladerf_sync_config().
+ *
+ * @pre A bladerf_sync_config() call has been to configure the device for
+ * synchronous data transfer.
+ *
+ * @note A call to bladerf_enable_module() should be made before attempting to
+ * transmit samples. Failing to do this may result in timeouts and other
+ * errors.
+ *
+ * @param dev Device handle
+ * @param[in] samples Array of samples
+ * @param[in] num_samples Number of samples to write
+ * @param[in] metadata Sample metadata. This must be provided when using
+ * the ::BLADERF_FORMAT_SC16_Q11_META format, but may
+ * be NULL when the interface is configured for
+ * the ::BLADERF_FORMAT_SC16_Q11 format.
+ * @param[in] timeout_ms Timeout (milliseconds) for this call to complete.
+ * Zero implies "infinite."
+ *
+ * @return 0 on success,
+ * ::BLADERF_ERR_UNSUPPORTED if libbladeRF is not built with support
+ * for this functionality,
+ * or a value from \ref RETCODES list on failures.
+ */
+API_EXPORT
+int CALL_CONV bladerf_sync_tx(struct bladerf *dev,
+ const void *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms);
+
+/**
+ * Receive IQ samples.
+ *
+ * Under the hood, this call starts up an underlying asynchronous stream as
+ * needed. This stream can be stopped by disabling the RX channel. (See
+ * bladerf_enable_module for more details.)
+ *
+ * @pre A bladerf_sync_config() call has been to configure the device for
+ * synchronous data transfer.
+ *
+ * @note A call to bladerf_enable_module() should be made before attempting to
+ * receive samples. Failing to do this may result in timeouts and other
+ * errors.
+ *
+ * @param dev Device handle
+ * @param[out] samples Buffer to store samples in. The caller is
+ * responsible for ensuring this buffer is sufficiently
+ * large for the number of samples requested,
+ * considering the size of the sample format being
+ * used.
+ * @param[in] num_samples Number of samples to read
+ * @param[out] metadata Sample metadata. This must be provided when using
+ * the ::BLADERF_FORMAT_SC16_Q11_META format, but may
+ * be NULL when the interface is configured for
+ * the ::BLADERF_FORMAT_SC16_Q11 format.
+ * @param[in] timeout_ms Timeout (milliseconds) for this call to complete.
+ * Zero implies "infinite."
+ *
+ * @return 0 on success,
+ * ::BLADERF_ERR_UNSUPPORTED if libbladeRF is not built with support
+ * for this functionality,
+ * or a value from \ref RETCODES list on failures.
+ */
+API_EXPORT
+int CALL_CONV bladerf_sync_rx(struct bladerf *dev,
+ void *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms);
+
+
+/** @} (End of FN_STREAMING_SYNC) */
+
+/**
+ * @defgroup FN_STREAMING_ASYNC Asynchronous API
+ *
+ * This interface gives the API user full control over the stream and buffer
+ * management, at the cost of added complexity.
+ *
+ * @note New users are recommended to first evaluate the \ref FN_STREAMING_SYNC
+ * interface, and to only use this interface if the former is found to not
+ * yield suitable performance.
+ *
+ * These functions are either thread-safe or may be used in a thread-safe
+ * manner (per the details noted in the function description).
+ *
+ * @{
+ */
+
+/**
+ * Use this as a return value in callbacks or as the buffer parameter to
+ * bladerf_submit_stream_buffer() to shutdown a stream.
+ */
+#define BLADERF_STREAM_SHUTDOWN (NULL)
+
+/**
+ * Use this value in a stream callback to indicate that no buffer is being
+ * provided. In this case, buffers are expected to be provided via
+ * bladerf_submit_stream_buffer().
+ */
+#define BLADERF_STREAM_NO_DATA ((void *)(-1))
+
+/** This opaque structure is used to keep track of stream information */
+struct bladerf_stream;
+
+/**
+ * This typedef represents a callback function that is executed in response to
+ * this interface's asynchronous events.
+ *
+ * Stream callbacks <b>must not</b> block or perform long-running operations.
+ * Otherwise, timeouts may occur. If this cannot be guaranteed, consider
+ * returning ::BLADERF_STREAM_NO_DATA in callbacks and later submit a buffer
+ * using bladerf_submit_stream_buffer(). However, callbacks should always take
+ * a single approach of returning buffers <b>or</b> returning
+ * ::BLADERF_STREAM_NO_DATA and submitting buffers later -- <b>but not both</b>.
+ *
+ * When running in a full-duplex mode of operation with simultaneous TX and RX
+ * stream threads, be aware that one stream's callback may occur in the context
+ * of another stream's thread. The API user is responsible for ensuring their
+ * callbacks are thread safe. For example, when managing access to sample
+ * buffers, the caller must ensure that if one thread is processing samples in a
+ * buffer, that this buffer is not returned via the callback's return value.
+ *
+ * As of libbladeRF v0.15.0, is guaranteed that only one callback from a stream
+ * will occur at a time. (i.e., a second TX callback will not fire while one is
+ * currently being handled.) To achieve this, while a callback is executing, a
+ * per-stream lock is held. It is important to consider this when thinking about
+ * the order of lock acquisitions both in the callbacks, and the code
+ * surrounding bladerf_submit_stream_buffer().
+ *
+ * @note Do not call bladerf_submit_stream_buffer() from a callback.
+ *
+ * For both RX and TX, the stream callback receives:
+ * - dev: Device structure
+ * - stream: The associated stream
+ * - metadata: For future support - do not attempt to read/write this
+ * in the current library implementation.
+ * - user_data: User data provided when initializing stream
+ *
+ * For TX callbacks:
+ * - samples: Pointer to buffer of samples that was sent
+ * - num_samples: Number of sent in last transfer and to send in next transfer
+ * - Return value: The user specifies the address of the next buffer to send,
+ * ::BLADERF_STREAM_SHUTDOWN, or ::BLADERF_STREAM_NO_DATA.
+ *
+ * For RX callbacks:
+ * - samples: Buffer filled with received data
+ * - num_samples: Number of samples received and size of next buffers
+ * - Return value: The user specifies the next buffer to fill with RX data,
+ * which should be `num_samples` in size,
+ * ::BLADERF_STREAM_SHUTDOWN, or ::BLADERF_STREAM_NO_DATA.
+ */
+typedef void *(*bladerf_stream_cb)(struct bladerf *dev,
+ struct bladerf_stream *stream,
+ struct bladerf_metadata *meta,
+ void *samples,
+ size_t num_samples,
+ void *user_data);
+
+/**
+ * Initialize a stream for use with asynchronous routines.
+ *
+ * This function will internally allocate data buffers, which will be provided
+ * to the API user in callback functions.
+ *
+ * The `buffers` output parameter populates a pointer to the list of allocated
+ * buffers. This allows the API user to implement a buffer management scheme to
+ * best suit his or her specific use case.
+ *
+ * Generally, one will want to set the `buffers` parameter to a value larger
+ * than the `num_transfers` parameter, and keep track of which buffers are
+ * currently "in-flight", versus those available for use.
+ *
+ * For example, for a transmit stream, modulated data can be actively written
+ * into free buffers while transfers of other buffers are occurring. Once a
+ * buffer has been filled with data, it can be marked 'in-flight' and be
+ * returned in a successive callback to transmit.
+ *
+ * The choice of values for the `num_transfers` and `buffer_size` should be
+ * made based upon the desired samplerate, and the stream timeout value
+ * specified via bladerf_set_stream_timeout(), which defaults to 1 second.
+ *
+ * For a given sample rate, the below relationship must be upheld to transmit or
+ * receive data without timeouts or dropped data.
+ *
+ * @f[
+ * Sample\ Rate > \frac{\#\ Transfers}{Timeout} \times Buffer\ Size
+ * @f]
+ *
+ * ...where Sample Rate is in samples per second, and Timeout is in seconds.
+ *
+ * To account for general system overhead, it is recommended to multiply the
+ * righthand side by 1.1 to 1.25.
+ *
+ * While increasing the number of buffers available provides additional
+ * elasticity, be aware that it also increases latency.
+ *
+ * @param[out] stream Upon success, this will be updated to contain
+ * a stream handle (i.e., address)
+ * @param dev Device to associate with the stream
+ * @param[in] callback Callback routine to handle asynchronous events
+ * @param[out] buffers This will be updated to point to a dynamically
+ * allocated array of buffer pointers.
+ * @param[in] num_buffers Number of buffers to allocate and return. This
+ * value must >= the `num_transfers` parameter.
+ * @param[in] format Sample data format
+ * @param[in] samples_per_buffer Size of allocated buffers, in units of
+ * samples Note that the physical size of the
+ * buffer is a function of this and the format
+ * parameter.
+ * @param[in] num_transfers Maximum number of transfers that may be
+ * in-flight simultaneously. This must be <= the
+ * `num_buffers` parameter.
+ * @param[in] user_data Caller-provided data that will be provided
+ * in stream callbacks
+ *
+ * @note This call should be later followed by a call to
+ * bladerf_deinit_stream() to avoid memory leaks.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_init_stream(struct bladerf_stream **stream,
+ struct bladerf *dev,
+ bladerf_stream_cb callback,
+ void ***buffers,
+ size_t num_buffers,
+ bladerf_format format,
+ size_t samples_per_buffer,
+ size_t num_transfers,
+ void *user_data);
+
+/**
+ * Begin running a stream. This call will block until the stream completes.
+ *
+ * Only 1 RX stream and 1 TX stream may be running at a time. Attempting to
+ * call bladerf_stream() with more than one stream will yield unexpected (and
+ * most likely undesirable) results.
+ *
+ * @note See the ::bladerf_stream_cb description for additional thread-safety
+ * caveats.
+ *
+ * @pre This function should be preceded by a call to bladerf_enable_module()
+ * to enable the associated RX or TX directions before attempting to use
+ * it to stream data.
+ *
+ * @param stream A stream handle that has been successfully been
+ * initialized via bladerf_init_stream()
+ * @param[in] layout Stream direction and channel layout
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_stream(struct bladerf_stream *stream,
+ bladerf_channel_layout layout);
+
+/**
+ * Submit a buffer to a stream from outside of a stream callback function.
+ * Use this only when returning BLADERF_STREAM_NO_DATA from callbacks. <b>Do
+ * not</b> use this function if the associated callback functions will be
+ * returning buffers for submission.
+ *
+ * This call may block if the device is not ready to submit a buffer for
+ * transfer. Use the `timeout_ms` to place an upper limit on the time this
+ * function can block.
+ *
+ * To safely submit buffers from outside the stream callback flow, this function
+ * internally acquires a per-stream lock (the same one that is held during the
+ * execution of a stream callback). Therefore, it is important to be aware of
+ * locks that may be held while making this call, especially those acquired
+ * during execution of the associated stream callback function. (i.e., be wary
+ * of the order of lock acquisitions, including the internal per-stream lock.)
+ *
+ * @param stream Stream to submit buffer to
+ * @param[inout] buffer Buffer to fill (RX) or containing data (TX).
+ * This buffer is assumed to be the size specified
+ * in the associated bladerf_init_stream() call.
+ * @param[in] timeout_ms Milliseconds to timeout in, if this call blocks.
+ * 0 implies an "infinite" wait.
+ *
+ * @return 0 on success, ::BLADERF_ERR_TIMEOUT upon a timeout, or a value from
+ * \ref RETCODES list on other failures
+ */
+API_EXPORT
+int CALL_CONV bladerf_submit_stream_buffer(struct bladerf_stream *stream,
+ void *buffer,
+ unsigned int timeout_ms);
+
+/**
+ * This is a non-blocking variant of bladerf_submit_stream_buffer(). All of the
+ * caveats and important notes from bladerf_submit_stream_buffer() apply.
+ *
+ * In the event that this call would need to block in order to submit a buffer,
+ * it returns BLADERF_ERR_WOULD_BLOCK. In this case, the caller could either
+ * wait and try again or defer buffer submission to the asynchronous callback.
+ *
+ * @param stream Stream to submit buffer to
+ * @param[inout] buffer Buffer to fill (RX) or containing data (TX).
+ * This buffer is assumed to be the size specified
+ * in the associated bladerf_init_stream() call.
+ *
+ * @return 0 on success, ::BLADERF_ERR_WOULD_BLOCK if the call would have to
+ * block to succeed, or another value from \ref RETCODES upon other
+ * failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_submit_stream_buffer_nb(struct bladerf_stream *stream,
+ void *buffer);
+
+
+/**
+ * Deinitialize and deallocate stream resources.
+ *
+ * @pre Stream is no longer being used (via bladerf_submit_stream_buffer() or
+ * bladerf_stream() calls.)
+ *
+ * @post Stream is deallocated and may no longer be used.
+ *
+ * @param stream Stream to deinitialize. This function does nothing if
+ * stream is `NULL`.
+ */
+API_EXPORT
+void CALL_CONV bladerf_deinit_stream(struct bladerf_stream *stream);
+
+/**
+ * Set stream transfer timeout in milliseconds
+ *
+ * @param dev Device handle
+ * @param[in] dir Stream direction
+ * @param[in] timeout Timeout in milliseconds
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_stream_timeout(struct bladerf *dev,
+ bladerf_direction dir,
+ unsigned int timeout);
+
+/**
+ * Get transfer timeout in milliseconds
+ *
+ * @param dev Device handle
+ * @param[in] dir Stream direction
+ * @param[out] timeout On success, updated with current transfer
+ * timeout value. Undefined on failure.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_stream_timeout(struct bladerf *dev,
+ bladerf_direction dir,
+ unsigned int *timeout);
+
+/** @} (End of FN_STREAMING_ASYNC) */
+
+/** @} (End of STREAMING) */
+
+/**
+ * @defgroup FN_PROG Firmware and FPGA
+ *
+ * These functions provide the ability to load and program devices on the
+ * bladeRF board.
+ *
+ * Care should be taken with bootloader recovery functions to ensure that
+ * devices operated on are indeed a bladeRF, as opposed to another FX3-based
+ * device running in bootloader mode.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * Write FX3 firmware to the bladeRF's SPI flash
+ *
+ * @note This will require a power cycle to take effect
+ *
+ * @param dev Device handle
+ * @param[in] firmware Full path to firmware file
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_flash_firmware(struct bladerf *dev, const char *firmware);
+
+/**
+ * Load device's FPGA.
+ *
+ * @note This FPGA configuration will be reset at the next power cycle.
+ *
+ * @param dev Device handle
+ * @param[in] fpga Full path to FPGA bitstream
+ *
+ * @return 0 upon successfully, or a value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_load_fpga(struct bladerf *dev, const char *fpga);
+
+/**
+ * Write the provided FPGA image to the bladeRF's SPI flash and enable FPGA
+ * loading from SPI flash at power on (also referred to within this project as
+ * FPGA "autoloading").
+ *
+ * @param dev Device handle
+ * @param[in] fpga_image Full path to FPGA file
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_flash_fpga(struct bladerf *dev, const char *fpga_image);
+
+/**
+ * Erase the FPGA region of SPI flash, effectively disabling FPGA autoloading
+ *
+ * @param dev Device handle
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_erase_stored_fpga(struct bladerf *dev);
+
+/**
+ * Reset the device, causing it to reload its firmware from flash
+ *
+ * @param dev Device handle
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_device_reset(struct bladerf *dev);
+
+/**
+ * Read firmware log data and write it to the specified file
+ *
+ * @param dev Device to read firmware log from
+ * @param[in] filename Filename to write log information to. If set to
+ * `NULL`, log data will be printed to stdout.
+ *
+ * @return 0 upon success, or a value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_fw_log(struct bladerf *dev, const char *filename);
+
+/**
+ * Clear out a firmware signature word in flash and jump to FX3 bootloader.
+ *
+ * The device will continue to boot into the FX3 bootloader across power cycles
+ * until new firmware is written to the device.
+ *
+ * @param dev Device handle
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_jump_to_bootloader(struct bladerf *dev);
+
+/**
+ * Get a list of devices that are running the FX3 bootloader.
+ *
+ * After obtaining this list, identify the device that you would like to load
+ * firmware onto. Save the bus and address values so that you can provide them
+ * to bladerf_load_fw_from_bootloader(), and then free this list via
+ * bladerf_free_device_list().
+ *
+ * @param[out] list Upon finding devices, this will be updated to point
+ * to a list of bladerf_devinfo structures that
+ * describe the identified devices.
+ *
+ * @return Number of items populated in `list`,
+ * or an error value from the \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_bootloader_list(struct bladerf_devinfo **list);
+
+/**
+ * Download firmware to the specified device that is enumarating an FX3
+ * bootloader, and begin executing the firmware from RAM.
+ *
+ * @note This function <b>does not</b> write the firmware to SPI flash. If this
+ * is desired, open the newly enumerated device with bladerf_open() and use
+ * bladerf_flash_firmware().
+ *
+ * @param[in] device_identifier Device identifier string describing the
+ * backend to use via the
+ * `<backend>:device=<bus>:<addr>` syntax. If
+ * this is NULL, the backend, bus, and addr
+ * arguments will be used instead.
+ * @param[in] backend Backend to use. This is only used if
+ * device_identifier is `NULL`.
+ * @param[in] bus Bus number the device is located on. This
+ * is only used if device_identifier is `NULL`.
+ * @param[in] addr Bus address the device is located on. This
+ * is only used if device_identifier is `NULL`.
+ * @param[in] file Filename of the firmware image to boot
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_load_fw_from_bootloader(const char *device_identifier,
+ bladerf_backend backend,
+ uint8_t bus,
+ uint8_t addr,
+ const char *file);
+
+/** @} (End of FN_PROG) */
+
+/**
+ * @defgroup FN_IMAGE Flash image format
+ *
+ * This section contains a file format and associated routines for storing
+ * and loading flash contents with metadata.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/** Type of data stored in a flash image */
+typedef enum {
+ BLADERF_IMAGE_TYPE_INVALID = -1, /**< Used to denote invalid value */
+ BLADERF_IMAGE_TYPE_RAW, /**< Misc. raw data */
+ BLADERF_IMAGE_TYPE_FIRMWARE, /**< Firmware data */
+ BLADERF_IMAGE_TYPE_FPGA_40KLE, /**< FPGA bitstream for 40 KLE device */
+ BLADERF_IMAGE_TYPE_FPGA_115KLE, /**< FPGA bitstream for 115 KLE device */
+ BLADERF_IMAGE_TYPE_FPGA_A4, /**< FPGA bitstream for A4 device */
+ BLADERF_IMAGE_TYPE_FPGA_A9, /**< FPGA bitstream for A9 device */
+ BLADERF_IMAGE_TYPE_CALIBRATION, /**< Board calibration */
+ BLADERF_IMAGE_TYPE_RX_DC_CAL, /**< RX DC offset calibration table */
+ BLADERF_IMAGE_TYPE_TX_DC_CAL, /**< TX DC offset calibration table */
+ BLADERF_IMAGE_TYPE_RX_IQ_CAL, /**< RX IQ balance calibration table */
+ BLADERF_IMAGE_TYPE_TX_IQ_CAL, /**< TX IQ balance calibration table */
+ BLADERF_IMAGE_TYPE_FPGA_A5, /**< FPGA bitstream for A5 device */
+ BLADERF_IMAGE_TYPE_GAIN_CAL, /**< Gain calibration */
+} bladerf_image_type;
+
+/** Size of the magic signature at the beginning of bladeRF image files */
+#define BLADERF_IMAGE_MAGIC_LEN 7
+
+/** Size of bladeRF flash image checksum */
+#define BLADERF_IMAGE_CHECKSUM_LEN 32
+
+/** Size of reserved region of flash image */
+#define BLADERF_IMAGE_RESERVED_LEN 128
+
+/**
+ * Image format for backing up and restoring bladeRF flash contents
+ *
+ * The on disk format generated by the bladerf_image_write function is a
+ * serialized version of this structure and its contents. When written to disk,
+ * values are converted to big-endian byte order, for ease of reading in a hex
+ * editor.
+ *
+ * When creating and using a bladerf_image of type ::BLADERF_IMAGE_TYPE_RAW,
+ * the address and length fields must be erase-block aligned.
+ */
+struct bladerf_image {
+ /**
+ * Magic value used to identify image file format.
+ *
+ * Note that an extra character is added to store a `NUL`-terminator,
+ * to allow this field to be printed. This `NUL`-terminator is *NOT*
+ * written in the serialized image.
+ */
+ char magic[BLADERF_IMAGE_MAGIC_LEN + 1];
+
+ /**
+ * SHA256 checksum of the flash image. This is computed over the entire
+ * image, with this field filled with 0's.
+ */
+ uint8_t checksum[BLADERF_IMAGE_CHECKSUM_LEN];
+
+ /**
+ * Image format version. Only the major, minor, and patch fields are
+ * written to the disk; the describe field is not used. The version is
+ * serialized as: [major | minor | patch]
+ */
+ struct bladerf_version version;
+
+ /** UTC image timestamp, in seconds since the Unix Epoch */
+ uint64_t timestamp;
+
+ /**
+ * Serial number of the device that the image was obtained from. This
+ * field should be all '\0' if irrelevant.
+ *
+ * The +1 here is actually extraneous; ::BLADERF_SERIAL_LENGTH already
+ * accounts for a `NUL` terminator. However, this is left here to avoid
+ * breaking backwards compatibility.
+ */
+ char serial[BLADERF_SERIAL_LENGTH + 1];
+
+ /**
+ * Reserved for future metadata. Should be 0's.
+ */
+ char reserved[BLADERF_IMAGE_RESERVED_LEN];
+
+ /**
+ * Type of data contained in the image. Serialized as a uint32_t.
+ */
+ bladerf_image_type type;
+
+ /**
+ * Address of the flash data in this image. A value of `0xffffffff`
+ * implies that this field is left unspecified (i.e., "don't care").
+ */
+ uint32_t address;
+
+ /** Length of the data contained in the image */
+ uint32_t length;
+
+ /** Image data */
+ uint8_t *data;
+};
+
+/**
+ * Allocate and initialize an image structure.
+ *
+ * This following bladerf_image fields are populated: `magic`, `version`,
+ * `timestamp`, `type`, `address`, and `length`
+ *
+ * The following bladerf_image fields are zeroed out: `checksum`, `serial`, and
+ * `reserved`
+ *
+ * If the `length` parameter is not 0, the ::bladerf_image `data` field will be
+ * dynamically allocated. Otherwise, `data` will be set to NULL.
+ *
+ * @note A non-zero `length` should be use only with bladerf_image_write();
+ * bladerf_image_read() allocates and sets `data` based upon size of the
+ * image contents, and does not attempt to free() the `data` field before
+ * setting it.
+ *
+ * The `address` and `length` fields should be set 0 when reading an image from
+ * a file.
+ *
+ * @param[in] dev Device handle
+ * @param[in] type Image type to be created, represented by `bladerf_image_type`
+ * @param[in] address Address in flash memory where the image is stored. Use 0xffffffff if not applicable.
+ * @param[in] length Length of the image data in bytes
+ *
+ * @return Pointer to allocated and initialized structure on success,
+ * `NULL` on memory allocation failure or invalid address/length.
+ */
+API_EXPORT
+struct bladerf_image *CALL_CONV bladerf_alloc_image(struct bladerf *dev,
+ bladerf_image_type type,
+ uint32_t address,
+ uint32_t length);
+
+/**
+ * Create a flash image initialized to contain a calibration data region.
+ *
+ * This is intended to be used in conjunction with bladerf_image_write(), or a
+ * write of the image's `data` field to flash.
+ *
+ * @param[in] dev Device handle
+ * @param[in] fpga_size Target FPGA size
+ * @param[in] vctcxo_trim VCTCXO oscillator trim value.
+ *
+ * @return Pointer to allocated and initialized structure on success,
+ * `NULL` on memory allocation failure
+ */
+API_EXPORT
+struct bladerf_image *CALL_CONV bladerf_alloc_cal_image(
+ struct bladerf *dev, bladerf_fpga_size fpga_size, uint16_t vctcxo_trim);
+
+/**
+ * Free a bladerf_image previously obtained via bladerf_alloc_image.
+ *
+ * If the bladerf_image's `data` field is non-`NULL`, it will be freed.
+ *
+ * @param[inout] image Flash image
+ */
+API_EXPORT
+void CALL_CONV bladerf_free_image(struct bladerf_image *image);
+
+/**
+ * @brief Prints the metadata of a bladeRF image structure.
+ *
+ * This function displays the metadata of a provided `bladerf_image` structure.
+ * It includes information such as the magic number, version, timestamp, serial number,
+ * address, and length of the image. The function will return an error code if the
+ * provided image pointer is `NULL`.
+ *
+ * @pre The image should have been allocated using bladerf_alloc_image().
+ *
+ * @note This function only prints the metadata of the image and does not
+ * perform any operations on the image data itself.
+ *
+ * @param[in] image Pointer to the `bladerf_image` structure whose metadata is to be printed.
+ * It should not be `NULL`.
+ *
+ * @return 0 on success, BLADERF_ERR_MEM if the image pointer is `NULL`.
+ */
+API_EXPORT
+int CALL_CONV bladerf_image_print_metadata(const struct bladerf_image *image);
+
+/**
+ * @brief Converts a bladeRF image type to its corresponding string representation.
+ *
+ * This function maps a `bladerf_image_type` enumeration value to a human-readable
+ * string. It is useful for logging, debugging, or displaying the image type
+ * to an end user.
+ *
+ * @param[in] type The `bladerf_image_type` enumeration value to be converted.
+ *
+ * @return A pointer to a string representing the image type. Returns "Unknown Type"
+ * for any unrecognized or out-of-range values.
+ */
+const char* bladerf_image_type_to_string(bladerf_image_type type);
+
+/**
+ * Write a flash image to a file.
+ *
+ * This function will fill in the checksum field before writing the contents to
+ * the specified file. The user-supplied contents of this field are ignored.
+ *
+ * @pre `image` has been initialized using bladerf_alloc_image()
+ * @post `image->checksum` will be populated if this function succeeds
+ *
+ * @param[in] dev Device handle
+ * @param[in] image Flash image
+ * @param[in] file File to write the flash image to
+ *
+ * @return 0 upon success, or a value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_image_write(struct bladerf *dev,
+ struct bladerf_image *image,
+ const char *file);
+
+/**
+ * Read flash image from a file.
+ *
+ * @pre The `image` parameter has been obtained via a call to
+ * bladerf_alloc_image(), with a `length` of 0.
+ *
+ * @post The `image` fields will be populated upon success, overwriting any
+ * previous values.
+ *
+ * @note The contents of the `image` parameter should not be used if this
+ * function fails.
+ *
+ * @param[out] image Flash image structure to populate.
+ * @param[in] file File to read image from.
+ *
+ * @return 0 upon success,
+ * ::BLADERF_ERR_CHECKSUM upon detecting a checksum mismatch,
+ * ::BLADERF_ERR_INVAL if any image fields are invalid,
+ * ::BLADERF_ERR_IO on a file I/O error,
+ * or a value from \ref RETCODES list on any other failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_image_read(struct bladerf_image *image, const char *file);
+
+/** @} (End of FN_IMAGE) */
+
+/**
+ * @defgroup FN_LOW_LEVEL Low-level Functions
+ *
+ * This section defines low-level APIs.
+ *
+ * @{
+ */
+
+/**
+ * @defgroup FN_VCTCXO_TAMER VCTCXO Tamer Mode
+ *
+ * This group provides routines for controlling the VTCTXO tamer.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * VCTCXO Tamer mode selection
+ *
+ * These values control the use of header J71 pin 1 for taming the
+ * on-board VCTCXO to improve or sustain frequency accuracy.
+ *
+ * When supplying input into the VCTCXO tamer, a 1.8V signal must be provided.
+ *
+ * @warning IMPORTANT: Exceeding 1.8V on J71-1 can damage the associated FPGA
+ * I/O bank. Ensure that you provide only a 1.8V signal!
+ */
+typedef enum {
+ /** Denotes an invalid selection or state */
+ BLADERF_VCTCXO_TAMER_INVALID = -1,
+
+ /** Do not attempt to tame the VCTCXO with an input source. */
+ BLADERF_VCTCXO_TAMER_DISABLED = 0,
+
+ /** Use a 1 pps input source to tame the VCTCXO. */
+ BLADERF_VCTCXO_TAMER_1_PPS = 1,
+
+ /** Use a 10 MHz input source to tame the VCTCXO. */
+ BLADERF_VCTCXO_TAMER_10_MHZ = 2
+} bladerf_vctcxo_tamer_mode;
+
+/**
+ * Set the VCTCXO tamer mode.
+ *
+ * @param dev Device handle
+ * @param[in] mode VCTCXO taming mode
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode mode);
+
+/**
+ * Get the current VCTCXO tamer mode
+ *
+ * @param dev Device handle
+ * @param[out] mode Current VCTCXO taming mode or
+ * ::BLADERF_VCTCXO_TAMER_INVALID if a failure
+ * occurs.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode *mode);
+
+/** @} (End of FN_VCTCXO_TAMER) */
+
+/**
+ * @defgroup FN_VCTCXO_TRIM_DAC VCTCXO Trim DAC
+ *
+ * These functions provide the ability to manipulate the VCTCXO Trim DAC.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * Query a device's VCTCXO calibration trim
+ *
+ * @param dev Device handle
+ * @param[out] trim VCTCXO calibration trim
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_vctcxo_trim(struct bladerf *dev, uint16_t *trim);
+
+/**
+ * Write value to VCTCXO trim DAC.
+ *
+ * @note This should not be used when the VCTCXO tamer is enabled.
+ *
+ * @param dev Device handle
+ * @param[in] val Desired VCTCXO trim DAC value
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_trim_dac_write(struct bladerf *dev, uint16_t val);
+
+/**
+ * Read value from VCTCXO trim DAC.
+ *
+ * This is similar to bladerf_get_vctcxo_trim(), except that it returns the
+ * current trim DAC value, as opposed to the calibration value read from flash.
+ *
+ * Use this if you are trying to query the value after having previously made
+ * calls to bladerf_trim_dac_write().
+ *
+ * @param dev Device handle
+ * @param[out] val Current VCTCXO trim DAC value
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_trim_dac_read(struct bladerf *dev, uint16_t *val);
+
+/** @} (End of FN_VCTCXO_TRIM_DAC) */
+
+/**
+ * @defgroup FN_TUNING_MODE Tuning Mode
+ *
+ * These functions provide the ability to select the tuning mode.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * Frequency tuning modes
+ *
+ * The default tuning mode, `BLADERF_TUNING_MODE_HOST`, can be overridden by
+ * setting a BLADERF_DEFAULT_TUNING_MODE environment variable to `host` or `fpga`.
+ *
+ * ::BLADERF_TUNING_MODE_HOST is the default tuning mode.
+ *
+ * ::BLADERF_TUNING_MODE_FPGA requirements:
+ * - libbladeRF >= v1.3.0
+ * - FPGA >= v0.2.0
+ *
+ * @note Overriding this value with a mode not supported by the FPGA will result
+ * in failures or unexpected behavior.
+ */
+typedef enum {
+ /** Indicates an invalid mode is set */
+ BLADERF_TUNING_MODE_INVALID = -1,
+
+ /**
+ * Perform tuning algorithm on the host. This is slower, but provides
+ * easier accessiblity to diagnostic information.
+ */
+ BLADERF_TUNING_MODE_HOST,
+
+ /** Perform tuning algorithm on the FPGA for faster tuning. */
+ BLADERF_TUNING_MODE_FPGA,
+} bladerf_tuning_mode;
+
+/**
+ * Set the device's tuning mode
+ *
+ * @param dev Device handle
+ * @param[in] mode Desired tuning mode. Note that the available modes
+ * depends on the FPGA version.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_tuning_mode(struct bladerf *dev,
+ bladerf_tuning_mode mode);
+
+/**
+ * Get the device's current tuning mode
+ *
+ * @param dev Device handle
+ * @param[in] mode Tuning mode
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_tuning_mode(struct bladerf *dev,
+ bladerf_tuning_mode *mode);
+
+/** @} (End of FN_TUNING_MODE) */
+
+/**
+ * @defgroup FN_TRIGGER_CONTROL Trigger Control
+ *
+ * These functions provide the ability to read and write the trigger control
+ * registers.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * Trigger control register "Arm" bit
+ *
+ * This bit arms (i.e., enables) the trigger controller when set to 1. Samples
+ * will be gated until the "Fire" bit has been asserted.
+ *
+ * A 0 in this bit disables the trigger controller. Samples will continue to
+ * flow as they normally do in this state.
+ */
+#define BLADERF_TRIGGER_REG_ARM ((uint8_t)(1 << 0))
+
+/**
+ * Trigger control register "Fire" bit
+ *
+ * For a master, this bit causes a trigger to be sent to all slave devices. Once
+ * this trigger is received (the master "receives" it immediately as well),
+ * devices begin streaming samples.
+ *
+ * This bit has no effect on slave devices.
+ */
+#define BLADERF_TRIGGER_REG_FIRE ((uint8_t)(1 << 1))
+
+/**
+ * Trigger control register "Master" bit
+ *
+ * Selects whether the device is a trigger master (1) or trigger slave (0). The
+ * trigger master drives the trigger signal as an output.
+ *
+ * Slave devices configure the trigger signal as an input.
+ */
+#define BLADERF_TRIGGER_REG_MASTER ((uint8_t)(1 << 2))
+
+/**
+ * Trigger control registers "line" bit
+ *
+ * This is a read-only register bit that denotes the current state of the the
+ * trigger signal.
+ */
+#define BLADERF_TRIGGER_REG_LINE ((uint8_t)(1 << 3))
+
+/**
+ * Read trigger control register
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] signal Trigger signal (control register) to read from
+ * @param[out] val Pointer to variable that register is read into See
+ * the BLADERF_TRIGGER_REG_* macros for the meaning of
+ * each bit.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_read_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal signal,
+ uint8_t *val);
+
+/**
+ * Write trigger control register
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] signal Trigger signal to configure
+ * @param[in] val Data to write into the trigger control register.
+ * See the BLADERF_TRIGGER_REG_* macros for options.
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_write_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal signal,
+ uint8_t val);
+
+/** @} (End of FN_TRIGGER_CONTROL) */
+
+/**
+ * @defgroup FN_WISHBONE_MASTER Wishbone bus master
+ *
+ * These functions provide the ability to read and write to the wishbone peripheral bus,
+ * which is reserved for modem
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * Read a specific Wishbone Master address
+ *
+ * @param dev Device handle
+ * @param addr Wishbone Master address
+ * @param[out] data Wishbone Master data
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_wishbone_master_read(struct bladerf *dev, uint32_t addr, uint32_t *data);
+
+/**
+ * Write value to a specific Wishbone Master address
+ *
+ *
+ * @param dev Device handle
+ * @param addr Wishbone Master address
+ * @param data Wishbone Master data
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_wishbone_master_write(struct bladerf *dev, uint32_t addr, uint32_t val);
+
+/** @} (End of FN_WISHBONE_MASTER) */
+
+
+/**
+ * @defgroup FN_CONFIG_GPIO Configuration GPIO
+ *
+ * These functions provide the ability to read and write the configuration
+ * GPIO.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * Read the configuration GPIO register.
+ *
+ * @param dev Device handle
+ * @param[out] val Current configuration GPIO value
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_config_gpio_read(struct bladerf *dev, uint32_t *val);
+
+/**
+ * Write the configuration GPIO register.
+ *
+ * @note Callers should be sure to perform a read-modify-write sequence to
+ * avoid accidentally clearing other GPIO bits that may be set by the
+ * library internally.
+ *
+ * @param dev Device handle
+ * @param[out] val Desired configuration GPIO value
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_config_gpio_write(struct bladerf *dev, uint32_t val);
+
+/** @} (End of FN_CONFIG_GPIO) */
+
+/**
+ * @defgroup FN_SPI_FLASH SPI Flash
+ *
+ * These functions provide the ability to erase, read, and write the SPI flash.
+ *
+ * @warning Use of SPI flash functions requires an understanding of the
+ * underlying SPI flash device, and the bladeRF's flash memory map. Be
+ * sure to review the following page and the associated flash datasheet
+ * before using these functions:
+ * https://github.com/nuand/bladeRF/wiki/FX3-Firmware#spi-flash-layout
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * Erase regions of the bladeRF's SPI flash
+ *
+ * @note This function operates in units of 64 KiB erase blocks
+ * @note Not recommended for new designs. Consider using the
+ * `bladerf_erase_flash_bytes()` function instead. It will perform the
+ * necessary conversion from bytes to pages based on the specific
+ * flash architecture found on the board.
+ *
+ * @param dev Device handle
+ * @param[in] erase_block Erase block from which to start erasing
+ * @param[in] count Number of blocks to erase
+ *
+ * @return 0 on success,
+ * or ::BLADERF_ERR_INVAL on an invalid `erase_block` or `count` value,
+ * or a value from \ref RETCODES list on other failures
+ */
+API_EXPORT
+int CALL_CONV bladerf_erase_flash(struct bladerf *dev,
+ uint32_t erase_block,
+ uint32_t count);
+
+/**
+ * Erase regions of the bladeRF's SPI flash
+ *
+ * @note This function operates in units of bytes
+ *
+ * @param dev Device handle
+ * @param[in] address Address at which to start erasing
+ * @param[in] length Number of bytes to erase
+ *
+ * @return 0 on success,
+ * or ::BLADERF_ERR_INVAL on an invalid `address` or `length` value,
+ * or a value from \ref RETCODES list on other failures
+ */
+API_EXPORT
+int CALL_CONV bladerf_erase_flash_bytes(struct bladerf *dev,
+ uint32_t address,
+ uint32_t length);
+
+/**
+ * Read data from the bladeRF's SPI flash
+ *
+ * @note This function operates in units of flash pages.
+ * @note Not recommended for new designs. Consider using the
+ * `bladerf_read_flash_bytes()` function instead. It will perform the
+ * necessary conversion from bytes to pages based on the specific
+ * flash architecture found on the board.
+ *
+ * @param dev Device handle
+ * @param[in] buf Buffer to read data into. Must be `count` *
+ * flash-page-size bytes or larger.
+ * @param[in] page Page to begin reading from
+ * @param[in] count Number of pages to read
+ *
+ * @return 0 on success,
+ * or ::BLADERF_ERR_INVAL on an invalid `page` or `count` value,
+ * or a value from \ref RETCODES list on other failures.
+ */
+API_EXPORT
+int CALL_CONV bladerf_read_flash(struct bladerf *dev,
+ uint8_t *buf,
+ uint32_t page,
+ uint32_t count);
+
+/**
+ * Read data from the bladeRF's SPI flash
+ *
+ * @note This function operates in units of bytes.
+ *
+ * @param dev Device handle
+ * @param[in] buf Buffer to read data into. Must be `bytes`
+ * bytes or larger.
+ * @param[in] address Address to begin reading from
+ * @param[in] bytes Number of bytes to read
+ *
+ * @return 0 on success,
+ * or ::BLADERF_ERR_INVAL on an invalid `address` or `bytes` value,
+ * or a value from \ref RETCODES list on other failures.
+ */
+API_EXPORT
+int CALL_CONV bladerf_read_flash_bytes(struct bladerf *dev,
+ uint8_t *buf,
+ uint32_t address,
+ uint32_t bytes);
+
+/**
+ * Write data to the bladeRF's SPI flash device
+ *
+ * @note This function operates in units of flash pages.
+ * @note Not recommended for new designs. Consider using the
+ * `bladerf_write_flash_bytes()` function instead. It will perform the
+ * necessary conversion from bytes to pages based on the specific
+ * flash architecture found on the board.
+ *
+ * @param dev Device handle
+ * @param[in] buf Data to write to flash
+ * @param[in] page Page to begin writing at
+ * @param[in] count Number of pages to write
+ *
+ * @return 0 on success,
+ * or ::BLADERF_ERR_INVAL on an invalid `page` or `count` value,
+ * or a value from \ref RETCODES list on other failures.
+ */
+API_EXPORT
+int CALL_CONV bladerf_write_flash(struct bladerf *dev,
+ const uint8_t *buf,
+ uint32_t page,
+ uint32_t count);
+
+/**
+ * Write data to the bladeRF's SPI flash device
+ *
+ * @note This function operates in units of bytes.
+ *
+ * @param dev Device handle
+ * @param[in] buf Data to write to flash
+ * @param[in] address Address to begin writing at
+ * @param[in] length Number of bytes to write
+ *
+ * @return 0 on success,
+ * or ::BLADERF_ERR_INVAL on an invalid `address` or `length` value,
+ * or a value from \ref RETCODES list on other failures.
+ */
+API_EXPORT
+int CALL_CONV bladerf_write_flash_bytes(struct bladerf *dev,
+ const uint8_t *buf,
+ uint32_t address,
+ uint32_t length);
+
+/**
+ * Lock the bladeRF's OTP
+ *
+ * @param dev Device handle
+ *
+ * @return 0 on success,
+ * or ::BLADERF_ERR_INVAL on an invalid `page` or `count` value,
+ * or a value from \ref RETCODES list on other failures.
+ */
+
+API_EXPORT
+int CALL_CONV bladerf_lock_otp(struct bladerf *dev);
+
+/**
+ * Read data from the bladeRF's SPI flash OTP
+ *
+ * @note This function operates solely on the first 256 byte page of the OTP
+ *
+ * @param dev Device handle
+ * @param[in] buf Buffer to read OTP data into
+ *
+ * @return 0 on success,
+ * or ::BLADERF_ERR_INVAL on an invalid `page` or `count` value,
+ * or a value from \ref RETCODES list on other failures.
+ */
+API_EXPORT
+int CALL_CONV bladerf_read_otp(struct bladerf *dev,
+ uint8_t *buf);
+
+/**
+ * Write data to the bladeRF's SPI flash OTP device
+ *
+ * @note This function operates solely on the first 256 byte page of the OTP
+ *
+ * @param dev Device handle
+ * @param[in] buf Data to write to OTP
+ *
+ * @return 0 on success,
+ * or ::BLADERF_ERR_INVAL on an invalid `page` or `count` value,
+ * or a value from \ref RETCODES list on other failures.
+ */
+API_EXPORT
+int CALL_CONV bladerf_write_otp(struct bladerf *dev,
+ uint8_t *buf);
+
+/** @} (End of FN_SPI_FLASH) */
+
+/**
+ * @defgroup FN_RF_PORTS RF Ports
+ *
+ * These functions provide the ability to select various RF ports for RX and TX
+ * channels. This is normally handled automatically.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * Set the RF port
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] port RF port name
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_set_rf_port(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *port);
+
+/**
+ * Get the RF port
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] port RF port name
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_rf_port(struct bladerf *dev,
+ bladerf_channel ch,
+ const char **port);
+
+/**
+ * Get available RF ports
+ *
+ * This function may be called with `NULL` for `ports`, or 0 for `count`, to
+ * determine the number of RF ports.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] ports RF port names
+ * @param[out] count Number to populate
+ *
+ * @return Number of RF ports on success, value from \ref RETCODES list on
+ * failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_rf_ports(struct bladerf *dev,
+ bladerf_channel ch,
+ const char **ports,
+ unsigned int count);
+
+/** @} (End of FN_RF_PORTS) */
+
+/** @} (End of FN_LOW_LEVEL) */
+
+/**
+ * @defgroup FN_SF Features
+ *
+ * This group of functions provides the ability to set features available
+ * to bladeRF devices.
+ *
+ * @{
+ */
+
+/**
+ * Feature Set
+ */
+typedef enum {
+ BLADERF_FEATURE_DEFAULT = 0, /**< No feature enabled */
+ BLADERF_FEATURE_OVERSAMPLE /**< Enforces AD9361 OC and 8bit mode */
+} bladerf_feature;
+
+/**
+ * Enables a feature.
+ *
+ * @param dev Device handle
+ * @param[out] feature Feature
+ * @param[in] enable true to enable, false to disable
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_enable_feature(struct bladerf *dev,
+ bladerf_feature feature,
+ bool enable);
+
+/**
+ * Gets currently enabled feature.
+ *
+ * @param dev Device handle
+ * @param[out] feature Feature
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_feature(struct bladerf *dev,
+ bladerf_feature* feature);
+
+/** @} (End of FN_SF) */
+
+/**
+ * @defgroup FN_CAL Gain Calibration
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * @brief Individual gain calibration entry. Each entry associates a frequency
+ * with a corresponding gain correction factor.
+ */
+struct bladerf_gain_cal_entry {
+ bladerf_frequency freq; /**< Frequency (Hz) */
+ double gain_corr; /**< Gain correction factor */
+};
+
+/**
+ * @brief Gain calibration table. The table contains a series of
+ * entries, each associating a frequency with a gain correction factor. Entries
+ * are sorted by frequency, from start_freq to stop_freq.
+ */
+struct bladerf_gain_cal_tbl {
+ struct bladerf_version version; /**< Table format version */
+ bladerf_channel ch; /**< Channel */
+ bool enabled; /**< Whether gain calibration is enabled. */
+ uint32_t n_entries; /**< Number of entries */
+ bladerf_frequency start_freq; /**< Start frequency (Hz) */
+ bladerf_frequency stop_freq; /**< Stop frequency (Hz) */
+ struct bladerf_gain_cal_entry *entries; /**< Sorted calibration entries */
+ bladerf_gain gain_target; /**< Compensated gain */
+ size_t file_path_len; /**< Length of the file path string. */
+ char *file_path; /**< Path to the file from which the table was loaded. */
+ enum gain_cal_state {
+ BLADERF_GAIN_CAL_UNINITIALIZED,
+ BLADERF_GAIN_CAL_LOADED,
+ BLADERF_GAIN_CAL_UNLOADED
+ } state; /**< Calibration state */
+};
+
+
+/**
+ * @brief Loads and applies gain calibration for a specified channel from a
+ * file.
+ *
+ * This function adjusts the specified channel's gain settings on the bladeRF
+ * device using calibration data from the provided file path. It supports
+ * calibration files in CSV format, automatically converting them to binary
+ * format as required by the device. This ensures the device operates with
+ * optimized gain settings across its frequency range. The operation is
+ * protected by mutex locks to maintain thread safety.
+ *
+ * @param[in] dev Pointer to the initialized bladeRF device.
+ * @param[in] ch The target channel (RX or TX) for gain calibration.
+ * @param[in] cal_file_loc Path to the calibration file, either in CSV or binary
+ * format. CSV files are converted to binary format during the process.
+ *
+ * @return 0 on success, indicating that the calibration data was successfully
+ * applied to the channel. Returns BLADERF_ERR_UNSUPPORTED if the device or
+ * channel does not support gain calibration. Other BLADERF_ERR_* error codes
+ * indicate specific failures, such as file access issues or conversion errors.
+ */
+API_EXPORT
+int CALL_CONV bladerf_load_gain_calibration(struct bladerf *dev,
+ bladerf_channel ch,
+ const char* cal_file_loc);
+
+/**
+ * @brief Displays gain calibration details for a specified channel.
+ *
+ * Outputs the gain calibration information to the console. The level of detail
+ * is adjustable via `with_entries`.
+ *
+ * @note This operation is thread-safe.
+ *
+ * @param[in] dev Non-NULL pointer to a bladeRF device.
+ * @param[in] ch Channel to display gain calibration for. Use
+ * `bladerf_channel`.
+ * @param[in] with_entries Set to `true` to print all calibration entries, or
+ * `false` for a summary only.
+ *
+ * @return 0 on success, BLADERF_ERR_UNSUPPORTED if calibration is not supported
+ * on the device, or other BLADERF_ERR_* codes for different failures.
+ */
+API_EXPORT
+int CALL_CONV bladerf_print_gain_calibration(struct bladerf *dev,
+ bladerf_channel ch,
+ bool with_entries);
+
+/**
+ * @brief Toggles gain calibration for a specified channel.
+ *
+ * Enables or disables automatic gain adjustment based on a preloaded
+ * calibration table for the specified channel. This operation is thread-safe.
+
+ *
+ * @note Gain calibration mode persists until device reset or power cycle.
+ * Ensure a calibration table is loaded before enabling.
+
+ *
+ * @param[in] dev Non-NULL pointer to the bladeRF device.
+ * @param[in] ch Channel (`BLADERF_CHANNEL_RX(0)`, `BLADERF_CHANNEL_TX(0)`,
+ * etc.) to set gain calibration for.
+ * @param[in] en `true` to enable or `false` to disable gain calibration.
+ *
+ * @return 0 on success, or a `BLADERF_ERR_*` code on failure (e.g., if
+ * calibration table is not initialized).
+ */
+API_EXPORT
+int CALL_CONV bladerf_enable_gain_calibration(struct bladerf *dev,
+ bladerf_channel ch,
+ bool en);
+
+/**
+ * @brief Provides read-only access to a channel's gain calibration table.
+ *
+ * Returns a read-only pointer to a specified channel's gain calibration table,
+ * preventing modification. Access is thread-safe, protected by device mutexes.
+ *
+ * @param[in] dev Non-NULL pointer to a BladeRF device structure.
+ * @param[in] ch Channel to retrieve the gain calibration table for.
+ * @param[out] tbl On success, updated to point to the read-only gain
+ * calibration table.
+ *
+ * @return 0 on success, BLADERF_ERR_UNEXPECTED if the table is not loaded, or
+ * BLADERF_ERR_INVAL for invalid inputs.
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_gain_calibration(struct bladerf *dev, bladerf_channel ch, const struct bladerf_gain_cal_tbl **tbl);
+
+/**
+ * @brief Computes the gain target for a specified channel, incorporating
+ * calibration corrections.
+ *
+ * For a channel, this calculates the target gain considering the current gain
+ * setting and calibration corrections, if available. In MGC mode, it returns
+ * the target gain from the calibration table. In AGC mode, it adjusts the
+ * target based on the calibration correction for the current frequency.
+ *
+ * @note Access to device and calibration data is thread-safe.
+ *
+ * @param[in] dev Non-NULL pointer to a BladeRF device.
+ * @param[in] ch Channel (RX/TX) for querying the gain target.
+ * @param[out] gain_target Where to store the computed gain target. Reflects
+ * current gain and calibration corrections.
+ *
+ * @return 0 if successful, with `gain_target` updated. On failure, returns
+ * BLADERF_ERR_UNEXPECTED if calibration is uninitialized, or other error codes
+ * for different failures.
+ */
+API_EXPORT
+int CALL_CONV bladerf_get_gain_target(struct bladerf *dev, bladerf_channel ch, int *gain_target);
+
+/** @} (End of FN_CAL) */
+
+/**
+ * @defgroup FN_XB Expansion board support
+ *
+ * This group of functions provides the ability to attach and detach expansion
+ * boards.
+ *
+ * In general, one should call bladerf_expansion_attach() immediately after
+ * opening the device.
+ *
+ * @note Hotplug and expansion board removal is not supported. It is expected
+ * that the expansion boards are attached at power-on and remain attached
+ * until power is removed.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * Expansion boards
+ */
+typedef enum {
+ BLADERF_XB_NONE = 0, /**< No expansion boards attached */
+ BLADERF_XB_100, /**< XB-100 GPIO expansion board.
+ * This device is not yet supported in
+ * libbladeRF, and is here as a placeholder
+ * for future support. */
+ BLADERF_XB_200, /**< XB-200 Transverter board */
+ BLADERF_XB_300 /**< XB-300 Amplifier board */
+} bladerf_xb;
+
+/**
+ * Attach and enable an expansion board's features
+ *
+ * @param dev Device handle
+ * @param[in] xb Expansion board
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_expansion_attach(struct bladerf *dev, bladerf_xb xb);
+
+/**
+ * Determine which expansion board is attached
+ *
+ * @param dev Device handle
+ * @param[out] xb Expansion board
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+API_EXPORT
+int CALL_CONV bladerf_expansion_get_attached(struct bladerf *dev,
+ bladerf_xb *xb);
+
+/** @} (End of FN_XB) */
+
+/**
+ * @defgroup FN_LOGGING Logging
+ *
+ * This section contains various helper/utility functions for library logging
+ * facilities.
+ *
+ * These functions are thread-safe.
+ *
+ * @{
+ */
+
+/**
+ * Severity levels for logging functions
+ */
+typedef enum {
+ BLADERF_LOG_LEVEL_VERBOSE, /**< Verbose level logging */
+ BLADERF_LOG_LEVEL_DEBUG, /**< Debug level logging */
+ BLADERF_LOG_LEVEL_INFO, /**< Information level logging */
+ BLADERF_LOG_LEVEL_WARNING, /**< Warning level logging */
+ BLADERF_LOG_LEVEL_ERROR, /**< Error level logging */
+ BLADERF_LOG_LEVEL_CRITICAL, /**< Fatal error level logging */
+ BLADERF_LOG_LEVEL_SILENT /**< No output */
+} bladerf_log_level;
+
+/**
+ * Sets the filter level for displayed log messages.
+ *
+ * Messages that are at or above the specified log level will be printed, while
+ * messages with a lower log level will be suppressed.
+ *
+ * @param[in] level The new log level filter value
+ */
+API_EXPORT
+void CALL_CONV bladerf_log_set_verbosity(bladerf_log_level level);
+
+/** @} (End of FN_LOGGING) */
+
+/**
+ * @defgroup FN_LIBRARY_VERSION Library version
+ *
+ * @{
+ */
+
+/**
+ * Get libbladeRF version information
+ *
+ * @param[out] version libbladeRF version information
+ */
+API_EXPORT
+void CALL_CONV bladerf_version(struct bladerf_version *version);
+
+/** @} (End of FN_LIBRARY_VERSION) */
+
+/**
+ * @defgroup RETCODES Error codes
+ *
+ * bladeRF library routines return negative values to indicate errors.
+ * Values >= 0 are used to indicate success.
+ *
+ * @code
+ * int status = bladerf_set_gain(dev, BLADERF_CHANNEL_RX(0), 2);
+ *
+ * if (status < 0) {
+ * handle_error();
+ * }
+ * @endcode
+ *
+ * @{
+ */
+// clang-format off
+#define BLADERF_ERR_UNEXPECTED (-1) /**< An unexpected failure occurred */
+#define BLADERF_ERR_RANGE (-2) /**< Provided parameter is out of range */
+#define BLADERF_ERR_INVAL (-3) /**< Invalid operation/parameter */
+#define BLADERF_ERR_MEM (-4) /**< Memory allocation error */
+#define BLADERF_ERR_IO (-5) /**< File/Device I/O error */
+#define BLADERF_ERR_TIMEOUT (-6) /**< Operation timed out */
+#define BLADERF_ERR_NODEV (-7) /**< No device(s) available */
+#define BLADERF_ERR_UNSUPPORTED (-8) /**< Operation not supported */
+#define BLADERF_ERR_MISALIGNED (-9) /**< Misaligned flash access */
+#define BLADERF_ERR_CHECKSUM (-10) /**< Invalid checksum */
+#define BLADERF_ERR_NO_FILE (-11) /**< File not found */
+#define BLADERF_ERR_UPDATE_FPGA (-12) /**< An FPGA update is required */
+#define BLADERF_ERR_UPDATE_FW (-13) /**< A firmware update is requied */
+#define BLADERF_ERR_TIME_PAST (-14) /**< Requested timestamp is in the past */
+#define BLADERF_ERR_QUEUE_FULL (-15) /**< Could not enqueue data into
+ * full queue */
+#define BLADERF_ERR_FPGA_OP (-16) /**< An FPGA operation reported failure */
+#define BLADERF_ERR_PERMISSION (-17) /**< Insufficient permissions for the
+ * requested operation */
+#define BLADERF_ERR_WOULD_BLOCK (-18) /**< Operation would block, but has been
+ * requested to be non-blocking. This
+ * indicates to a caller that it may
+ * need to retry the operation later.
+ */
+#define BLADERF_ERR_NOT_INIT (-19) /**< Device insufficiently initialized
+ * for operation */
+// clang-format on
+
+/**
+ * Obtain a textual description of a value from the \ref RETCODES list
+ *
+ * @param[in] error Error value to look up
+ *
+ * @return Error string
+ */
+API_EXPORT
+const char *CALL_CONV bladerf_strerror(int error);
+
+/** @} (End RETCODES) */
+
+#include "bladeRF1.h"
+#include "bladeRF2.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBBLADERF_H_ */
diff --git a/Radio/HW/BladeRF/src/backend/backend.c b/Radio/HW/BladeRF/src/backend/backend.c
new file mode 100644
index 0000000..28b5a6d
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/backend.c
@@ -0,0 +1,172 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+
+#include "rel_assert.h"
+#include "log.h"
+
+#include "devinfo.h"
+
+#include "backend/backend.h"
+#include "backend/backend_config.h"
+
+static const struct backend_fns *backend_list[] = BLADERF_BACKEND_LIST;
+
+int open_with_any_backend(struct bladerf *dev,
+ struct bladerf_devinfo *info)
+{
+ size_t i;
+ int status = BLADERF_ERR_NODEV;
+ const size_t n_backends = ARRAY_SIZE(backend_list);
+
+ for (i = 0; i < n_backends && status != 0; i++) {
+ status = backend_list[i]->open(dev, info);
+ }
+
+ return status;
+}
+
+int backend_open(struct bladerf *dev, struct bladerf_devinfo *info)
+{
+ size_t i;
+ int status = BLADERF_ERR_NODEV;
+ const size_t n_backends = ARRAY_SIZE(backend_list);
+
+ if (info->backend == BLADERF_BACKEND_ANY) {
+ status = open_with_any_backend(dev, info);
+ } else {
+ for (i = 0; i < n_backends; i++) {
+ if (backend_list[i]->matches(info->backend)) {
+ status = backend_list[i]->open(dev, info);
+ break;
+ }
+ }
+ }
+
+ return status;
+}
+
+int backend_probe(backend_probe_target probe_target,
+ struct bladerf_devinfo **devinfo_items, size_t *num_items)
+{
+ int status;
+ int first_backend_error = 0;
+ struct bladerf_devinfo_list list;
+ size_t i;
+ const size_t n_backends = ARRAY_SIZE(backend_list);
+
+ *devinfo_items = NULL;
+ *num_items = 0;
+
+ status = bladerf_devinfo_list_init(&list);
+ if (status != 0) {
+ log_debug("Failed to initialize devinfo list: %s\n",
+ bladerf_strerror(status));
+ return status;
+ }
+
+ for (i = 0; i < n_backends; i++) {
+ status = backend_list[i]->probe(probe_target, &list);
+
+ if (status < 0 && status != BLADERF_ERR_NODEV) {
+ log_debug("Probe failed on backend %d: %s\n",
+ i, bladerf_strerror(status));
+
+ if (!first_backend_error) {
+ first_backend_error = status;
+ }
+ }
+ }
+
+ *num_items = list.num_elt;
+
+ if (*num_items != 0) {
+ *devinfo_items = list.elt;
+ } else {
+ /* For no items, we end up passing back a NULL list to the
+ * API caller, so we'll just free this up now */
+ free(list.elt);
+
+ /* Report the first error that occurred if we couldn't find anything */
+ status =
+ first_backend_error == 0 ? BLADERF_ERR_NODEV : first_backend_error;
+ }
+
+ return status;
+}
+
+int backend_load_fw_from_bootloader(bladerf_backend backend,
+ uint8_t bus, uint8_t addr,
+ struct fx3_firmware *fw)
+{
+ int status = BLADERF_ERR_NODEV;
+ size_t i;
+ const size_t n_backends = ARRAY_SIZE(backend_list);
+
+ for (i = 0; i < n_backends; i++) {
+ if (backend_list[i]->matches(backend)) {
+ status = backend_list[i]->load_fw_from_bootloader(backend, bus,
+ addr, fw);
+ break;
+ }
+ }
+
+ return status;
+}
+
+const char *backend2str(bladerf_backend backend)
+{
+ switch (backend) {
+ case BLADERF_BACKEND_LIBUSB:
+ return BACKEND_STR_LIBUSB;
+
+ case BLADERF_BACKEND_LINUX:
+ return BACKEND_STR_LINUX;
+
+ case BLADERF_BACKEND_CYPRESS:
+ return BACKEND_STR_CYPRESS;
+
+ default:
+ return BACKEND_STR_ANY;
+ }
+}
+
+int str2backend(const char *str, bladerf_backend *backend)
+{
+ int status = 0;
+
+ if (!strcasecmp(BACKEND_STR_LIBUSB, str)) {
+ *backend = BLADERF_BACKEND_LIBUSB;
+ } else if (!strcasecmp(BACKEND_STR_LINUX, str)) {
+ *backend = BLADERF_BACKEND_LINUX;
+ } else if (!strcasecmp(BACKEND_STR_CYPRESS, str)) {
+ *backend = BLADERF_BACKEND_CYPRESS;
+ } else if (!strcasecmp(BACKEND_STR_ANY, str)) {
+ *backend = BLADERF_BACKEND_ANY;
+ } else {
+ log_debug("Invalid backend: %s\n", str);
+ status = BLADERF_ERR_INVAL;
+ *backend = BLADERF_BACKEND_ANY;
+ }
+
+ return status;
+}
diff --git a/Radio/HW/BladeRF/src/backend/backend.h b/Radio/HW/BladeRF/src/backend/backend.h
new file mode 100644
index 0000000..e5e6d2c
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/backend.h
@@ -0,0 +1,364 @@
+/**
+ * @file backend.h
+ *
+ * @brief This file defines the generic interface to bladeRF backends
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef BACKEND_BACKEND_H_
+#define BACKEND_BACKEND_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#include <libbladeRF.h>
+
+#include "logger_entry.h"
+
+#define BACKEND_STR_ANY "*"
+#define BACKEND_STR_LIBUSB "libusb"
+#define BACKEND_STR_LINUX "linux"
+#define BACKEND_STR_CYPRESS "cypress"
+
+/**
+ * Specifies what to probe for
+ */
+typedef enum {
+ BACKEND_PROBE_BLADERF,
+ BACKEND_PROBE_FX3_BOOTLOADER,
+} backend_probe_target;
+
+/**
+ * Backend protocol to use
+ */
+typedef enum {
+ BACKEND_FPGA_PROTOCOL_NIOSII_LEGACY,
+ BACKEND_FPGA_PROTOCOL_NIOSII,
+} backend_fpga_protocol;
+
+struct bladerf_devinfo_list;
+struct fx3_firmware;
+
+/**
+ * Backend-specific function table
+ *
+ * The backend is responsible for making calls to
+ * capabilities_init_pre_fpga_load() and capabilities_init_post_fpga_load() when
+ * opening a device and after loading an FPGA, or detecting an FPGA is already
+ * loaded.
+ *
+ */
+struct backend_fns {
+ /* Returns true if a backend supports the specified backend type,
+ * and false otherwise */
+ bool (*matches)(bladerf_backend backend);
+
+ /* Backends probe for devices and append entries to this list using
+ * bladerf_devinfo_list_append() */
+ int (*probe)(backend_probe_target probe_target,
+ struct bladerf_devinfo_list *info_list);
+
+ /* Get VID and PID of the device */
+ int (*get_vid_pid)(struct bladerf *dev, uint16_t *vid, uint16_t *pid);
+
+ /* Get flash manufacturer/device IDs */
+ int (*get_flash_id)(struct bladerf *dev, uint8_t *mid, uint8_t *did);
+
+ /* Opening device based upon specified device info. */
+ int (*open)(struct bladerf *dev, struct bladerf_devinfo *info);
+
+ /* Set the FPGA protocol */
+ int (*set_fpga_protocol)(struct bladerf *dev,
+ backend_fpga_protocol fpga_protocol);
+
+ /* Closing of the device and freeing of the data */
+ void (*close)(struct bladerf *dev);
+
+ /* Is firmware ready */
+ int (*is_fw_ready)(struct bladerf *dev);
+
+ /* Get handle */
+ int (*get_handle)(struct bladerf *dev, void **handle);
+
+ /* FPGA Loading and checking */
+ int (*load_fpga)(struct bladerf *dev,
+ const uint8_t *image,
+ size_t image_size);
+ int (*is_fpga_configured)(struct bladerf *dev);
+ bladerf_fpga_source (*get_fpga_source)(struct bladerf *dev);
+
+ /* Version checking */
+ int (*get_fw_version)(struct bladerf *dev, struct bladerf_version *version);
+ int (*get_fpga_version)(struct bladerf *dev,
+ struct bladerf_version *version);
+
+ /* Flash operations */
+
+ /* Erase the specified number of erase blocks */
+ int (*erase_flash_blocks)(struct bladerf *dev, uint32_t eb, uint16_t count);
+
+ /* Read the specified number of pages */
+ int (*read_flash_pages)(struct bladerf *dev,
+ uint8_t *buf,
+ uint32_t page,
+ uint32_t count);
+
+ /* Write the specified number of pages */
+ int (*write_flash_pages)(struct bladerf *dev,
+ const uint8_t *buf,
+ uint32_t page,
+ uint32_t count);
+
+ /* Device startup and reset */
+ int (*device_reset)(struct bladerf *dev);
+ int (*jump_to_bootloader)(struct bladerf *dev);
+
+ /* Platform information */
+ int (*get_cal)(struct bladerf *dev, char *cal);
+ int (*get_otp)(struct bladerf *dev, char *otp);
+ int (*write_otp)(struct bladerf *dev, char *otp);
+ int (*lock_otp)(struct bladerf *dev);
+ int (*get_device_speed)(struct bladerf *dev, bladerf_dev_speed *speed);
+
+ /* Configuration GPIO (NIOS II <-> logic) accessors */
+ int (*config_gpio_write)(struct bladerf *dev, uint32_t val);
+ int (*config_gpio_read)(struct bladerf *dev, uint32_t *val);
+
+ /* Expansion GPIO accessors */
+ int (*expansion_gpio_write)(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t val);
+ int (*expansion_gpio_read)(struct bladerf *dev, uint32_t *val);
+ int (*expansion_gpio_dir_write)(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t outputs);
+ int (*expansion_gpio_dir_read)(struct bladerf *dev, uint32_t *outputs);
+
+ /* IQ Correction Settings */
+ int (*set_iq_gain_correction)(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t value);
+ int (*set_iq_phase_correction)(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t value);
+ int (*get_iq_gain_correction)(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t *value);
+ int (*get_iq_phase_correction)(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t *value);
+
+ /* AGC DC Correction Lookup Table */
+ int (*set_agc_dc_correction)(struct bladerf *dev,
+ int16_t q_max,
+ int16_t i_max,
+ int16_t q_mid,
+ int16_t i_mid,
+ int16_t q_low,
+ int16_t i_low);
+
+ /* Get current timestamp counter values */
+ int (*get_timestamp)(struct bladerf *dev,
+ bladerf_direction dir,
+ uint64_t *value);
+
+ /* Si5338 accessors */
+ int (*si5338_write)(struct bladerf *dev, uint8_t addr, uint8_t data);
+ int (*si5338_read)(struct bladerf *dev, uint8_t addr, uint8_t *data);
+
+ /* LMS6002D accessors */
+ int (*lms_write)(struct bladerf *dev, uint8_t addr, uint8_t data);
+ int (*lms_read)(struct bladerf *dev, uint8_t addr, uint8_t *data);
+
+ /* INA219 accessors */
+ int (*ina219_write)(struct bladerf *dev, uint8_t addr, uint16_t data);
+ int (*ina219_read)(struct bladerf *dev, uint8_t addr, uint16_t *data);
+
+ /* AD9361 accessors */
+ int (*ad9361_spi_write)(struct bladerf *dev, uint16_t cmd, uint64_t data);
+ int (*ad9361_spi_read)(struct bladerf *dev, uint16_t cmd, uint64_t *data);
+
+ /* AD9361 accessors */
+ int (*adi_axi_write)(struct bladerf *dev, uint32_t addr, uint32_t data);
+ int (*adi_axi_read)(struct bladerf *dev, uint32_t addr, uint32_t *data);
+
+ /* Wishbone Master accessors */
+ int (*wishbone_master_write)(struct bladerf *dev, uint32_t addr, uint32_t data);
+ int (*wishbone_master_read)(struct bladerf *dev, uint32_t addr, uint32_t *data);
+
+ /* RFIC command accessors */
+ int (*rfic_command_write)(struct bladerf *dev, uint16_t cmd, uint64_t data);
+ int (*rfic_command_read)(struct bladerf *dev, uint16_t cmd, uint64_t *data);
+
+ /* RFFE control accessors */
+ int (*rffe_control_write)(struct bladerf *dev, uint32_t value);
+ int (*rffe_control_read)(struct bladerf *dev, uint32_t *value);
+
+ /* RFFE-to-Nios fast lock profile saver */
+ int (*rffe_fastlock_save)(struct bladerf *dev,
+ bool is_tx,
+ uint8_t rffe_profile,
+ uint16_t nios_profile);
+
+ /* AD56X1 VCTCXO Trim DAC accessors */
+ int (*ad56x1_vctcxo_trim_dac_write)(struct bladerf *dev, uint16_t value);
+ int (*ad56x1_vctcxo_trim_dac_read)(struct bladerf *dev, uint16_t *value);
+
+ /* ADF400X accessors */
+ int (*adf400x_write)(struct bladerf *dev, uint8_t addr, uint32_t data);
+ int (*adf400x_read)(struct bladerf *dev, uint8_t addr, uint32_t *data);
+
+ /* VCTCXO accessors */
+ int (*vctcxo_dac_write)(struct bladerf *dev, uint8_t addr, uint16_t value);
+ int (*vctcxo_dac_read)(struct bladerf *dev, uint8_t addr, uint16_t *value);
+
+ int (*set_vctcxo_tamer_mode)(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode mode);
+ int (*get_vctcxo_tamer_mode)(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode *mode);
+
+ /* Expansion board SPI */
+ int (*xb_spi)(struct bladerf *dev, uint32_t value);
+
+ /* Configure firmware loopback */
+ int (*set_firmware_loopback)(struct bladerf *dev, bool enable);
+ int (*get_firmware_loopback)(struct bladerf *dev, bool *is_enabled);
+
+ /* Sample stream */
+ int (*enable_module)(struct bladerf *dev,
+ bladerf_direction dir,
+ bool enable);
+
+ int (*init_stream)(struct bladerf_stream *stream, size_t num_transfers);
+ int (*stream)(struct bladerf_stream *stream, bladerf_channel_layout layout);
+ int (*submit_stream_buffer)(struct bladerf_stream *stream,
+ void *buffer,
+ size_t *length,
+ unsigned int timeout_ms,
+ bool nonblock);
+ void (*deinit_stream)(struct bladerf_stream *stream);
+
+ /* Schedule a frequency retune operation */
+ int (*retune)(struct bladerf *dev,
+ bladerf_channel ch,
+ uint64_t timestamp,
+ uint16_t nint,
+ uint32_t nfrac,
+ uint8_t freqsel,
+ uint8_t vcocap,
+ bool low_band,
+ uint8_t xb_gpio,
+ bool quick_tune);
+
+ /* Schedule a frequency retune2 operation */
+ int (*retune2)(struct bladerf *dev,
+ bladerf_channel ch,
+ uint64_t timestamp,
+ uint16_t nios_profile,
+ uint8_t rffe_profile,
+ uint8_t port,
+ uint8_t spdt);
+
+ /* Load firmware from FX3 bootloader */
+ int (*load_fw_from_bootloader)(bladerf_backend backend,
+ uint8_t bus,
+ uint8_t addr,
+ struct fx3_firmware *fw);
+
+ /* Read a log entry from the FX3 firmware */
+ int (*read_fw_log)(struct bladerf *dev, logger_entry *e);
+
+ /* Read and Write access to trigger registers */
+ int (*read_trigger)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t *val);
+ int (*write_trigger)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t val);
+
+ /* Backend name */
+ const char *name;
+};
+
+/**
+ * Open the device using the backend specified in the provided
+ * bladerf_devinfo structure.
+ *
+ * @param[in] device Device to fill in backend info for
+ * @param[in] info Filled-in device info
+ *
+ * @return 0 on success, BLADERF_ERR_* code on failure
+ */
+int backend_open(struct bladerf *dev, struct bladerf_devinfo *info);
+
+/**
+ * Probe for devices, filling in the provided devinfo list and size of
+ * the list that gets populated
+ *
+ * @param[in] probe_target Device type to probe for
+ * @param[out] devinfo_items Device info for identified devices
+ * @param[out] num_items Number of items in the devinfo list.
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int backend_probe(backend_probe_target probe_target,
+ struct bladerf_devinfo **devinfo_items,
+ size_t *num_items);
+
+/**
+ * Search for bootloader via provided specification, download firmware,
+ * and boot it.
+ *
+ * @param[in] backend Backend to use for this operation
+ * @param[in] bus USB bus the device is connected to
+ * @param[in] addr USB addr associated with the device
+ * @param[in] fw Firmware to load
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int backend_load_fw_from_bootloader(bladerf_backend backend,
+ uint8_t bus,
+ uint8_t addr,
+ struct fx3_firmware *fw);
+
+/**
+ * Convert a backend enumeration value to a string
+ *
+ * @param[in] backend Value to convert to a string
+ *
+ * @return A backend string for the associated enumeration value. An invalid
+ * value will yield the "ANY" wildcard.
+ */
+const char *backend2str(bladerf_backend backend);
+
+/**
+ * Convert a string to a backend type value
+ *
+ * @param[in] str Backend type, as a string.
+ * @param[out] backend Associated backend, on success
+ *
+ * @return 0 on success, BLADERF_ERR_INVAL on invalid type string
+ */
+int str2backend(const char *str, bladerf_backend *backend);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/backend/backend_config.h b/Radio/HW/BladeRF/src/backend/backend_config.h
new file mode 100644
index 0000000..351d5d1
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/backend_config.h
@@ -0,0 +1,88 @@
+/**
+ * @file backend_config.h
+ *
+ * @brief Compile-time backend selection
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef BACKEND_BACKEND_CONFIG_H_
+#define BACKEND_BACKEND_CONFIG_H_
+
+#define ENABLE_BACKEND_USB
+#define ENABLE_BACKEND_LIBUSB
+/* #undef ENABLE_BACKEND_CYAPI */
+/* #undef ENABLE_BACKEND_DUMMY */
+/* #undef ENABLE_BACKEND_LINUX_DRIVER */
+
+#include "backend/backend.h"
+#include "backend/usb/usb.h"
+
+#ifdef ENABLE_BACKEND_DUMMY
+extern const struct backend_fns backend_fns_dummy;
+#define BACKEND_DUMMY &backend_fns_dummy,
+#else
+#define BACKEND_DUMMY
+#endif
+
+#ifdef ENABLE_BACKEND_USB
+extern const struct backend_fns backend_fns_usb;
+#define BACKEND_USB &backend_fns_usb,
+
+#ifdef ENABLE_BACKEND_LIBUSB
+extern const struct usb_driver usb_driver_libusb;
+#define BACKEND_USB_LIBUSB &usb_driver_libusb,
+#else
+#define BACKEND_USB_LIBUSB
+#endif
+
+#ifdef ENABLE_BACKEND_CYAPI
+extern const struct usb_driver usb_driver_cypress;
+#define BACKEND_USB_CYAPI &usb_driver_cypress,
+#else
+#define BACKEND_USB_CYAPI
+#endif
+
+#define BLADERF_USB_BACKEND_LIST \
+ { \
+ BACKEND_USB_LIBUSB \
+ BACKEND_USB_CYAPI \
+ }
+
+#if !defined(ENABLE_BACKEND_LIBUSB) && !defined(ENABLE_BACKEND_CYAPI)
+#error "No USB backends are enabled. One or more must be enabled."
+#endif
+
+#else
+#define BACKEND_USB
+#endif
+
+#if !defined(ENABLE_BACKEND_USB) && !defined(ENABLE_BACKEND_DUMMY)
+#error "No backends are enabled. One more more must be enabled."
+#endif
+
+/* This list should be ordered by preference (highest first) */
+#define BLADERF_BACKEND_LIST \
+ { \
+ BACKEND_USB \
+ BACKEND_DUMMY \
+ }
+
+#endif
diff --git a/Radio/HW/BladeRF/src/backend/backend_config.h.in b/Radio/HW/BladeRF/src/backend/backend_config.h.in
new file mode 100644
index 0000000..15ba6f4
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/backend_config.h.in
@@ -0,0 +1,88 @@
+/**
+ * @file backend_config.h
+ *
+ * @brief Compile-time backend selection
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef BACKEND_BACKEND_CONFIG_H_
+#define BACKEND_BACKEND_CONFIG_H_
+
+#cmakedefine ENABLE_BACKEND_USB
+#cmakedefine ENABLE_BACKEND_LIBUSB
+#cmakedefine ENABLE_BACKEND_CYAPI
+#cmakedefine ENABLE_BACKEND_DUMMY
+#cmakedefine ENABLE_BACKEND_LINUX_DRIVER
+
+#include "backend/backend.h"
+#include "backend/usb/usb.h"
+
+#ifdef ENABLE_BACKEND_DUMMY
+extern const struct backend_fns backend_fns_dummy;
+#define BACKEND_DUMMY &backend_fns_dummy,
+#else
+#define BACKEND_DUMMY
+#endif
+
+#ifdef ENABLE_BACKEND_USB
+extern const struct backend_fns backend_fns_usb;
+#define BACKEND_USB &backend_fns_usb,
+
+#ifdef ENABLE_BACKEND_LIBUSB
+extern const struct usb_driver usb_driver_libusb;
+#define BACKEND_USB_LIBUSB &usb_driver_libusb,
+#else
+#define BACKEND_USB_LIBUSB
+#endif
+
+#ifdef ENABLE_BACKEND_CYAPI
+extern const struct usb_driver usb_driver_cypress;
+#define BACKEND_USB_CYAPI &usb_driver_cypress,
+#else
+#define BACKEND_USB_CYAPI
+#endif
+
+#define BLADERF_USB_BACKEND_LIST \
+ { \
+ BACKEND_USB_LIBUSB \
+ BACKEND_USB_CYAPI \
+ }
+
+#if !defined(ENABLE_BACKEND_LIBUSB) && !defined(ENABLE_BACKEND_CYAPI)
+#error "No USB backends are enabled. One or more must be enabled."
+#endif
+
+#else
+#define BACKEND_USB
+#endif
+
+#if !defined(ENABLE_BACKEND_USB) && !defined(ENABLE_BACKEND_DUMMY)
+#error "No backends are enabled. One more more must be enabled."
+#endif
+
+/* This list should be ordered by preference (highest first) */
+#define BLADERF_BACKEND_LIST \
+ { \
+ BACKEND_USB \
+ BACKEND_DUMMY \
+ }
+
+#endif
diff --git a/Radio/HW/BladeRF/src/backend/dummy/dummy.c b/Radio/HW/BladeRF/src/backend/dummy/dummy.c
new file mode 100644
index 0000000..14d8a2c
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/dummy/dummy.c
@@ -0,0 +1,554 @@
+/*
+ * Dummy backend to allow libbladeRF to build when no other backends are
+ * enabled. This is intended for development purposes only, and should
+ * generally should not be enabled for libbladeRF releases.
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "backend/backend.h"
+
+#include "board/board.h"
+
+static bool dummy_matches(bladerf_backend backend)
+{
+ return false;
+}
+
+/* We never "find" dummy devices */
+static int dummy_probe(backend_probe_target probe_target,
+ struct bladerf_devinfo_list *info_list)
+{
+ return 0;
+}
+
+static int dummy_get_vid_pid(struct bladerf *dev, uint16_t *vid, uint16_t *pid)
+{
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int dummy_open(struct bladerf *dev, struct bladerf_devinfo *info)
+{
+ return BLADERF_ERR_NODEV;
+}
+
+static int dummy_set_fpga_protocol(struct bladerf *dev,
+ backend_fpga_protocol fpga_protocol)
+{
+ return 0;
+}
+
+static void dummy_close(struct bladerf *dev)
+{
+ /* Nothing to do */
+}
+
+static int dummy_is_fw_ready(struct bladerf *dev)
+{
+ return 0;
+}
+
+static int dummy_get_handle(struct bladerf *dev, void **handle)
+{
+ return 0;
+}
+
+static int dummy_get_flash_id(struct bladerf *dev, uint8_t *mid, uint8_t *did)
+{
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int dummy_load_fpga(struct bladerf *dev,
+ const uint8_t *image,
+ size_t image_size)
+{
+ return 0;
+}
+
+static int dummy_is_fpga_configured(struct bladerf *dev)
+{
+ return 0;
+}
+
+static int dummy_get_fw_version(struct bladerf *dev,
+ struct bladerf_version *version)
+{
+ return 0;
+}
+
+static int dummy_get_fpga_version(struct bladerf *dev,
+ struct bladerf_version *version)
+{
+ return 0;
+}
+
+static int dummy_erase_flash_blocks(struct bladerf *dev,
+ uint32_t eb,
+ uint16_t count)
+{
+ return 0;
+}
+
+static int dummy_read_flash_pages(struct bladerf *dev,
+ uint8_t *buf,
+ uint32_t page,
+ uint32_t count)
+{
+ return 0;
+}
+
+static int dummy_write_flash_pages(struct bladerf *dev,
+ const uint8_t *buf,
+ uint32_t page,
+ uint32_t count)
+{
+ return 0;
+}
+
+static int dummy_device_reset(struct bladerf *dev)
+{
+ return 0;
+}
+
+static int dummy_jump_to_bootloader(struct bladerf *dev)
+{
+ return 0;
+}
+
+static int dummy_get_cal(struct bladerf *dev, char *cal)
+{
+ return 0;
+}
+
+static int dummy_get_otp(struct bladerf *dev, char *otp)
+{
+ return 0;
+}
+
+static int dummy_write_otp(struct bladerf *dev, char *otp)
+{
+ return 0;
+}
+
+static int dummy_lock_otp(struct bladerf *dev)
+{
+ return 0;
+}
+
+static int dummy_get_device_speed(struct bladerf *dev,
+ bladerf_dev_speed *device_speed)
+{
+ return 0;
+}
+
+static int dummy_config_gpio_write(struct bladerf *dev, uint32_t val)
+{
+ return 0;
+}
+
+static int dummy_config_gpio_read(struct bladerf *dev, uint32_t *val)
+{
+ return 0;
+}
+
+static int dummy_expansion_gpio_write(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t val)
+{
+ return 0;
+}
+
+static int dummy_expansion_gpio_read(struct bladerf *dev, uint32_t *val)
+{
+ return 0;
+}
+
+static int dummy_expansion_gpio_dir_write(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t val)
+{
+ return 0;
+}
+
+static int dummy_expansion_gpio_dir_read(struct bladerf *dev, uint32_t *val)
+{
+ return 0;
+}
+
+static int dummy_set_iq_gain_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t value)
+{
+ return 0;
+}
+
+static int dummy_set_iq_phase_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t value)
+{
+ return 0;
+}
+
+static int dummy_get_iq_gain_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t *value)
+{
+ *value = 0;
+ return 0;
+}
+
+static int dummy_get_iq_phase_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t *value)
+{
+ *value = 0;
+ return 0;
+}
+
+static int dummy_get_timestamp(struct bladerf *dev,
+ bladerf_direction dir,
+ uint64_t *val)
+{
+ return 0;
+}
+
+static int dummy_si5338_read(struct bladerf *dev, uint8_t addr, uint8_t *data)
+{
+ return 0;
+}
+
+static int dummy_si5338_write(struct bladerf *dev, uint8_t addr, uint8_t data)
+{
+ return 0;
+}
+
+static int dummy_lms_write(struct bladerf *dev, uint8_t addr, uint8_t data)
+{
+ return 0;
+}
+
+static int dummy_lms_read(struct bladerf *dev, uint8_t addr, uint8_t *data)
+{
+ return 0;
+}
+
+static int dummy_ina219_write(struct bladerf *dev, uint8_t cmd, uint16_t data)
+{
+ return 0;
+}
+
+static int dummy_ina219_read(struct bladerf *dev, uint8_t addr, uint16_t *data)
+{
+ return 0;
+}
+
+static int dummy_ad9361_spi_write(struct bladerf *dev,
+ uint16_t cmd,
+ uint64_t data)
+{
+ return 0;
+}
+
+static int dummy_ad9361_spi_read(struct bladerf *dev,
+ uint16_t cmd,
+ uint64_t *data)
+{
+ return 0;
+}
+
+static int dummy_adi_axi_write(struct bladerf *dev,
+ uint32_t addr,
+ uint32_t data)
+{
+ return 0;
+}
+
+static int dummy_adi_axi_read(struct bladerf *dev,
+ uint32_t addr,
+ uint32_t *data)
+{
+ return 0;
+}
+
+static int dummy_rffe_control_write(struct bladerf *dev, uint32_t value)
+{
+ return 0;
+}
+
+static int dummy_rffe_control_read(struct bladerf *dev, uint32_t *value)
+{
+ return 0;
+}
+
+static int dummy_rffe_fastlock_save(struct bladerf *dev,
+ bool is_tx,
+ uint8_t rffe_profile,
+ uint16_t nios_profile)
+{
+ return 0;
+}
+
+static int dummy_ad56x1_vctcxo_trim_dac_write(struct bladerf *dev,
+ uint16_t value)
+{
+ return 0;
+}
+
+static int dummy_ad56x1_vctcxo_trim_dac_read(struct bladerf *dev,
+ uint16_t *value)
+{
+ return 0;
+}
+
+static int dummy_adf400x_write(struct bladerf *dev, uint8_t addr, uint32_t data)
+{
+ return 0;
+}
+
+static int dummy_adf400x_read(struct bladerf *dev, uint8_t addr, uint32_t *data)
+{
+ return 0;
+}
+
+static int dummy_vctcxo_dac_write(struct bladerf *dev,
+ uint8_t addr,
+ uint16_t value)
+{
+ return 0;
+}
+
+static int dummy_vctcxo_dac_read(struct bladerf *dev,
+ uint8_t addr,
+ uint16_t *value)
+{
+ return 0;
+}
+
+static int dummy_set_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode mode)
+{
+ return 0;
+}
+
+static int dummy_get_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode *mode)
+{
+ *mode = BLADERF_VCTCXO_TAMER_INVALID;
+ return 0;
+}
+
+static int dummy_xb_spi(struct bladerf *dev, uint32_t value)
+{
+ return 0;
+}
+
+static int dummy_set_firmware_loopback(struct bladerf *dev, bool enable)
+{
+ return 0;
+}
+
+static int dummy_get_firmware_loopback(struct bladerf *dev, bool *is_enabled)
+{
+ *is_enabled = false;
+ return 0;
+}
+
+static int dummy_enable_module(struct bladerf *dev,
+ bladerf_direction dir,
+ bool enable)
+{
+ return 0;
+}
+
+static int dummy_init_stream(struct bladerf_stream *stream,
+ size_t num_transfers)
+{
+ return 0;
+}
+
+static int dummy_stream(struct bladerf_stream *stream,
+ bladerf_channel_layout layout)
+{
+ return 0;
+}
+
+static int dummy_submit_stream_buffer(struct bladerf_stream *stream,
+ void *buffer,
+ unsigned int timeout_ms,
+ bool nonblock)
+{
+ return 0;
+}
+
+static void dummy_deinit_stream(struct bladerf_stream *stream)
+{
+ return;
+}
+
+static int dummy_retune(struct bladerf *dev,
+ bladerf_channel ch,
+ uint64_t timestamp,
+ uint16_t nint,
+ uint32_t nfrac,
+ uint8_t freqsel,
+ uint8_t vcocap,
+ bool low_band,
+ bool quick_tune)
+{
+ return 0;
+}
+
+
+static int dummy_load_fw_from_bootloader(bladerf_backend backend,
+ uint8_t bus,
+ uint8_t addr,
+ struct fx3_firmware *fw)
+{
+ return 0;
+}
+
+static int dummy_read_fw_log(struct bladerf *dev, logger_entry *e)
+{
+ *e = LOG_EOF;
+ return 0;
+}
+
+static int dummy_read_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t *value)
+{
+ *value = 0;
+ return 0;
+}
+
+static int dummy_write_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t value)
+{
+ return 0;
+}
+
+const struct backend_fns backend_fns_dummy = {
+ FIELD_INIT(.matches, dummy_matches),
+
+ FIELD_INIT(.probe, dummy_probe),
+
+ FIELD_INIT(.get_vid_pid, dummy_get_vid_pid),
+ FIELD_INIT(.get_flash_id, dummy_get_flash_id),
+ FIELD_INIT(.open, dummy_open),
+ FIELD_INIT(.set_fpga_protocol, dummy_set_fpga_protocol),
+ FIELD_INIT(.close, dummy_close),
+
+ FIELD_INIT(.is_fw_ready, dummy_is_fw_ready),
+
+ FIELD_INIT(.get_handle, dummy_get_handle),
+
+ FIELD_INIT(.load_fpga, dummy_load_fpga),
+ FIELD_INIT(.is_fpga_configured, dummy_is_fpga_configured),
+
+ FIELD_INIT(.get_fw_version, dummy_get_fw_version),
+ FIELD_INIT(.get_fpga_version, dummy_get_fpga_version),
+
+ FIELD_INIT(.erase_flash_blocks, dummy_erase_flash_blocks),
+ FIELD_INIT(.read_flash_pages, dummy_read_flash_pages),
+ FIELD_INIT(.write_flash_pages, dummy_write_flash_pages),
+
+ FIELD_INIT(.device_reset, dummy_device_reset),
+ FIELD_INIT(.jump_to_bootloader, dummy_jump_to_bootloader),
+
+ FIELD_INIT(.get_cal, dummy_get_cal),
+ FIELD_INIT(.get_otp, dummy_get_otp),
+ FIELD_INIT(.write_otp, dummy_write_otp),
+ FIELD_INIT(.lock_otp, dummy_lock_otp),
+ FIELD_INIT(.get_device_speed, dummy_get_device_speed),
+
+ FIELD_INIT(.config_gpio_write, dummy_config_gpio_write),
+ FIELD_INIT(.config_gpio_read, dummy_config_gpio_read),
+
+ FIELD_INIT(.expansion_gpio_write, dummy_expansion_gpio_write),
+ FIELD_INIT(.expansion_gpio_read, dummy_expansion_gpio_read),
+ FIELD_INIT(.expansion_gpio_dir_write, dummy_expansion_gpio_dir_write),
+ FIELD_INIT(.expansion_gpio_dir_read, dummy_expansion_gpio_dir_read),
+
+ FIELD_INIT(.set_iq_gain_correction, dummy_set_iq_gain_correction),
+ FIELD_INIT(.set_iq_phase_correction, dummy_set_iq_phase_correction),
+ FIELD_INIT(.get_iq_gain_correction, dummy_get_iq_gain_correction),
+ FIELD_INIT(.get_iq_phase_correction, dummy_get_iq_phase_correction),
+
+ FIELD_INIT(.get_timestamp, dummy_get_timestamp),
+
+ FIELD_INIT(.si5338_write, dummy_si5338_write),
+ FIELD_INIT(.si5338_read, dummy_si5338_read),
+
+ FIELD_INIT(.lms_write, dummy_lms_write),
+ FIELD_INIT(.lms_read, dummy_lms_read),
+
+ FIELD_INIT(.ina219_write, dummy_ina219_write),
+ FIELD_INIT(.ina219_read, dummy_ina219_read),
+
+ FIELD_INIT(.ad9361_spi_write, dummy_ad9361_spi_write),
+ FIELD_INIT(.ad9361_spi_read, dummy_ad9361_spi_read),
+
+ FIELD_INIT(.adi_axi_write, dummy_adi_axi_write),
+ FIELD_INIT(.adi_axi_read, dummy_adi_axi_read),
+
+ FIELD_INIT(.rffe_control_write, dummy_rffe_control_write),
+ FIELD_INIT(.rffe_control_read, dummy_rffe_control_read),
+
+ FIELD_INIT(.rffe_fastlock_save, dummy_rffe_fastlock_save),
+
+ FIELD_INIT(.ad56x1_vctcxo_trim_dac_write,
+ dummy_ad56x1_vctcxo_trim_dac_write),
+ FIELD_INIT(.ad56x1_vctcxo_trim_dac_read, dummy_ad56x1_vctcxo_trim_dac_read),
+
+ FIELD_INIT(.adf400x_write, dummy_adf400x_write),
+ FIELD_INIT(.adf400x_read, dummy_adf400x_read),
+
+ FIELD_INIT(.vctcxo_dac_write, dummy_vctcxo_dac_write),
+ FIELD_INIT(.vctcxo_dac_read, dummy_vctcxo_dac_read),
+
+ FIELD_INIT(.set_vctcxo_tamer_mode, dummy_set_vctcxo_tamer_mode),
+ FIELD_INIT(.get_vctcxo_tamer_mode, dummy_get_vctcxo_tamer_mode),
+
+ FIELD_INIT(.xb_spi, dummy_xb_spi),
+
+ FIELD_INIT(.set_firmware_loopback, dummy_set_firmware_loopback),
+ FIELD_INIT(.get_firmware_loopback, dummy_get_firmware_loopback),
+
+ FIELD_INIT(.enable_module, dummy_enable_module),
+
+ FIELD_INIT(.init_stream, dummy_init_stream),
+ FIELD_INIT(.stream, dummy_stream),
+ FIELD_INIT(.submit_stream_buffer, dummy_submit_stream_buffer),
+ FIELD_INIT(.deinit_stream, dummy_deinit_stream),
+
+ FIELD_INIT(.retune, dummy_retune),
+
+ FIELD_INIT(.load_fw_from_bootloader, dummy_load_fw_from_bootloader),
+
+ FIELD_INIT(.read_fw_log, dummy_read_fw_log),
+
+ FIELD_INIT(.read_trigger, dummy_read_trigger),
+ FIELD_INIT(.write_trigger, dummy_write_trigger),
+
+ FIELD_INIT(.name, "dummy"),
+};
diff --git a/Radio/HW/BladeRF/src/backend/dummy/dummy.h b/Radio/HW/BladeRF/src/backend/dummy/dummy.h
new file mode 100644
index 0000000..2c86bd7
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/dummy/dummy.h
@@ -0,0 +1,32 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#if defined(ENABLE_BACKEND_DUMMY)
+
+#define BACKEND_DUMMY_H_
+#ifndef BACKEND_DUMMY_H_
+
+#include "bladerf_priv.h"
+
+extern const struct bladerf_fn bladerf_dummy_fn;
+
+#endif
+
+#endif
diff --git a/Radio/HW/BladeRF/src/backend/usb/cyapi.c b/Radio/HW/BladeRF/src/backend/usb/cyapi.c
new file mode 100644
index 0000000..48bef43
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/usb/cyapi.c
@@ -0,0 +1,854 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013-2016 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* This is a Windows-specific USB backend using Cypress's CyAPI, which utilizes
+ * the CyUSB3.sys driver (with a CyUSB3.inf modified to include the bladeRF
+ * VID/PID).
+ */
+
+extern "C" {
+#include <stdlib.h>
+#include <pthread.h>
+#include <errno.h>
+#include "bladeRF.h" /* Firmware interface */
+
+#include "devinfo.h"
+#include "backend/backend.h"
+#include "backend/usb/usb.h"
+#include "streaming/async.h"
+#include "helpers/timeout.h"
+#include "log.h"
+}
+
+#include <CyAPI.h>
+
+/* This GUID must match that in the modified CyUSB3.inf used with the bladeRF */
+static const GUID driver_guid = {
+ 0x35D5D3F1, 0x9D0E, 0x4F62, 0xBC, 0xFB, 0xB0, 0xD4, 0x8E,0xA6, 0x34, 0x16
+};
+
+/* "Private data" for the CyAPI backend */
+struct bladerf_cyapi {
+ CCyUSBDevice *dev;
+ HANDLE mutex;
+};
+
+struct transfer {
+ OVERLAPPED event; /* Transfer completion event handle */
+ PUCHAR handle; /* Handle for in-flight transfer */
+ PUCHAR buffer; /* Buffer associated with transfer */
+};
+
+struct stream_data {
+ CCyBulkEndPoint *ep; /* Endpoint associated with the stream */
+
+ struct transfer *transfers; /* Transfer slots */
+
+ size_t num_transfers; /* Max # of in-flight transfers */
+ size_t num_avail;
+ size_t avail_i; /* Index of next available transfer slot */
+ size_t inflight_i; /* Index of in-flight transfer that is
+ * expected to finish next */
+};
+
+static inline struct bladerf_cyapi * get_backend_data(void *driver)
+{
+ assert(driver);
+ return (struct bladerf_cyapi *) driver;
+}
+
+static inline CCyUSBDevice * get_device(void *driver)
+{
+ struct bladerf_cyapi *backend_data = get_backend_data(driver);
+ assert(backend_data->dev);
+ return backend_data->dev;
+}
+
+static inline struct stream_data * get_stream_data(struct bladerf_stream *s)
+{
+ assert(s && s->backend_data);
+ return (struct stream_data *) s->backend_data;
+}
+
+static int open_device(CCyUSBDevice *dev, int instance, HANDLE *mutex)
+{
+ int status = 0;
+ bool success;
+ DWORD last_error;
+ const char prefix[] = "Global\\bladeRF-";
+ const size_t mutex_name_len = strlen(prefix) + BLADERF_SERIAL_LENGTH;
+ char *mutex_name = (char *) calloc(mutex_name_len, 1);
+
+ if (mutex_name == NULL) {
+ return BLADERF_ERR_MEM;
+ } else {
+ strcpy(mutex_name, prefix);
+ }
+
+ success = dev->Open(instance);
+ if (!success) {
+ status = BLADERF_ERR_IO;
+ goto out;
+ }
+
+ wcstombs(mutex_name + strlen(prefix), dev->SerialNumber, sizeof(mutex_name) - BLADERF_SERIAL_LENGTH - 1);
+ log_verbose("Mutex name: %s\n", mutex_name);
+
+ SetLastError(0);
+ *mutex = CreateMutex(NULL, true, mutex_name);
+ last_error = GetLastError();
+ if (mutex == NULL || last_error != 0) {
+ log_debug("Unable to create device mutex: mutex=%p, last_error=%ld\n",
+ mutex, last_error);
+ dev->Close();
+ status = BLADERF_ERR_NODEV;
+ }
+
+out:
+ free(mutex_name);
+ return status;
+}
+
+static inline bool has_bladerf_ids(CCyUSBDevice *dev)
+{
+ return ((dev->VendorID == USB_NUAND_VENDOR_ID) && (dev->ProductID == USB_NUAND_BLADERF_PRODUCT_ID)) ||
+ ((dev->VendorID == USB_NUAND_VENDOR_ID) && (dev->ProductID == USB_NUAND_BLADERF2_PRODUCT_ID)) ||
+ ((dev->VendorID == USB_NUAND_LEGACY_VENDOR_ID) && (dev->ProductID == USB_NUAND_BLADERF_LEGACY_PRODUCT_ID));
+}
+
+static inline bool has_boot_ids(CCyUSBDevice *dev)
+{
+ return ((dev->VendorID == USB_CYPRESS_VENDOR_ID) && (dev->ProductID == USB_FX3_PRODUCT_ID)) ||
+ ((dev->VendorID == USB_NUAND_VENDOR_ID) && (dev->ProductID == USB_NUAND_BLADERF_BOOT_PRODUCT_ID)) ||
+ ((dev->VendorID == USB_NUAND_LEGACY_VENDOR_ID) && (dev->ProductID == USB_NUAND_BLADERF_LEGACY_BOOT_PRODUCT_ID));
+}
+
+static bool device_matches_target(CCyUSBDevice *dev,
+ backend_probe_target target)
+{
+ bool matches = false;
+
+ switch (target) {
+ case BACKEND_PROBE_BLADERF:
+ matches = has_bladerf_ids(dev);
+ break;
+
+ case BACKEND_PROBE_FX3_BOOTLOADER:
+ matches = has_boot_ids(dev);
+ break;
+ }
+
+ return matches;
+}
+
+static int cyapi_probe(backend_probe_target probe_target,
+ struct bladerf_devinfo_list *info_list)
+{
+ CCyUSBDevice *dev = new CCyUSBDevice(NULL, driver_guid);
+ if (dev == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ for (int i = 0; i < dev->DeviceCount(); i++) {
+ struct bladerf_devinfo info;
+ bool opened;
+ int status;
+
+ opened = dev->Open(i);
+ if (opened) {
+ if (device_matches_target(dev, probe_target)) {
+ const size_t max_serial = sizeof(info.serial) - 1;
+ info.instance = i;
+ memset(info.serial, 0, sizeof(info.serial));
+ wcstombs(info.serial, dev->SerialNumber, max_serial);
+ info.usb_addr = dev->USBAddress;
+ info.usb_bus = 0; /* CyAPI doesn't provide this */
+ info.backend = BLADERF_BACKEND_CYPRESS;
+ status = bladerf_devinfo_list_add(info_list, &info);
+ if (status != 0) {
+ log_error("Could not add device to list: %s\n",
+ bladerf_strerror(status));
+ } else {
+ log_verbose("Added instance %d to device list\n",
+ info.instance);
+ }
+ }
+
+ dev->Close();
+ }
+ }
+
+ delete dev;
+ return 0;
+}
+
+static int open_via_info(void **driver, backend_probe_target probe_target,
+ struct bladerf_devinfo *info_in,
+ struct bladerf_devinfo *info_out)
+{
+ int status;
+ int instance = -1;
+ struct bladerf_devinfo_list info_list;
+ CCyUSBDevice *dev;
+ struct bladerf_cyapi *cyapi_data;
+
+ dev = new CCyUSBDevice(NULL, driver_guid);
+ if (dev == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ cyapi_data = (struct bladerf_cyapi *) calloc(1, sizeof(cyapi_data[0]));
+ if (cyapi_data == NULL) {
+ delete dev;
+ return BLADERF_ERR_MEM;
+ }
+
+ bladerf_devinfo_list_init(&info_list);
+ status = cyapi_probe(probe_target, &info_list);
+
+ for (unsigned int i = 0; i < info_list.num_elt && status == 0; i++) {
+
+
+ if (bladerf_devinfo_matches(&info_list.elt[i], info_in)) {
+ instance = info_list.elt[i].instance;
+
+ status = open_device(dev, instance, &cyapi_data->mutex);
+ if (status == 0) {
+ cyapi_data->dev = dev;
+ *driver = cyapi_data;
+ if (info_out != NULL) {
+ memcpy(info_out,
+ &info_list.elt[instance],
+ sizeof(info_out[0]));
+ }
+ status = 0;
+ break;
+ }
+ }
+ }
+
+ if (status == 0 && instance < 0) {
+ status = BLADERF_ERR_NODEV;
+ }
+
+ if (status != 0) {
+ delete dev;
+ ReleaseMutex(cyapi_data->mutex);
+ CloseHandle(cyapi_data->mutex);
+ }
+
+ free(info_list.elt);
+ return status;
+}
+
+
+static int cyapi_open(void **driver,
+ struct bladerf_devinfo *info_in,
+ struct bladerf_devinfo *info_out)
+{
+ return open_via_info(driver, BACKEND_PROBE_BLADERF, info_in, info_out);
+}
+
+static int cyapi_change_setting(void *driver, uint8_t setting)
+{
+ CCyUSBDevice *dev = get_device(driver);
+
+ if (dev->SetAltIntfc(setting)) {
+ return 0;
+ } else {
+ return BLADERF_ERR_IO;
+ }
+}
+
+static void cyapi_close(void *driver)
+{
+ struct bladerf_cyapi *cyapi_data = get_backend_data(driver);
+ cyapi_data->dev->Close();
+ delete cyapi_data->dev;
+ ReleaseMutex(cyapi_data->mutex);
+ CloseHandle(cyapi_data->mutex);
+ free(driver);
+
+}
+
+static int cyapi_get_vid_pid(void *driver, uint16_t *vid,
+ uint16_t *pid)
+{
+ CCyUSBDevice *dev = get_device(driver);
+
+ *vid = dev->VendorID;
+ *pid = dev->ProductID;
+
+ return 0;
+}
+
+static int cyapi_get_flash_id(void *driver, uint8_t *mid, uint8_t *did)
+{
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int cyapi_get_handle(void *driver, void **handle)
+{
+ CCyUSBDevice *dev = get_device(driver);
+ *handle = dev;
+
+ return 0;
+}
+static int cyapi_get_speed(void *driver,
+ bladerf_dev_speed *device_speed)
+{
+ int status = 0;
+ CCyUSBDevice *dev = get_device(driver);
+
+ if (dev->bHighSpeed) {
+ *device_speed = BLADERF_DEVICE_SPEED_HIGH;
+ } else if (dev->bSuperSpeed) {
+ *device_speed = BLADERF_DEVICE_SPEED_SUPER;
+ } else {
+ *device_speed = BLADERF_DEVICE_SPEED_UNKNOWN;
+ status = BLADERF_ERR_UNEXPECTED;
+ log_debug("%s: Unable to determine device speed.\n", __FUNCTION__);
+ }
+
+ return status;
+}
+
+static int cyapi_control_transfer(void *driver,
+ usb_target target_type, usb_request req_type,
+ usb_direction dir, uint8_t request,
+ uint16_t wvalue, uint16_t windex,
+ void *buffer, uint32_t buffer_len,
+ uint32_t timeout_ms)
+{
+ CCyUSBDevice *dev = get_device(driver);
+ LONG len;
+ bool success;
+
+ int status = 0;
+
+ switch (dir) {
+ case USB_DIR_DEVICE_TO_HOST:
+ dev->ControlEndPt->Direction = DIR_FROM_DEVICE;
+ break;
+ case USB_DIR_HOST_TO_DEVICE:
+ dev->ControlEndPt->Direction = DIR_TO_DEVICE;
+ break;
+ }
+
+ switch (req_type) {
+ case USB_REQUEST_CLASS:
+ dev->ControlEndPt->ReqType = REQ_CLASS;
+ break;
+ case USB_REQUEST_STANDARD:
+ dev->ControlEndPt->ReqType = REQ_STD;
+ break;
+ case USB_REQUEST_VENDOR:
+ dev->ControlEndPt->ReqType = REQ_VENDOR;
+ break;
+ }
+
+ switch (target_type) {
+ case USB_TARGET_DEVICE:
+ dev->ControlEndPt->Target = TGT_DEVICE;
+ break;
+ case USB_TARGET_ENDPOINT:
+ dev->ControlEndPt->Target = TGT_ENDPT;
+ break;
+ case USB_TARGET_INTERFACE:
+ dev->ControlEndPt->Target = TGT_INTFC;
+ break;
+ case USB_TARGET_OTHER:
+ dev->ControlEndPt->Target = TGT_OTHER;
+ break;
+ }
+
+ dev->ControlEndPt->MaxPktSize = buffer_len;
+ dev->ControlEndPt->ReqCode = request;
+ dev->ControlEndPt->Index = windex;
+ dev->ControlEndPt->Value = wvalue;
+ dev->ControlEndPt->TimeOut = timeout_ms ? timeout_ms : INFINITE;
+ len = buffer_len;
+
+ success = dev->ControlEndPt->XferData((PUCHAR)buffer, len);
+ if (!success) {
+ status = BLADERF_ERR_IO;
+ }
+
+ return status;
+
+}
+
+static CCyBulkEndPoint *get_ep(CCyUSBDevice *USBDevice, int id)
+{
+
+ int count;
+ count = USBDevice->EndPointCount();
+
+ for (int i = 1; i < count; i++) {
+ if (USBDevice->EndPoints[i]->Address == id) {
+ return (CCyBulkEndPoint *) USBDevice->EndPoints[i];
+ }
+ }
+
+ return NULL;
+}
+
+static int cyapi_bulk_transfer(void *driver, uint8_t endpoint, void *buffer,
+ uint32_t buffer_len, uint32_t timeout_ms)
+{
+ bool success;
+ int status = BLADERF_ERR_IO;
+ CCyUSBDevice *dev = get_device(driver);
+ CCyBulkEndPoint *ep = get_ep(dev, endpoint);
+
+ if (ep != NULL) {
+ LONG len = buffer_len;
+ dev->ControlEndPt->TimeOut = timeout_ms ? timeout_ms : INFINITE;
+ success = ep->XferData((PUCHAR)buffer, len);
+
+ if (success) {
+ if (len == buffer_len) {
+ status = 0;
+ } else {
+ log_debug("Transfer len mismatch: %u vs %u\n",
+ (unsigned int) buffer_len, (unsigned int) len);
+ }
+ } else {
+ log_debug("Transfered failed.\n");
+ }
+ } else {
+ log_debug("Failed to get EP handle.\n");
+ }
+
+ return status;
+}
+
+static int cyapi_get_string_descriptor(void *driver, uint8_t index,
+ void *buffer, uint32_t buffer_len)
+{
+ int status;
+ char *str;
+
+ memset(buffer, 0, buffer_len);
+
+ status = cyapi_control_transfer(driver,
+ USB_TARGET_DEVICE,
+ USB_REQUEST_STANDARD,
+ USB_DIR_DEVICE_TO_HOST,
+ 0x06, 0x0300 | index, 0,
+ buffer, buffer_len,
+ BLADE_USB_TIMEOUT_MS);
+
+ if (status != 0) {
+ return status;
+ }
+
+ str = (char*)buffer;
+ for (unsigned int i = 0; i < (buffer_len / 2); i++) {
+ str[i] = str[2 + (i * 2)];
+ }
+
+ return 0;
+}
+
+static int cyapi_deinit_stream(void *driver, struct bladerf_stream *stream)
+{
+ struct stream_data *data;
+
+ assert(stream != NULL);
+ data = get_stream_data(stream);
+ assert(data != NULL);
+
+ for (unsigned int i = 0; i < data->num_transfers; i++) {
+ CloseHandle(data->transfers[i].event.hEvent);
+ }
+
+ free(data->transfers);
+ free(data);
+
+ return 0;
+}
+
+static int cyapi_init_stream(void *driver, struct bladerf_stream *stream,
+ size_t num_transfers)
+{
+ int status = BLADERF_ERR_MEM;
+ CCyUSBDevice *dev = get_device(driver);
+ struct stream_data *data;
+
+ data = (struct stream_data *) calloc(1, sizeof(data[0]));
+ if (data != NULL) {
+ stream->backend_data = data;
+ } else {
+ return status;
+ }
+
+ data->transfers =
+ (struct transfer*) calloc(num_transfers, sizeof(struct transfer));
+
+ if (data->transfers == NULL) {
+ goto out;
+ }
+
+ for (unsigned int i = 0; i < num_transfers; i++) {
+ data->transfers[i].event.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (data->transfers[i].event.hEvent == NULL) {
+ log_debug("%s: Failed to create EventObject for transfer %u\n",
+ (unsigned int) i);
+ goto out;
+ }
+ }
+
+ data->num_transfers = num_transfers;
+ data->num_avail = num_transfers;
+ data->inflight_i = 0;
+ data->avail_i = 0;
+
+ status = 0;
+
+out:
+ if (status != 0) {
+ cyapi_deinit_stream(driver, stream);
+ stream->backend_data = NULL;
+ }
+
+ return status;
+}
+
+#ifdef LOGGING_ENABLED
+static inline int find_buf(void* ptr, struct bladerf_stream *stream)
+{
+ for (unsigned int i=0; i<stream->num_buffers; i++) {
+ if (stream->buffers[i] == ptr) {
+ return i;
+ }
+ }
+
+ log_debug("Unabled to find buffer %p\n:", ptr);
+ return -1;
+}
+#endif
+
+#ifndef ENABLE_LIBBLADERF_ASYNC_LOG_VERBOSE
+#undef log_verbose
+#define log_verbose(...)
+#endif
+
+static inline size_t next_idx(struct stream_data *data, size_t i)
+{
+ return (i + 1) % data->num_transfers;
+}
+
+/* Assumes a transfer is available and the stream lock is being held */
+static int submit_transfer(struct bladerf_stream *stream, void *buffer, size_t len)
+{
+ int status = 0;
+ PUCHAR xfer;
+ struct stream_data *data = get_stream_data(stream);
+
+ LONG buffer_size = (LONG) len;
+
+ assert(data->transfers[data->avail_i].handle == NULL);
+ assert(data->transfers[data->avail_i].buffer == NULL);
+ assert(data->num_avail != 0);
+
+ xfer = data->ep->BeginDataXfer((PUCHAR) buffer, buffer_size,
+ &data->transfers[data->avail_i].event);
+
+ if (xfer != NULL) {
+ data->transfers[data->avail_i].handle = xfer;
+ data->transfers[data->avail_i].buffer = (PUCHAR) buffer;
+
+ log_verbose("Submitted buffer %p using transfer slot %u.\n",
+ buffer, (unsigned int) data->avail_i);
+
+ data->avail_i = next_idx(data, data->avail_i);
+ data->num_avail--;
+ } else {
+ status = BLADERF_ERR_UNEXPECTED;
+ log_debug("Failed to submit buffer %p in transfer slot %u.\n",
+ buffer, (unsigned int) data->avail_i);
+ }
+
+ return status;
+}
+
+static int cyapi_stream(void *driver, struct bladerf_stream *stream,
+ bladerf_channel_layout layout)
+{
+ int status;
+ int idx = 0;
+ long len;
+ void *next_buffer;
+ ULONG timeout_ms;
+ bool success, done;
+ struct stream_data *data = get_stream_data(stream);
+ struct bladerf_cyapi *cyapi = get_backend_data(driver);
+ struct bladerf_metadata meta;
+
+ assert(stream->transfer_timeout <= ULONG_MAX);
+ if (stream->transfer_timeout == 0) {
+ timeout_ms = INFINITE;
+ } else {
+ timeout_ms = stream->transfer_timeout;
+ }
+
+ switch (layout & BLADERF_DIRECTION_MASK) {
+ case BLADERF_RX:
+ data->ep = get_ep(cyapi->dev, SAMPLE_EP_IN);
+ break;
+
+ case BLADERF_TX:
+ data->ep = get_ep(cyapi->dev, SAMPLE_EP_OUT);
+ break;
+ }
+
+ if (data->ep == NULL) {
+ log_debug("Failed to get EP handle.\n");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ data->ep->XferMode = XMODE_DIRECT;
+ data->ep->Abort();
+ data->ep->Reset();
+
+ log_verbose("Starting stream...\n");
+ status = 0;
+ done = false;
+ memset(&meta, 0, sizeof(meta));
+
+ MUTEX_LOCK(&stream->lock);
+
+ for (unsigned int i = 0; i < data->num_transfers && status == 0; i++) {
+ if ((layout & BLADERF_DIRECTION_MASK) == BLADERF_TX) {
+ next_buffer = stream->cb(stream->dev, stream, &meta, NULL,
+ stream->samples_per_buffer,
+ stream->user_data);
+
+ if (next_buffer == BLADERF_STREAM_SHUTDOWN) {
+ done = true;
+ break;
+ } else if (next_buffer == BLADERF_STREAM_NO_DATA) {
+ continue;
+ }
+ } else {
+ next_buffer = stream->buffers[i];
+ }
+
+ if ((stream->layout & BLADERF_DIRECTION_MASK) == BLADERF_TX
+ && stream->format == BLADERF_FORMAT_PACKET_META) {
+ status = submit_transfer(stream, next_buffer, meta.actual_count);
+ } else {
+ status = submit_transfer(stream, next_buffer, async_stream_buf_bytes(stream));
+ }
+ }
+
+ MUTEX_UNLOCK(&stream->lock);
+ if (status != 0) {
+ goto out;
+ }
+
+ while (!done) {
+ struct transfer *xfer;
+ size_t i;
+
+ i = data->inflight_i;
+ xfer = &data->transfers[i];
+ success = data->ep->WaitForXfer(&xfer->event, timeout_ms);
+
+ if (!success) {
+ status = BLADERF_ERR_TIMEOUT;
+ log_debug("Steam timed out.\n");
+ break;
+ }
+
+ len = 0;
+ next_buffer = NULL;
+
+ log_verbose("Got transfer complete in slot %u (buffer %p)\n",
+ i, data->transfers[i].buffer);
+
+ MUTEX_LOCK(&stream->lock);
+ success = data->ep->FinishDataXfer(data->transfers[i].buffer, (LONG &)len,
+ &data->transfers[i].event,
+ xfer->handle);
+
+ if (success) {
+ next_buffer = stream->cb(stream->dev, stream, &meta,
+ data->transfers[i].buffer,
+ bytes_to_samples(stream->format, (LONG &)len),
+ stream->user_data);
+
+ } else {
+ done = true;
+ status = BLADERF_ERR_IO;
+ log_debug("Failed to finish transfer %u, buf=%p.\n",
+ (unsigned int)i, &data->transfers[i].buffer);
+ }
+
+ data->transfers[i].buffer = NULL;
+ data->transfers[i].handle = NULL;
+ data->num_avail++;
+ pthread_cond_signal(&stream->can_submit_buffer);
+
+ if (next_buffer == BLADERF_STREAM_SHUTDOWN) {
+ done = true;
+ } else if (next_buffer != BLADERF_STREAM_NO_DATA) {
+ if ((layout & BLADERF_DIRECTION_MASK) == BLADERF_TX
+ && stream->format == BLADERF_FORMAT_PACKET_META) {
+ status = submit_transfer(stream, next_buffer, meta.actual_count);
+ } else {
+ status = submit_transfer(stream, next_buffer, async_stream_buf_bytes(stream));
+ }
+
+ done = (status != 0);
+ }
+
+ data->inflight_i = next_idx(data, data->inflight_i);
+ MUTEX_UNLOCK(&stream->lock);
+ }
+
+out:
+
+ MUTEX_LOCK(&stream->lock);
+ stream->error_code = status;
+ stream->state = STREAM_SHUTTING_DOWN;
+
+ data->ep->Abort();
+ data->ep->Reset();
+
+
+ for (unsigned int i = 0; i < data->num_transfers; i++) {
+ LONG len = 0;
+ if (data->transfers[i].handle != NULL) {
+ data->ep->FinishDataXfer(data->transfers[i].buffer, (LONG &)len,
+ &data->transfers[i].event,
+ data->transfers[i].handle);
+
+
+ data->transfers[i].buffer = NULL;
+ data->transfers[i].handle = NULL;
+ data->num_avail++;
+ }
+ }
+
+ assert(data->num_avail == data->num_transfers);
+
+ stream->state = STREAM_DONE;
+ log_verbose("Stream done (error_code = %d)\n", stream->error_code);
+ MUTEX_UNLOCK(&stream->lock);
+
+ return 0;
+}
+
+/* The top-level code will have acquired the stream->lock for us */
+int cyapi_submit_stream_buffer(void *driver, struct bladerf_stream *stream,
+ void *buffer, size_t *length,
+ unsigned int timeout_ms, bool nonblock)
+{
+ int status = 0;
+ struct timespec timeout_abs;
+ struct stream_data *data = get_stream_data(stream);
+
+ if (buffer == BLADERF_STREAM_SHUTDOWN) {
+ if (data->num_avail == data->num_transfers) {
+ stream->state = STREAM_DONE;
+ } else {
+ stream->state = STREAM_SHUTTING_DOWN;
+ }
+
+ return 0;
+ }
+
+ if (data->num_avail == 0) {
+ if (nonblock) {
+ log_debug("Non-blocking buffer submission requested, but no "
+ "transfers are currently available.");
+
+ return BLADERF_ERR_WOULD_BLOCK;
+ }
+
+ if (timeout_ms != 0) {
+ status = populate_abs_timeout(&timeout_abs, timeout_ms);
+ if (status != 0) {
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ while (data->num_avail == 0 && status == 0) {
+ status = pthread_cond_timedwait(&stream->can_submit_buffer,
+ &stream->lock,
+ &timeout_abs);
+ }
+ } else {
+ while (data->num_avail == 0 && status == 0) {
+ status = pthread_cond_wait(&stream->can_submit_buffer,
+ &stream->lock);
+ }
+ }
+ }
+
+ if (status == ETIMEDOUT) {
+ return BLADERF_ERR_TIMEOUT;
+ } else if (status != 0) {
+ return BLADERF_ERR_UNEXPECTED;
+ } else {
+ return submit_transfer(stream, buffer, *length);
+ }
+}
+
+int cyapi_open_bootloader(void **driver, uint8_t bus, uint8_t addr)
+{
+ struct bladerf_devinfo info;
+
+ bladerf_init_devinfo(&info);
+ info.backend = BLADERF_BACKEND_CYPRESS;
+ info.usb_bus = bus;
+ info.usb_addr = addr;
+
+ return open_via_info(driver, BACKEND_PROBE_FX3_BOOTLOADER, &info, NULL);
+}
+
+extern "C" {
+ static const struct usb_fns cypress_fns = {
+ FIELD_INIT(.probe, cyapi_probe),
+ FIELD_INIT(.open, cyapi_open),
+ FIELD_INIT(.close, cyapi_close),
+ FIELD_INIT(.get_vid_pid, cyapi_get_vid_pid),
+ FIELD_INIT(.get_flash_id, cyapi_get_flash_id),
+ FIELD_INIT(.get_handle, cyapi_get_handle),
+ FIELD_INIT(.get_speed, cyapi_get_speed),
+ FIELD_INIT(.change_setting, cyapi_change_setting),
+ FIELD_INIT(.control_transfer, cyapi_control_transfer),
+ FIELD_INIT(.bulk_transfer, cyapi_bulk_transfer),
+ FIELD_INIT(.get_string_descriptor, cyapi_get_string_descriptor),
+ FIELD_INIT(.init_stream, cyapi_init_stream),
+ FIELD_INIT(.stream, cyapi_stream),
+ FIELD_INIT(.submit_stream_buffer, cyapi_submit_stream_buffer),
+ FIELD_INIT(.deinit_stream, cyapi_deinit_stream),
+ FIELD_INIT(.open_bootloader, cyapi_open_bootloader),
+ FIELD_INIT(.close_bootloader, cyapi_close),
+ };
+
+ struct usb_driver usb_driver_cypress = {
+ FIELD_INIT(.fn, &cypress_fns),
+ FIELD_INIT(.id, BLADERF_BACKEND_CYPRESS),
+ };
+}
diff --git a/Radio/HW/BladeRF/src/backend/usb/libusb.c b/Radio/HW/BladeRF/src/backend/usb/libusb.c
new file mode 100644
index 0000000..1f63bbc
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/usb/libusb.c
@@ -0,0 +1,1504 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013-2016 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "host_config.h"
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <errno.h>
+#include "libusb.h"
+#if 1 == BLADERF_OS_FREEBSD
+#include <limits.h>
+#endif // BLADERF_OS_FREEBSD
+
+#include "log.h"
+
+#include "devinfo.h"
+#include "backend/backend.h"
+#include "backend/usb/usb.h"
+#include "streaming/async.h"
+#include "helpers/timeout.h"
+
+#include "bladeRF.h"
+
+#include "host_config.h"
+
+#ifndef LIBUSB_HANDLE_EVENTS_TIMEOUT_NSEC
+# define LIBUSB_HANDLE_EVENTS_TIMEOUT_NSEC (15 * 1000)
+#endif
+
+struct bladerf_lusb {
+ libusb_device *dev;
+ libusb_device_handle *handle;
+ libusb_context *context;
+#if 1 == BLADERF_OS_WINDOWS
+ HANDLE mutex;
+#endif // BLADERF_OS_WINDOWS
+};
+
+typedef enum {
+ TRANSFER_UNINITIALIZED = 0,
+ TRANSFER_AVAIL,
+ TRANSFER_IN_FLIGHT,
+ TRANSFER_CANCEL_PENDING
+} transfer_status;
+
+struct lusb_stream_data {
+ size_t num_transfers; /* Total # of allocated transfers */
+ size_t num_avail; /* # of currently available transfers */
+ size_t i; /* Index to next transfer */
+ struct libusb_transfer **transfers; /* Array of transfer metadata */
+ transfer_status *transfer_status; /* Status of each transfer */
+
+ /* Warn the first time we get a transfer callback out of order.
+ * This shouldn't happen normally, but we've seen it intermittently on
+ * libusb 1.0.19 for Windows. Further investigation required...
+ */
+ bool out_of_order_event;
+};
+
+static inline struct bladerf_lusb * lusb_backend(struct bladerf *dev)
+{
+ struct bladerf_usb *usb;
+
+ assert(dev && dev->backend_data);
+ usb = (struct bladerf_usb *) dev->backend_data;
+
+ assert(usb->driver);
+ return (struct bladerf_lusb *) usb->driver;
+}
+
+/* Convert libusb error codes to libbladeRF error codes */
+static int error_conv(int error)
+{
+ int ret;
+
+ switch (error) {
+ case 0:
+ ret = 0;
+ break;
+
+ case LIBUSB_ERROR_IO:
+ ret = BLADERF_ERR_IO;
+ break;
+
+ case LIBUSB_ERROR_INVALID_PARAM:
+ ret = BLADERF_ERR_INVAL;
+ break;
+
+ case LIBUSB_ERROR_BUSY:
+ case LIBUSB_ERROR_NO_DEVICE:
+ ret = BLADERF_ERR_NODEV;
+ break;
+
+ case LIBUSB_ERROR_TIMEOUT:
+ ret = BLADERF_ERR_TIMEOUT;
+ break;
+
+ case LIBUSB_ERROR_NO_MEM:
+ ret = BLADERF_ERR_MEM;
+ break;
+
+ case LIBUSB_ERROR_NOT_SUPPORTED:
+ ret = BLADERF_ERR_UNSUPPORTED;
+ break;
+
+ case LIBUSB_ERROR_ACCESS:
+ ret = BLADERF_ERR_PERMISSION;
+ break;
+
+ case LIBUSB_ERROR_OVERFLOW:
+ case LIBUSB_ERROR_PIPE:
+ case LIBUSB_ERROR_INTERRUPTED:
+ case LIBUSB_ERROR_NOT_FOUND:
+ default:
+ ret = BLADERF_ERR_UNEXPECTED;
+ }
+
+ return ret;
+}
+
+/* Returns libusb error codes */
+static int get_devinfo(libusb_device *dev, struct bladerf_devinfo *info)
+{
+ int status = 0;
+ libusb_device_handle *handle;
+ struct libusb_device_descriptor desc;
+
+ /* Populate device info */
+ bladerf_init_devinfo(info);
+ info->backend = BLADERF_BACKEND_LIBUSB;
+ info->usb_bus = libusb_get_bus_number(dev);
+ info->usb_addr = libusb_get_device_address(dev);
+
+ status = libusb_open(dev, &handle);
+
+ if (status == 0) {
+ status = libusb_get_device_descriptor(dev, &desc);
+ if (status != 0) {
+ memset(info->serial, 0, BLADERF_SERIAL_LENGTH);
+ } else {
+ status = libusb_get_string_descriptor_ascii(
+ handle, desc.iSerialNumber, (unsigned char *)&info->serial,
+ BLADERF_SERIAL_LENGTH);
+
+ /* Consider this to be non-fatal, otherwise firmware <= 1.1
+ * wouldn't be able to get far enough to upgrade */
+ if (status < 0) {
+ log_debug("Failed to retrieve serial number\n");
+ memset(info->serial, 0, BLADERF_SERIAL_LENGTH);
+ }
+
+ status = libusb_get_string_descriptor_ascii(
+ handle, desc.iManufacturer,
+ (unsigned char *)&info->manufacturer,
+ BLADERF_DESCRIPTION_LENGTH);
+
+ if (status < 0) {
+ log_debug("Failed to retrieve manufacturer string\n");
+ memset(info->manufacturer, 0, BLADERF_DESCRIPTION_LENGTH);
+ }
+
+ status = libusb_get_string_descriptor_ascii(
+ handle, desc.iProduct, (unsigned char *)&info->product,
+ BLADERF_DESCRIPTION_LENGTH);
+
+ if (status < 0) {
+ log_debug("Failed to retrieve product string\n");
+ memset(info->product, 0, BLADERF_DESCRIPTION_LENGTH);
+ }
+
+ log_debug("Bus %03d Device %03d: %s %s, serial %s\n", info->usb_bus,
+ info->usb_addr, info->manufacturer, info->product,
+ info->serial);
+
+ if (status > 0) {
+ status = 0;
+ }
+ }
+
+ libusb_close(handle);
+ }
+
+ return status;
+}
+
+static bool device_has_vid_pid(libusb_device *dev, uint16_t vid, uint16_t pid)
+{
+ int status;
+ struct libusb_device_descriptor desc;
+ bool match = false;
+
+ status = libusb_get_device_descriptor(dev, &desc);
+ if (status != 0) {
+ log_debug("Couldn't get device descriptor: %s\n",
+ libusb_error_name(status));
+ } else {
+ match = (desc.idVendor == vid) && (desc.idProduct == pid);
+ }
+
+ return match;
+}
+
+static bool device_is_fx3_bootloader(libusb_device *dev)
+{
+ return device_has_vid_pid(dev, USB_CYPRESS_VENDOR_ID, USB_FX3_PRODUCT_ID) ||
+ device_has_vid_pid(dev, USB_NUAND_VENDOR_ID, USB_NUAND_BLADERF_BOOT_PRODUCT_ID) ||
+ device_has_vid_pid(dev, USB_NUAND_LEGACY_VENDOR_ID, USB_NUAND_BLADERF_LEGACY_BOOT_PRODUCT_ID);
+}
+
+static inline bool device_has_bladeRF_ids(libusb_device *dev)
+{
+ return device_has_vid_pid(dev, USB_NUAND_VENDOR_ID, USB_NUAND_BLADERF_PRODUCT_ID) ||
+ device_has_vid_pid(dev, USB_NUAND_VENDOR_ID, USB_NUAND_BLADERF2_PRODUCT_ID) ||
+ device_has_vid_pid(dev, USB_NUAND_LEGACY_VENDOR_ID, USB_NUAND_BLADERF_LEGACY_PRODUCT_ID);
+
+}
+
+static bool device_is_bladerf(libusb_device *dev)
+{
+ struct libusb_config_descriptor *cfg;
+ int status;
+ bool ret;
+
+ if (!device_has_bladeRF_ids(dev)) {
+ return false;
+ }
+
+ status = libusb_get_config_descriptor(dev, 0, &cfg);
+ if (status != 0) {
+ log_debug("Failed to get configuration descriptor: %s\n",
+ libusb_error_name(status));
+ return false;
+ }
+
+ /* As of firmware v0.4, we expect there to be 4 altsettings on the
+ * first interface. */
+ if (cfg->interface->num_altsetting != 4) {
+ const uint8_t bus = libusb_get_bus_number(dev);
+ const uint8_t addr = libusb_get_device_address(dev);
+
+ log_warning("A bladeRF running incompatible firmware appears to be "
+ "present on bus=%u, addr=%u. If this is true, a firmware "
+ "update via the device's bootloader is required.\n\n",
+ bus, addr);
+
+ ret = false;
+ } else {
+ ret = true;
+ }
+
+ libusb_free_config_descriptor(cfg);
+ return ret;
+}
+
+static bool device_is_probe_target(backend_probe_target probe_target,
+ libusb_device *dev)
+{
+ bool is_probe_target = false;
+
+ switch (probe_target) {
+ case BACKEND_PROBE_BLADERF:
+ is_probe_target = device_is_bladerf(dev);
+ if (is_probe_target) {
+ log_verbose("Found a bladeRF\n");
+ }
+ break;
+
+ case BACKEND_PROBE_FX3_BOOTLOADER:
+ is_probe_target = device_is_fx3_bootloader(dev);
+ if (is_probe_target) {
+ log_verbose("Found an FX3 bootloader.\n");
+ }
+ break;
+
+ default:
+ assert(!"Invalid probe target");
+ }
+
+ return is_probe_target;
+}
+
+static int lusb_probe(backend_probe_target probe_target,
+ struct bladerf_devinfo_list *info_list)
+{
+ int status, i, n;
+ ssize_t count;
+ libusb_device **list;
+ struct bladerf_devinfo info;
+ bool printed_access_warning = false;
+
+ libusb_context *context;
+
+ /* Initialize libusb for device tree walking */
+ status = libusb_init(&context);
+ if (status) {
+ log_error("Could not initialize libusb: %s\n",
+ libusb_error_name(status));
+ goto lusb_probe_done;
+ }
+
+ count = libusb_get_device_list(context, &list);
+ /* Iterate through all the USB devices */
+ for (i = 0, n = 0; i < count && status == 0; i++) {
+ if (device_is_probe_target(probe_target, list[i])) {
+ bool do_add = true;
+
+ /* Open the USB device and get some information */
+ status = get_devinfo(list[i], &info);
+ if (status) {
+ /* We may not be able to open the device if another driver
+ * (e.g., CyUSB3) is associated with it. Therefore, just log to
+ * the debug level and carry on. */
+ log_debug("Could not open device: %s\n",
+ libusb_error_name(status));
+
+ if (status == LIBUSB_ERROR_ACCESS) {
+ /* If it's an access error, odds are good this is happening
+ * because we've already got the device open. Pass the info
+ * we have back to the caller. */
+ do_add = true;
+
+ if (!printed_access_warning) {
+ printed_access_warning = true;
+ log_warning(
+ "Found a bladeRF via VID/PID, but could not open "
+ "it due to insufficient permissions, or because "
+ "the device is already open.\n");
+ }
+ } else {
+ do_add = false;
+ }
+
+ /* Don't stop probing because one device was "problematic" */
+ status = 0;
+ }
+
+ if (do_add) {
+ info.instance = n++;
+
+ status = bladerf_devinfo_list_add(info_list, &info);
+ if (status) {
+ log_error("Could not add device to list: %s\n",
+ bladerf_strerror(status));
+ } else {
+ log_verbose("Added instance %d to device list\n",
+ info.instance);
+ }
+ }
+ }
+ }
+
+ libusb_free_device_list(list, 1);
+ libusb_exit(context);
+
+lusb_probe_done:
+ return status;
+}
+
+#ifdef HAVE_LIBUSB_GET_VERSION
+static inline void get_libusb_version(char *buf, size_t buf_len)
+{
+ const struct libusb_version *version;
+ version = libusb_get_version();
+
+ snprintf(buf, buf_len, "%d.%d.%d.%d%s", version->major, version->minor,
+ version->micro, version->nano, version->rc);
+}
+#else
+static void get_libusb_version(char *buf, size_t buf_len)
+{
+ snprintf(buf, buf_len, "<= 1.0.9");
+}
+#endif
+
+static int create_device_mutex(const struct bladerf_devinfo *info,
+ struct bladerf_lusb *dev)
+{
+ int status = 0;
+
+#if 1 == BLADERF_OS_WINDOWS
+ const char prefix[] = "Global\\bladeRF-";
+ const size_t mutex_name_len = strlen(prefix) + BLADERF_SERIAL_LENGTH;
+ char *mutex_name = (char *)calloc(mutex_name_len, 1);
+ DWORD last_error;
+
+ if (NULL == mutex_name) {
+ return BLADERF_ERR_MEM;
+ }
+
+ snprintf(mutex_name, mutex_name_len, "%s%s", prefix, info->serial);
+ log_verbose("Mutex name: %s\n", mutex_name);
+
+ SetLastError(0);
+ dev->mutex = CreateMutex(NULL, true, mutex_name);
+ last_error = GetLastError();
+ if (NULL == dev->mutex || last_error != 0) {
+ log_debug("Unable to create device mutex: mutex=%p, last_error=%ld\n",
+ dev->mutex, last_error);
+ status = BLADERF_ERR_NODEV;
+ ReleaseMutex(dev->mutex);
+ CloseHandle(dev->mutex);
+ }
+
+ free(mutex_name);
+#endif // BLADERF_OS_WINDOWS
+
+ return status;
+}
+
+static int open_device(const struct bladerf_devinfo *info,
+ libusb_context *context,
+ libusb_device *libusb_dev_in,
+ struct bladerf_lusb **dev_out)
+{
+ int status;
+ struct bladerf_lusb *dev;
+
+ *dev_out = NULL;
+
+ dev = (struct bladerf_lusb *) calloc(1, sizeof(dev[0]));
+ if (dev == NULL) {
+ log_debug("Failed to allocate handle for instance %d.\n",
+ info->instance);
+
+ /* Report "no device" so we could try again with
+ * another matching device */
+ return BLADERF_ERR_NODEV;
+ }
+
+ dev->context = context;
+ dev->dev = libusb_dev_in;
+
+ status = libusb_open(libusb_dev_in, &dev->handle);
+ if (status < 0) {
+ log_debug("Failed to open device instance %d: %s\n",
+ info->instance, libusb_error_name(status));
+
+ status = error_conv(status);
+ goto out;
+ }
+
+ status = libusb_claim_interface(dev->handle, 0);
+ if (status < 0) {
+ log_debug("Failed to claim interface 0 for instance %d: %s\n",
+ info->instance, libusb_error_name(status));
+
+ status = error_conv(status);
+ goto out;
+ }
+
+ status = create_device_mutex(info, dev);
+ if (status < 0) {
+ log_debug("Failed to get device mutex for instance %d: %s\n",
+ info->instance, bladerf_strerror(status));
+
+ status = error_conv(status);
+ goto out;
+ }
+
+out:
+ if (status != 0) {
+ if (dev->handle != NULL) {
+ libusb_close(dev->handle);
+ }
+
+ free(dev);
+ } else {
+ *dev_out = dev;
+ }
+
+ return status;
+}
+
+static int find_and_open_device(libusb_context *context,
+ const struct bladerf_devinfo *info_in,
+ struct bladerf_lusb **dev_out,
+ struct bladerf_devinfo *info_out)
+{
+ int status = BLADERF_ERR_NODEV;
+ int i, n;
+ ssize_t count;
+ struct libusb_device **list;
+ struct bladerf_devinfo curr_info;
+ bool printed_access_warning = false;
+
+ *dev_out = NULL;
+
+ count = libusb_get_device_list(context, &list);
+ if (count < 0) {
+ if (count < INT_MIN) {
+ /* Ensure we don't have a situation where we accidentally return 0
+ * due to a narrowing conversion */
+ return BLADERF_ERR_UNEXPECTED;
+ } else {
+ return error_conv((int) count);
+ }
+ }
+
+ for (i = 0, n = 0; (i < count) && (*dev_out == NULL); i++) {
+ if (device_is_bladerf(list[i])) {
+ log_verbose("Found a bladeRF (idx=%d)\n", i);
+
+ /* Open the USB device and get some information */
+ status = get_devinfo(list[i], &curr_info);
+ if (status < 0) {
+
+ /* Give the user a helpful hint in case the have forgotten
+ * to update their udev rules */
+ if (status == LIBUSB_ERROR_ACCESS && !printed_access_warning) {
+ printed_access_warning = true;
+ log_warning("Found a bladeRF via VID/PID, but could not "
+ "open it due to insufficient permissions.\n");
+ } else {
+ log_debug("Could not open bladeRF device: %s\n",
+ libusb_error_name(status) );
+ }
+
+ status = BLADERF_ERR_NODEV;
+ continue; /* Continue trying the next devices */
+ } else {
+ curr_info.instance = n++;
+ }
+
+ /* Check to see if this matches the info struct */
+ if (bladerf_devinfo_matches(&curr_info, info_in)) {
+ status = open_device(&curr_info, context, list[i], dev_out);
+ if (status < 0) {
+ status = BLADERF_ERR_NODEV;
+ continue; /* Continue trying the next matching device */
+ } else {
+ memcpy(info_out, &curr_info, sizeof(info_out[0]));
+ }
+ } else {
+ status = BLADERF_ERR_NODEV;
+
+ log_verbose("Devinfo doesn't match - skipping"
+ "(instance=%d, serial=%d, bus/addr=%d\n",
+ bladerf_instance_matches(&curr_info, info_in),
+ bladerf_serial_matches(&curr_info, info_in),
+ bladerf_bus_addr_matches(&curr_info, info_in));
+ }
+ }
+ }
+
+ if (status == 0) {
+ /* Returning 0 indicates this function is providing a device */
+ assert(*dev_out != NULL);
+ }
+
+ libusb_free_device_list(list, 1);
+ return status;
+}
+
+#if ENABLE_USB_DEV_RESET_ON_OPEN
+static int reset_and_reopen(libusb_context *context,
+ struct bladerf_lusb **dev,
+ struct bladerf_devinfo *info)
+{
+ int status;
+
+ status = libusb_reset_device((*dev)->handle);
+ if (status == 0) {
+ log_verbose("USB port reset succeeded for bladeRF %s\n", info->serial);
+ return 0;
+ } else if (status == LIBUSB_ERROR_NO_DEVICE) {
+ struct bladerf_devinfo new_info;
+
+ /* The reset has caused the device to drop out and re-enumerate.
+ *
+ * We'll find it again via the info we gathered about it via its
+ * serial number, which is now stored in the devinfo
+ */
+ log_verbose("Re-scan required after port reset for bladeRF %s\n",
+ info->serial);
+
+
+ libusb_release_interface((*dev)->handle, 0);
+ libusb_close((*dev)->handle);
+#if 1 == BLADERF_OS_WINDOWS
+ ReleaseMutex((*dev)->mutex);
+ CloseHandle((*dev)->mutex);
+#endif // BLADERF_OS_WINDOWS
+
+ *dev = NULL;
+
+ memcpy(&new_info, info, sizeof(new_info));
+ new_info.usb_bus = DEVINFO_BUS_ANY;
+ new_info.usb_addr = DEVINFO_ADDR_ANY;
+
+ status = find_and_open_device(context, &new_info, dev, info);
+
+ } else {
+ status = BLADERF_ERR_IO;
+ log_verbose("Port reset failed for bladerf %s: %s\n",
+ info->serial, libusb_error_name(status));
+ }
+
+ return status;
+}
+#endif
+
+static int lusb_open(void **driver,
+ struct bladerf_devinfo *info_in,
+ struct bladerf_devinfo *info_out)
+{
+ int status;
+ struct bladerf_lusb *lusb = NULL;
+ libusb_context *context;
+
+ /* Initialize libusb for device tree walking */
+ status = libusb_init(&context);
+ if (status) {
+ log_error("Could not initialize libusb: %s\n",
+ libusb_error_name(status));
+ return error_conv(status);
+ }
+
+ /* We can only print this out when log output is enabled, or else we'll
+ * get snagged by -Werror=unused-but-set-variable */
+# ifdef LOGGING_ENABLED
+ {
+ char buf[64];
+ get_libusb_version(buf, sizeof(buf));
+ log_verbose("Using libusb version: %s\n", buf);
+ }
+# endif
+
+ status = find_and_open_device(context, info_in, &lusb, info_out);
+ if (status != 0) {
+ libusb_exit(context);
+
+ if (status == BLADERF_ERR_NODEV) {
+ log_debug("No devices available on the libusb backend.\n");
+ } else {
+ log_debug("Failed to open bladeRF on libusb backend: %s\n",
+ bladerf_strerror(status));
+ }
+ } else {
+ assert(lusb != NULL);
+
+ /* Cosmin and Marian from Null Team (null.ro) and YateBTS(.com) found
+ * that it is possible to recover from "Issue #95: Not enough bandwidth
+ * for altsetting" by performing a USB port reset prior to actually
+ * trying to use the device.
+ */
+# if ENABLE_USB_DEV_RESET_ON_OPEN
+ if (bladerf_usb_reset_device_on_open) {
+ status = reset_and_reopen(context, &lusb, info_out);
+ }
+# endif
+
+ if (status == 0) {
+ *driver = (void *) lusb;
+ }
+ }
+
+ return status;
+}
+
+static int lusb_change_setting(void *driver, uint8_t setting)
+{
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+
+ int status = libusb_set_interface_alt_setting(lusb->handle, 0, setting);
+
+ return error_conv(status);
+}
+
+static void lusb_close(void *driver)
+{
+ int status;
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+
+ status = libusb_release_interface(lusb->handle, 0);
+ if (status < 0) {
+ log_error("Failed to release interface: %s\n",
+ libusb_error_name(status));
+ }
+
+ libusb_close(lusb->handle);
+ libusb_exit(lusb->context);
+#if 1 == BLADERF_OS_WINDOWS
+ ReleaseMutex(lusb->mutex);
+ CloseHandle(lusb->mutex);
+#endif // BLADERF_OS_WINDOWS
+ free(lusb);
+}
+
+static void lusb_close_bootloader(void *driver)
+{
+ int status;
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+
+ if (lusb != NULL) {
+ if (lusb->handle != NULL) {
+ status = libusb_release_interface(lusb->handle, 0);
+ if (status < 0) {
+ log_debug("Failed to release interface: %s\n",
+ libusb_error_name(status));
+ }
+
+ libusb_close(lusb->handle);
+ }
+
+ if (lusb->context != NULL) {
+ libusb_exit(lusb->context);
+ }
+
+#if 1 == BLADERF_OS_WINDOWS
+ ReleaseMutex(lusb->mutex);
+ CloseHandle(lusb->mutex);
+#endif // BLADERF_OS_WINDOWS
+
+ free(lusb);
+ }
+}
+
+static inline bool bus_matches(uint8_t bus, struct libusb_device *d)
+{
+ return (bus == DEVINFO_BUS_ANY) ||
+ (bus == libusb_get_bus_number(d));
+}
+
+static inline bool addr_matches(uint8_t addr, struct libusb_device *d)
+{
+ return (addr == DEVINFO_BUS_ANY) ||
+ (addr == libusb_get_device_address(d));
+}
+
+static int lusb_open_bootloader(void **driver, uint8_t bus, uint8_t addr)
+{
+ int status;
+ struct libusb_device **dev_list = NULL;
+ ssize_t dev_list_size, i;
+ struct bladerf_lusb *lusb;
+
+ *driver = NULL;
+
+ lusb = calloc(1, sizeof(lusb[0]));
+ if (lusb == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ status = libusb_init(&lusb->context);
+ if (status != 0) {
+ log_debug("Failed to initialize libusb context: %s\n",
+ libusb_error_name(status));
+ goto error;
+ }
+
+ dev_list_size = libusb_get_device_list(lusb->context, &dev_list);
+ if (dev_list_size < 0) {
+ log_debug("Failed to get device list: %s\n", libusb_error_name(status));
+ status = (int) dev_list_size;
+ goto error;
+ }
+
+ for (i = 0; i < dev_list_size; i++) {
+ if (device_is_fx3_bootloader(dev_list[i]) &&
+ bus_matches(bus, dev_list[i]) &&
+ addr_matches(addr, dev_list[i])) {
+
+
+ status = libusb_open(dev_list[i], &lusb->handle);
+ if (status != 0) {
+ log_debug("Failed to open device: %s\n",
+ libusb_error_name(status));
+ goto error;
+ } else {
+ status = libusb_claim_interface(lusb->handle, 0);
+ if (status < 0) {
+ log_debug("Failed to claim interface: %s\n",
+ libusb_error_name(status));
+
+ goto error;
+ } else {
+ log_verbose("Opened bootloader at %u:%u\n",
+ libusb_get_bus_number(dev_list[i]),
+ libusb_get_device_address(dev_list[i]));
+ *driver = lusb;
+ }
+ break;
+ }
+ }
+ }
+
+error:
+ if (dev_list != NULL) {
+ libusb_free_device_list(dev_list, 1);
+ }
+
+ if (status != 0) {
+ status = error_conv(status);
+ lusb_close_bootloader(lusb);
+ } else if (*driver == NULL) {
+ status = BLADERF_ERR_NODEV;
+ lusb_close_bootloader(lusb);
+ }
+
+ return status;
+}
+
+static int lusb_get_vid_pid(void *driver, uint16_t *vid,
+ uint16_t *pid)
+{
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *)driver;
+ struct libusb_device_descriptor desc;
+ int status;
+
+ status = libusb_get_device_descriptor(lusb->dev, &desc);
+ if (status != 0) {
+ log_debug("Couldn't get device descriptor: %s\n",
+ libusb_error_name(status));
+ return BLADERF_ERR_IO;
+ }
+
+ *vid = desc.idVendor;
+ *pid = desc.idProduct;
+
+ return 0;
+}
+
+static int lusb_get_handle(void *driver, void **handle)
+{
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+ *handle = lusb->handle;
+
+ return 0;
+}
+static int lusb_get_speed(void *driver,
+ bladerf_dev_speed *device_speed)
+{
+ int speed;
+ int status = 0;
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+
+ speed = libusb_get_device_speed(lusb->dev);
+ if (speed == LIBUSB_SPEED_SUPER) {
+ *device_speed = BLADERF_DEVICE_SPEED_SUPER;
+ } else if (speed == LIBUSB_SPEED_HIGH) {
+ *device_speed = BLADERF_DEVICE_SPEED_HIGH;
+ } else {
+ *device_speed = BLADERF_DEVICE_SPEED_UNKNOWN;
+
+ if (speed == LIBUSB_SPEED_FULL) {
+ log_debug("Full speed connection is not suppored.\n");
+ status = BLADERF_ERR_UNSUPPORTED;
+ } else if (speed == LIBUSB_SPEED_LOW) {
+ log_debug("Low speed connection is not supported.\n");
+ status = BLADERF_ERR_UNSUPPORTED;
+ } else {
+ log_debug("Unknown/unexpected device speed (%d)\n", speed);
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+ }
+
+ return status;
+}
+
+static inline uint8_t bm_request_type(usb_target target_type,
+ usb_request req_type,
+ usb_direction direction)
+{
+ uint8_t ret = 0;
+
+ switch (target_type) {
+ case USB_TARGET_DEVICE:
+ ret |= LIBUSB_RECIPIENT_DEVICE;
+ break;
+
+ case USB_TARGET_INTERFACE:
+ ret |= LIBUSB_RECIPIENT_INTERFACE;
+ break;
+
+ case USB_TARGET_ENDPOINT:
+ ret |= LIBUSB_RECIPIENT_ENDPOINT;
+ break;
+
+ default:
+ ret |= LIBUSB_RECIPIENT_OTHER;
+
+ }
+
+ switch (req_type) {
+ case USB_REQUEST_STANDARD:
+ ret |= LIBUSB_REQUEST_TYPE_STANDARD;
+ break;
+
+ case USB_REQUEST_CLASS:
+ ret |= LIBUSB_REQUEST_TYPE_CLASS;
+ break;
+
+ case USB_REQUEST_VENDOR:
+ ret |= LIBUSB_REQUEST_TYPE_VENDOR;
+ break;
+ }
+
+ switch (direction) {
+ case USB_DIR_HOST_TO_DEVICE:
+ ret |= LIBUSB_ENDPOINT_OUT;
+ break;
+
+ case USB_DIR_DEVICE_TO_HOST:
+ ret |= LIBUSB_ENDPOINT_IN;
+ break;
+ }
+
+ return ret;
+}
+
+static int lusb_control_transfer(void *driver,
+ usb_target target_type, usb_request req_type,
+ usb_direction dir, uint8_t request,
+ uint16_t wvalue, uint16_t windex,
+ void *buffer, uint32_t buffer_len,
+ uint32_t timeout_ms)
+{
+
+ int status;
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+ const uint8_t bm_req_type = bm_request_type(target_type, req_type, dir);
+
+ status = libusb_control_transfer(lusb->handle,
+ bm_req_type, request,
+ wvalue, windex,
+ buffer, buffer_len,
+ timeout_ms);
+
+ if (status >= 0 && (uint32_t)status == buffer_len) {
+ status = 0;
+ } else {
+ log_debug("%s failed: status = %d\n", __FUNCTION__, status);
+ }
+
+ return error_conv(status);
+}
+
+static int lusb_bulk_transfer(void *driver, uint8_t endpoint, void *buffer,
+ uint32_t buffer_len, uint32_t timeout_ms)
+{
+ int status;
+ int n_transferred;
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+
+ status = libusb_bulk_transfer(lusb->handle, endpoint, buffer, buffer_len,
+ &n_transferred, timeout_ms);
+
+ status = error_conv(status);
+ if (status == 0 && ((uint32_t)n_transferred != buffer_len)) {
+ log_debug("Short bulk transfer: requested=%u, transferred=%u\n",
+ buffer_len, n_transferred);
+ status = BLADERF_ERR_IO;
+ }
+
+ return status;
+}
+
+static int lusb_get_string_descriptor(void *driver, uint8_t index,
+ void *buffer, uint32_t buffer_len)
+{
+ int status;
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+
+ status = libusb_get_string_descriptor_ascii(lusb->handle, index,
+ (unsigned char*)buffer,
+ buffer_len);
+
+ if (status > 0 && (uint32_t)status < buffer_len) {
+ status = 0;
+ } else {
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ return status;
+}
+
+/* At the risk of being a little inefficient, just keep attempting to cancel
+ * everything. If a transfer's no longer active, we'll just get a NOT_FOUND
+ * error -- no big deal. Just accepting that alleviates the need to track
+ * the status of each transfer...
+ */
+static inline void cancel_all_transfers(struct bladerf_stream *stream)
+{
+ size_t i;
+ int status;
+ struct lusb_stream_data *stream_data = stream->backend_data;
+
+ for (i = 0; i < stream_data->num_transfers; i++) {
+ if (stream_data->transfer_status[i] == TRANSFER_IN_FLIGHT) {
+ status = libusb_cancel_transfer(stream_data->transfers[i]);
+ if (status < 0 && status != LIBUSB_ERROR_NOT_FOUND) {
+ log_error("Error canceling transfer (%d): %s\n",
+ status, libusb_error_name(status));
+ } else {
+ stream_data->transfer_status[i] = TRANSFER_CANCEL_PENDING;
+ }
+ }
+ }
+}
+
+static inline size_t transfer_idx(struct lusb_stream_data *stream_data,
+ struct libusb_transfer *transfer)
+{
+ size_t i;
+ for (i = 0; i < stream_data->num_transfers; i++) {
+ if (stream_data->transfers[i] == transfer) {
+ return i;
+ }
+ }
+
+ return UINT_MAX;
+}
+
+static int submit_transfer(struct bladerf_stream *stream, void *buffer, size_t len);
+
+static void LIBUSB_CALL lusb_stream_cb(struct libusb_transfer *transfer)
+{
+ struct bladerf_stream *stream = transfer->user_data;
+ void *next_buffer = NULL;
+ struct bladerf_metadata metadata;
+ struct lusb_stream_data *stream_data = stream->backend_data;
+ size_t transfer_i;
+
+ /* Currently unused - zero out for out own debugging sanity... */
+ memset(&metadata, 0, sizeof(metadata));
+
+ MUTEX_LOCK(&stream->lock);
+
+ transfer_i = transfer_idx(stream_data, transfer);
+ assert(stream_data->transfer_status[transfer_i] == TRANSFER_IN_FLIGHT ||
+ stream_data->transfer_status[transfer_i] == TRANSFER_CANCEL_PENDING);
+
+ if (transfer_i >= stream_data->num_transfers) {
+ log_error("Unable to find transfer\n");
+ stream->state = STREAM_SHUTTING_DOWN;
+ } else {
+ stream_data->transfer_status[transfer_i] = TRANSFER_AVAIL;
+ stream_data->num_avail++;
+ pthread_cond_signal(&stream->can_submit_buffer);
+ }
+
+ /* Check to see if the transfer has been cancelled or errored */
+ if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+ /* Errored out for some reason .. */
+ stream->state = STREAM_SHUTTING_DOWN;
+
+ switch (transfer->status) {
+ case LIBUSB_TRANSFER_CANCELLED:
+ /* We expect this case when we begin tearing down the stream */
+ break;
+
+ case LIBUSB_TRANSFER_STALL:
+ log_error("Hit stall for buffer %p\n\r", transfer->buffer);
+ stream->error_code = BLADERF_ERR_IO;
+ break;
+
+ case LIBUSB_TRANSFER_ERROR:
+ log_error("Got transfer error for buffer %p\n\r",
+ transfer->buffer);
+ stream->error_code = BLADERF_ERR_IO;
+ break;
+
+ case LIBUSB_TRANSFER_OVERFLOW:
+ log_error("Got transfer over for buffer %p, "
+ "transfer \"actual_length\" = %d\n\r",
+ transfer->buffer, transfer->actual_length);
+ stream->error_code = BLADERF_ERR_IO;
+ break;
+
+ case LIBUSB_TRANSFER_TIMED_OUT:
+ log_error("Transfer timed out for buffer %p\n\r",
+ transfer->buffer);
+ stream->error_code = BLADERF_ERR_TIMEOUT;
+ break;
+
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ stream->error_code = BLADERF_ERR_NODEV;
+ break;
+
+ default:
+ log_error("Unexpected transfer status: %d\n\r", transfer->status);
+ break;
+ }
+ }
+
+ if (stream->state == STREAM_RUNNING) {
+ if (stream->format == BLADERF_FORMAT_PACKET_META) {
+ /* Call user callback requesting more data to transmit */
+ next_buffer = stream->cb(
+ stream->dev, stream, &metadata, transfer->buffer,
+ bytes_to_samples(stream->format, transfer->actual_length), stream->user_data);
+ } else {
+ /* Sanity check for debugging purposes */
+ if (transfer->length != transfer->actual_length) {
+ log_warning("Received short transfer\n");
+ }
+
+ /* Call user callback requesting more data to transmit */
+ next_buffer = stream->cb(
+ stream->dev, stream, &metadata, transfer->buffer,
+ bytes_to_samples(stream->format, transfer->actual_length), stream->user_data);
+ }
+
+ if (next_buffer == BLADERF_STREAM_SHUTDOWN) {
+ stream->state = STREAM_SHUTTING_DOWN;
+ } else if (next_buffer != BLADERF_STREAM_NO_DATA) {
+ int status;
+ if((stream->layout & BLADERF_DIRECTION_MASK) == BLADERF_TX
+ && stream->format == BLADERF_FORMAT_PACKET_META) {
+ status = submit_transfer(stream, next_buffer, metadata.actual_count);
+ } else {
+ status = submit_transfer(stream, next_buffer, async_stream_buf_bytes(stream));
+ }
+ if (status != 0) {
+ /* If this fails, we probably have a serious problem...so just
+ * shut it down. */
+ stream->state = STREAM_SHUTTING_DOWN;
+ }
+ }
+ }
+
+
+ /* Check to see if all the transfers have been cancelled,
+ * and if so, clean up the stream */
+ if (stream->state == STREAM_SHUTTING_DOWN) {
+ /* We know we're done when all of our transfers have returned to their
+ * "available" states */
+ if (stream_data->num_avail == stream_data->num_transfers) {
+ stream->state = STREAM_DONE;
+ } else {
+ cancel_all_transfers(stream);
+ }
+ }
+
+ MUTEX_UNLOCK(&stream->lock);
+}
+
+static inline struct libusb_transfer *
+get_next_available_transfer(struct lusb_stream_data *stream_data)
+{
+ unsigned int n;
+ size_t i = stream_data->i;
+
+ for (n = 0; n < stream_data->num_transfers; n++) {
+ if (stream_data->transfer_status[i] == TRANSFER_AVAIL) {
+
+ if (stream_data->i != i &&
+ stream_data->out_of_order_event == false) {
+
+ log_warning("Transfer callback occurred out of order. "
+ "(Warning only this time.)\n");
+ stream_data->out_of_order_event = true;
+ }
+
+ stream_data->i = i;
+ return stream_data->transfers[i];
+ }
+
+ i = (i + 1) % stream_data->num_transfers;
+ }
+
+ return NULL;
+}
+
+/* Precondition: A transfer is available. */
+static int submit_transfer(struct bladerf_stream *stream, void *buffer, size_t len)
+{
+ int status;
+ struct bladerf_lusb *lusb = lusb_backend(stream->dev);
+ struct lusb_stream_data *stream_data = stream->backend_data;
+ struct libusb_transfer *transfer;
+ const size_t bytes_per_buffer = async_stream_buf_bytes(stream);
+ size_t prev_idx;
+ const unsigned char ep =
+ (stream->layout & BLADERF_DIRECTION_MASK) == BLADERF_TX ? SAMPLE_EP_OUT : SAMPLE_EP_IN;
+
+ transfer = get_next_available_transfer(stream_data);
+ assert(transfer != NULL);
+
+ assert(bytes_per_buffer <= INT_MAX);
+ libusb_fill_bulk_transfer(transfer,
+ lusb->handle,
+ ep,
+ buffer,
+ (int)len,
+ lusb_stream_cb,
+ stream,
+ stream->transfer_timeout);
+
+ prev_idx = stream_data->i;
+ stream_data->transfer_status[stream_data->i] = TRANSFER_IN_FLIGHT;
+ stream_data->i = (stream_data->i + 1) % stream_data->num_transfers;
+ assert(stream_data->num_avail != 0);
+ stream_data->num_avail--;
+
+ /* FIXME We have an inherent issue here with lock ordering between
+ * stream->lock and libusb's underlying event lock, so we
+ * have to drop the stream->lock as a workaround.
+ *
+ * This implies that a callback can potentially execute,
+ * including a callback for this transfer. Therefore, the transfer
+ * has been setup and its metadata logged.
+ *
+ * Ultimately, we need to review our async scheme and associated
+ * lock schemes.
+ */
+ MUTEX_UNLOCK(&stream->lock);
+ status = libusb_submit_transfer(transfer);
+ MUTEX_LOCK(&stream->lock);
+
+ if (status != 0) {
+ log_error("Failed to submit transfer in %s: %s\n",
+ __FUNCTION__, libusb_error_name(status));
+
+ /* We need to undo the metadata we updated prior to dropping
+ * the lock and attempting to submit the transfer */
+ assert(stream_data->transfer_status[prev_idx] == TRANSFER_IN_FLIGHT);
+ stream_data->transfer_status[prev_idx] = TRANSFER_AVAIL;
+ stream_data->num_avail++;
+ if (stream_data->i == 0) {
+ stream_data->i = stream_data->num_transfers - 1;
+ } else {
+ stream_data->i--;
+ }
+ }
+
+ return error_conv(status);
+}
+
+static int lusb_init_stream(void *driver, struct bladerf_stream *stream,
+ size_t num_transfers)
+{
+ int status = 0;
+ size_t i;
+ struct lusb_stream_data *stream_data;
+
+ /* Fill in backend stream information */
+ stream_data = malloc(sizeof(struct lusb_stream_data));
+ if (!stream_data) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Backend stream information */
+ stream->backend_data = stream_data;
+ stream_data->transfers = NULL;
+ stream_data->transfer_status = NULL;
+ stream_data->num_transfers = num_transfers;
+ stream_data->num_avail = 0;
+ stream_data->i = 0;
+ stream_data->out_of_order_event = false;
+
+ stream_data->transfers =
+ malloc(num_transfers * sizeof(struct libusb_transfer *));
+
+ if (stream_data->transfers == NULL) {
+ log_error("Failed to allocate libusb tranfers\n");
+ status = BLADERF_ERR_MEM;
+ goto error;
+ }
+
+ stream_data->transfer_status =
+ calloc(num_transfers, sizeof(transfer_status));
+
+ if (stream_data->transfer_status == NULL) {
+ log_error("Failed to allocated libusb transfer status array\n");
+ status = BLADERF_ERR_MEM;
+ goto error;
+ }
+
+ /* Create the libusb transfers */
+ for (i = 0; i < stream_data->num_transfers; i++) {
+ stream_data->transfers[i] = libusb_alloc_transfer(0);
+
+ /* Upon error, start tearing down anything we've started allocating
+ * and report that the stream is in a bad state */
+ if (stream_data->transfers[i] == NULL) {
+
+ /* Note: <= 0 not appropriate as we're dealing
+ * with an unsigned index */
+ while (i > 0) {
+ if (--i) {
+ libusb_free_transfer(stream_data->transfers[i]);
+ stream_data->transfers[i] = NULL;
+ stream_data->transfer_status[i] = TRANSFER_UNINITIALIZED;
+ stream_data->num_avail--;
+ }
+ }
+
+ status = BLADERF_ERR_MEM;
+ break;
+ } else {
+ stream_data->transfer_status[i] = TRANSFER_AVAIL;
+ stream_data->num_avail++;
+ }
+ }
+
+error:
+ if (status != 0) {
+ free(stream_data->transfer_status);
+ free(stream_data->transfers);
+ free(stream_data);
+ stream->backend_data = NULL;
+ }
+
+ return status;
+}
+
+static int lusb_stream(void *driver, struct bladerf_stream *stream,
+ bladerf_channel_layout layout)
+{
+ size_t i;
+ int status = 0;
+ void *buffer;
+ struct bladerf_metadata metadata;
+ struct bladerf *dev = stream->dev;
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+ struct lusb_stream_data *stream_data = stream->backend_data;
+ struct timeval tv = { 0, LIBUSB_HANDLE_EVENTS_TIMEOUT_NSEC };
+
+ /* Currently unused, so zero it out for a sanity check when debugging */
+ memset(&metadata, 0, sizeof(metadata));
+
+ MUTEX_LOCK(&stream->lock);
+
+ /* Set up initial set of buffers */
+ for (i = 0; i < stream_data->num_transfers; i++) {
+ if ((layout & BLADERF_DIRECTION_MASK) == BLADERF_TX) {
+ buffer = stream->cb(dev,
+ stream,
+ &metadata,
+ NULL,
+ stream->samples_per_buffer,
+ stream->user_data);
+
+ if (buffer == BLADERF_STREAM_SHUTDOWN) {
+ /* If we have transfers in flight and the user prematurely
+ * cancels the stream, we'll start shutting down */
+ if (stream_data->num_avail != stream_data->num_transfers) {
+ stream->state = STREAM_SHUTTING_DOWN;
+ } else {
+ /* No transfers have been shipped out yet so we can
+ * simply enter our "done" state */
+ stream->state = STREAM_DONE;
+ }
+
+ /* In either of the above we don't want to attempt to
+ * get any more buffers from the user */
+ break;
+ }
+ } else {
+ buffer = stream->buffers[i];
+ }
+
+ if (buffer != BLADERF_STREAM_NO_DATA) {
+ if((layout & BLADERF_DIRECTION_MASK) == BLADERF_TX
+ && stream->format == BLADERF_FORMAT_PACKET_META) {
+ status = submit_transfer(stream, buffer, metadata.actual_count);
+ } else {
+ status = submit_transfer(stream, buffer, async_stream_buf_bytes(stream));
+ }
+
+ /* If we failed to submit any transfers, cancel everything in
+ * flight. We'll leave the stream in the running state so we can
+ * have libusb fire off callbacks with the cancelled status*/
+ if (status < 0) {
+ stream->error_code = status;
+ cancel_all_transfers(stream);
+ }
+ }
+ }
+ MUTEX_UNLOCK(&stream->lock);
+
+ /* This loop is required so libusb can do callbacks and whatnot */
+ while (stream->state != STREAM_DONE) {
+ status = libusb_handle_events_timeout(lusb->context, &tv);
+
+ if (status < 0 && status != LIBUSB_ERROR_INTERRUPTED) {
+ log_warning("unexpected value from events processing: "
+ "%d: %s\n", status, libusb_error_name(status));
+ status = error_conv(status);
+ }
+ }
+
+ return status;
+}
+
+/* The top-level code will have aquired the stream->lock for us */
+int lusb_submit_stream_buffer(void *driver, struct bladerf_stream *stream,
+ void *buffer, size_t *length,
+ unsigned int timeout_ms, bool nonblock)
+{
+ int status = 0;
+ struct lusb_stream_data *stream_data = stream->backend_data;
+ struct timespec timeout_abs;
+
+ if (buffer == BLADERF_STREAM_SHUTDOWN) {
+ if (stream_data->num_avail == stream_data->num_transfers) {
+ stream->state = STREAM_DONE;
+ } else {
+ stream->state = STREAM_SHUTTING_DOWN;
+ }
+
+ return 0;
+ }
+
+ if (stream_data->num_avail == 0) {
+ if (nonblock) {
+ log_debug("Non-blocking buffer submission requested, but no "
+ "transfers are currently available.\n");
+
+ return BLADERF_ERR_WOULD_BLOCK;
+ }
+
+ if (timeout_ms != 0) {
+ status = populate_abs_timeout(&timeout_abs, timeout_ms);
+ if (status != 0) {
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ while (stream_data->num_avail == 0 && status == 0) {
+ status = pthread_cond_timedwait(&stream->can_submit_buffer,
+ &stream->lock,
+ &timeout_abs);
+ }
+ } else {
+ while (stream_data->num_avail == 0 && status == 0) {
+ status = pthread_cond_wait(&stream->can_submit_buffer,
+ &stream->lock);
+ }
+ }
+ }
+
+ if (status == ETIMEDOUT) {
+ log_debug("%s: Timed out waiting for a transfer to become available.\n",
+ __FUNCTION__);
+ return BLADERF_ERR_TIMEOUT;
+ } else if (status != 0) {
+ return BLADERF_ERR_UNEXPECTED;
+ } else {
+ return submit_transfer(stream, buffer, *length);
+ }
+}
+
+static int lusb_deinit_stream(void *driver, struct bladerf_stream *stream)
+{
+ size_t i;
+ struct lusb_stream_data *stream_data = stream->backend_data;
+
+ for (i = 0; i < stream_data->num_transfers; i++) {
+ libusb_free_transfer(stream_data->transfers[i]);
+ stream_data->transfers[i] = NULL;
+ stream_data->transfer_status[i] = TRANSFER_UNINITIALIZED;
+ }
+
+ free(stream_data->transfers);
+ free(stream_data->transfer_status);
+ free(stream->backend_data);
+
+ stream->backend_data = NULL;
+ return 0;
+}
+
+static const struct usb_fns libusb_fns = {
+ FIELD_INIT(.probe, lusb_probe),
+ FIELD_INIT(.open, lusb_open),
+ FIELD_INIT(.close, lusb_close),
+ FIELD_INIT(.get_vid_pid, lusb_get_vid_pid),
+ FIELD_INIT(.get_flash_id, NULL),
+ FIELD_INIT(.get_handle, lusb_get_handle),
+ FIELD_INIT(.get_speed, lusb_get_speed),
+ FIELD_INIT(.change_setting, lusb_change_setting),
+ FIELD_INIT(.control_transfer, lusb_control_transfer),
+ FIELD_INIT(.bulk_transfer, lusb_bulk_transfer),
+ FIELD_INIT(.get_string_descriptor, lusb_get_string_descriptor),
+ FIELD_INIT(.init_stream, lusb_init_stream),
+ FIELD_INIT(.stream, lusb_stream),
+ FIELD_INIT(.submit_stream_buffer, lusb_submit_stream_buffer),
+ FIELD_INIT(.deinit_stream, lusb_deinit_stream),
+ FIELD_INIT(.open_bootloader, lusb_open_bootloader),
+ FIELD_INIT(.close_bootloader, lusb_close_bootloader),
+};
+
+const struct usb_driver usb_driver_libusb = {
+ FIELD_INIT(.fn, &libusb_fns),
+ FIELD_INIT(.id, BLADERF_BACKEND_LIBUSB),
+};
diff --git a/Radio/HW/BladeRF/src/backend/usb/nios_access.c b/Radio/HW/BladeRF/src/backend/usb/nios_access.c
new file mode 100644
index 0000000..354d80b
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/usb/nios_access.c
@@ -0,0 +1,1322 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2015 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include "log.h"
+#include "conversions.h"
+
+#include "usb.h"
+#include "nios_access.h"
+#include "nios_pkt_formats.h"
+
+#include "board/board.h"
+#include "helpers/version.h"
+
+#if 0
+static void print_buf(const char *msg, const uint8_t *buf, size_t len)
+{
+ size_t i;
+ if (msg != NULL) {
+ fputs(msg, stderr);
+ }
+
+ for (i = 0; i < len; i++) {
+ fprintf(stderr, " %02x", buf[i]);
+ }
+
+ fputc('\n', stderr);
+}
+#else
+#define print_buf(msg, data, len) do {} while(0)
+#endif
+
+/* Buf is assumed to be NIOS_PKT_LEN bytes */
+static int nios_access(struct bladerf *dev, uint8_t *buf)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+ int status;
+
+ print_buf("NIOS II REQ:", buf, NIOS_PKT_LEN);
+
+ /* Send the command */
+ status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_OUT, buf,
+ NIOS_PKT_LEN, PERIPHERAL_TIMEOUT_MS);
+ if (status != 0) {
+ log_error("Failed to send NIOS II request: %s\n",
+ bladerf_strerror(status));
+ return status;
+ }
+
+ /* Retrieve the request */
+ status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_IN, buf,
+ NIOS_PKT_LEN, PERIPHERAL_TIMEOUT_MS);
+ if (status != 0) {
+ log_error("Failed to receive NIOS II response: %s\n",
+ bladerf_strerror(status));
+ }
+
+ print_buf("NIOS II res:", buf, NIOS_PKT_LEN);
+
+ return status;
+}
+
+/* Variant that doesn't output to log_error on error. */
+static int nios_access_quiet(struct bladerf *dev, uint8_t *buf)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+ int status;
+
+ print_buf("NIOS II REQ:", buf, NIOS_PKT_LEN);
+
+ /* Send the command */
+ status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_OUT, buf,
+ NIOS_PKT_LEN, PERIPHERAL_TIMEOUT_MS);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Retrieve the request */
+ status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_IN, buf,
+ NIOS_PKT_LEN, PERIPHERAL_TIMEOUT_MS);
+
+ print_buf("NIOS II res:", buf, NIOS_PKT_LEN);
+
+ return status;
+}
+
+static int nios_8x8_read(struct bladerf *dev, uint8_t id,
+ uint8_t addr, uint8_t *data)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ bool success;
+
+ nios_pkt_8x8_pack(buf, id, false, addr, 0);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_8x8_resp_unpack(buf, NULL, NULL, NULL, data, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ *data = 0;
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static int nios_8x8_write(struct bladerf *dev, uint8_t id,
+ uint8_t addr, uint8_t data)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ bool success;
+
+ nios_pkt_8x8_pack(buf, id, true, addr, data);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_8x8_resp_unpack(buf, NULL, NULL, NULL, NULL, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static int nios_8x16_read(struct bladerf *dev, uint8_t id,
+ uint8_t addr, uint16_t *data)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ bool success;
+ uint16_t tmp;
+
+ nios_pkt_8x16_pack(buf, id, false, addr, 0);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_8x16_resp_unpack(buf, NULL, NULL, NULL, &tmp, &success);
+
+ if (success) {
+ *data = tmp;
+ return 0;
+ } else {
+ *data = 0;
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static int nios_8x16_write(struct bladerf *dev, uint8_t id,
+ uint8_t addr, uint16_t data)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ bool success;
+
+ nios_pkt_8x16_pack(buf, id, true, addr, data);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_8x16_resp_unpack(buf, NULL, NULL, NULL, NULL, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static void log_debug_8x32_pkt(const uint8_t *buf) {
+ if (buf == NULL) {
+ log_debug("Failed to log packet: packet buffer is NULL\n");
+ return;
+ }
+
+ uint8_t target_id;
+ bool write;
+ uint8_t addr;
+ uint32_t data;
+ bool success;
+
+ nios_pkt_8x32_resp_unpack(buf, &target_id, &write, &addr, &data, &success);
+
+ const char *operation = write ? "Write" : "Read";
+ const char *status = success ? "Success" : "Failure";
+
+ log_debug("Packet Magic: 0x%02x\n", buf[NIOS_PKT_8x32_IDX_MAGIC]);
+ log_debug("Packet Target: %s\n", target2str(target_id));
+ log_debug("Packet Operation: %s\n", operation);
+ log_debug("Packet Address: 0x%02x\n", addr);
+ log_debug("Packet Data: 0x%08x\n", data);
+ log_debug("Packet Status: %s\n", status);
+}
+
+static int nios_8x32_read(struct bladerf *dev, uint8_t id,
+ uint8_t addr, uint32_t *data)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ bool success;
+
+ nios_pkt_8x32_pack(buf, id, false, addr, 0);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_8x32_resp_unpack(buf, NULL, NULL, NULL, data, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ *data = 0;
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ log_debug_8x32_pkt(buf);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static int nios_8x32_write(struct bladerf *dev, uint8_t id,
+ uint8_t addr, uint32_t data)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ bool success;
+
+ nios_pkt_8x32_pack(buf, id, true, addr, data);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_8x32_resp_unpack(buf, NULL, NULL, NULL, NULL, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ log_debug_8x32_pkt(buf);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static int nios_16x64_read(struct bladerf *dev,
+ uint8_t id,
+ uint16_t addr,
+ uint64_t *data)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ bool success;
+
+ nios_pkt_16x64_pack(buf, id, false, addr, 0);
+
+ /* RFIC access times out occasionally, and this is fine. */
+ if (NIOS_PKT_16x64_TARGET_RFIC == id) {
+ status = nios_access_quiet(dev, buf);
+ } else {
+ status = nios_access(dev, buf);
+ }
+
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_16x64_resp_unpack(buf, NULL, NULL, NULL, data, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ *data = 0;
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static int nios_16x64_write(struct bladerf *dev,
+ uint8_t id,
+ uint16_t addr,
+ uint64_t data)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ bool success;
+
+ nios_pkt_16x64_pack(buf, id, true, addr, data);
+
+ /* RFIC access times out occasionally, and this is fine. */
+ if (NIOS_PKT_16x64_TARGET_RFIC == id) {
+ status = nios_access_quiet(dev, buf);
+ } else {
+ status = nios_access(dev, buf);
+ }
+
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_16x64_resp_unpack(buf, NULL, NULL, NULL, NULL, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static int nios_32x32_read(struct bladerf *dev, uint32_t id,
+ uint32_t addr, uint32_t *data)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ bool success;
+
+ nios_pkt_32x32_pack(buf, id, false, addr, 0);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_32x32_resp_unpack(buf, NULL, NULL, NULL, data, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ *data = 0;
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static int nios_32x32_write(struct bladerf *dev, uint32_t id,
+ uint32_t addr, uint32_t data)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ bool success;
+
+ nios_pkt_32x32_pack(buf, id, true, addr, data);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_32x32_resp_unpack(buf, NULL, NULL, NULL, NULL, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static int nios_32x32_masked_read(struct bladerf *dev, uint8_t id,
+ uint32_t mask, uint32_t *val)
+{
+ int status;
+ bool success;
+ uint8_t buf[NIOS_PKT_LEN];
+
+ /* The address is used as a mask of bits to read and return */
+ nios_pkt_32x32_pack(buf, id, false, mask, 0);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_32x32_resp_unpack(buf, NULL, NULL, NULL, val, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ *val = 0;
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+static int nios_32x32_masked_write(struct bladerf *dev, uint8_t id,
+ uint32_t mask, uint32_t val)
+{
+ int status;
+ bool success;
+ uint8_t buf[NIOS_PKT_LEN];
+
+ /* The address is used as a mask of bits to read and return */
+ nios_pkt_32x32_pack(buf, id, true, mask, val);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_32x32_resp_unpack(buf, NULL, NULL, NULL, NULL, &success);
+
+ if (success) {
+ return 0;
+ } else {
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+int nios_config_read(struct bladerf *dev, uint32_t *val)
+{
+ int status = nios_8x32_read(dev, NIOS_PKT_8x32_TARGET_CONTROL, 0, val);
+
+ if (status == 0) {
+ log_verbose("%s: Read 0x%08x\n", __FUNCTION__, *val);
+ }
+
+ return status;
+}
+
+int nios_config_write(struct bladerf *dev, uint32_t val)
+{
+ int status = nios_8x32_write(dev, NIOS_PKT_8x32_TARGET_CONTROL, 0, val);
+
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%08x\n", __FUNCTION__, val);
+ } else {
+ log_error("%s: Failed to write 0x%08x\n", __FUNCTION__, val);
+ }
+
+ return status;
+}
+
+int nios_get_fpga_version(struct bladerf *dev, struct bladerf_version *ver)
+{
+ uint32_t regval;
+ int status = nios_8x32_read(dev, NIOS_PKT_8x32_TARGET_VERSION, 0, &regval);
+
+ if (status == 0) {
+ log_verbose("%s: Read FPGA version word: 0x%08x\n",
+ __FUNCTION__, regval);
+
+ ver->major = (regval >> 24) & 0xff;
+ ver->minor = (regval >> 16) & 0xff;
+ ver->patch = LE16_TO_HOST(regval & 0xffff);
+
+ snprintf((char*)ver->describe, BLADERF_VERSION_STR_MAX,
+ "%d.%d.%d", ver->major, ver->minor, ver->patch);
+
+ return 0;
+ }
+
+ return status;
+}
+
+int nios_get_timestamp(struct bladerf *dev,
+ bladerf_direction dir,
+ uint64_t *timestamp)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+ uint8_t addr;
+ bool success;
+
+ switch (dir) {
+ case BLADERF_RX:
+ addr = NIOS_PKT_8x64_TIMESTAMP_RX;
+ break;
+
+ case BLADERF_TX:
+ addr = NIOS_PKT_8x64_TIMESTAMP_TX;
+ break;
+
+ default:
+ log_debug("Invalid direction: %d\n", dir);
+ return BLADERF_ERR_INVAL;
+ }
+
+ nios_pkt_8x64_pack(buf, NIOS_PKT_8x64_TARGET_TIMESTAMP, false, addr, 0);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_8x64_resp_unpack(buf, NULL, NULL, NULL, timestamp, &success);
+
+ if (success) {
+ log_verbose("%s: Read %s timestamp: %" PRIu64 "\n", __FUNCTION__,
+ direction2str(dir), *timestamp);
+ return 0;
+ } else {
+ log_debug("%s: response packet reported failure.\n", __FUNCTION__);
+ *timestamp = 0;
+ return BLADERF_ERR_FPGA_OP;
+ }
+}
+
+int nios_si5338_read(struct bladerf *dev, uint8_t addr, uint8_t *data)
+{
+ int status = nios_8x8_read(dev, NIOS_PKT_8x8_TARGET_SI5338, addr, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Read 0x%02x from addr 0x%02x\n",
+ __FUNCTION__, *data, addr);
+ }
+#endif
+
+ return status;
+}
+
+int nios_si5338_write(struct bladerf *dev, uint8_t addr, uint8_t data)
+{
+ int status = nios_8x8_write(dev, NIOS_PKT_8x8_TARGET_SI5338, addr, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%02x to addr 0x%02x\n",
+ __FUNCTION__, data, addr);
+ }
+#endif
+
+ return status;
+}
+
+int nios_lms6_read(struct bladerf *dev, uint8_t addr, uint8_t *data)
+{
+ int status = nios_8x8_read(dev, NIOS_PKT_8x8_TARGET_LMS6, addr, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Read 0x%02x from addr 0x%02x\n",
+ __FUNCTION__, *data, addr);
+ }
+#endif
+
+ return status;
+}
+
+int nios_lms6_write(struct bladerf *dev, uint8_t addr, uint8_t data)
+{
+ int status = nios_8x8_write(dev, NIOS_PKT_8x8_TARGET_LMS6, addr, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%02x to addr 0x%02x\n",
+ __FUNCTION__, data, addr);
+ }
+#endif
+
+ return status;
+}
+
+int nios_ina219_read(struct bladerf *dev, uint8_t addr, uint16_t *data)
+{
+ int status;
+
+ status = nios_8x16_read(dev, NIOS_PKT_8x16_TARGET_INA219, addr, data);
+ if (status == 0) {
+ log_verbose("%s: Read 0x%04x from addr 0x%02x\n",
+ __FUNCTION__, *data, addr);
+ }
+
+ return status;
+}
+
+int nios_ina219_write(struct bladerf *dev, uint8_t addr, uint16_t data)
+{
+ int status;
+
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_INA219, addr, data);
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%04x to addr 0x%02x\n",
+ __FUNCTION__, data, addr);
+ }
+
+ return status;
+}
+
+#define VERBOSE_OUT_SINGLEBYTE "%s: %s 0x%02x @ addr 0x%04x\n"
+#define VERBOSE_OUT_MULTIBYTE "%s: %s 0x%02x @ addr 0x%04x (%d/%d)\n"
+
+int nios_ad9361_spi_read(struct bladerf *dev, uint16_t cmd, uint64_t *data)
+{
+ int status;
+
+ status = nios_16x64_read(dev, NIOS_PKT_16x64_TARGET_AD9361, cmd, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (log_get_verbosity() == BLADERF_LOG_LEVEL_VERBOSE && status == 0) {
+ size_t bytes = (((cmd >> 12) & 0x7) + 1);
+ size_t addr = cmd & 0xFFF;
+
+ if (bytes > 1) {
+ size_t i;
+ for (i = 0; i < bytes; ++i) {
+ uint8_t byte = (*data >> (56 - 8 * i)) & 0xFF;
+ log_verbose(VERBOSE_OUT_MULTIBYTE, "ad9361_spi", " MRead", byte,
+ addr - i, i + 1, bytes);
+ }
+ } else {
+ uint8_t byte = (*data >> 56) & 0xFF;
+ log_verbose(VERBOSE_OUT_SINGLEBYTE, "ad9361_spi", " Read", byte,
+ addr);
+ }
+ }
+#endif
+
+ return status;
+}
+
+int nios_ad9361_spi_write(struct bladerf *dev, uint16_t cmd, uint64_t data)
+{
+ int status;
+
+ status = nios_16x64_write(dev, NIOS_PKT_16x64_TARGET_AD9361, cmd, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (log_get_verbosity() == BLADERF_LOG_LEVEL_VERBOSE && status == 0) {
+ size_t bytes = (((cmd >> 12) & 0x7) + 1) & 0xFF;
+ size_t addr = cmd & 0xFFF;
+
+ if (bytes > 1) {
+ size_t i;
+ for (i = 0; i < bytes; ++i) {
+ uint8_t byte = (data >> (56 - 8 * i)) & 0xFF;
+ log_verbose(VERBOSE_OUT_MULTIBYTE, "ad9361_spi", "MWrite", byte,
+ addr - i, i + 1, bytes);
+ }
+ } else {
+ uint8_t byte = (data >> 56) & 0xFF;
+ log_verbose(VERBOSE_OUT_SINGLEBYTE, "ad9361_spi", " Write", byte,
+ addr);
+ }
+ }
+#endif
+
+ return status;
+}
+
+int nios_adi_axi_read(struct bladerf *dev, uint32_t addr, uint32_t *data)
+{
+ int status;
+
+ status = nios_32x32_read(dev, NIOS_PKT_32x32_TARGET_ADI_AXI, addr, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Read 0x%08" PRIx32 " from addr 0x%04" PRIx32 "\n",
+ __FUNCTION__, *data, addr);
+ }
+#endif
+
+ return status;
+}
+
+int nios_adi_axi_write(struct bladerf *dev, uint32_t addr, uint32_t data)
+{
+ int status;
+
+ status = nios_32x32_write(dev, NIOS_PKT_32x32_TARGET_ADI_AXI, addr, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%08" PRIx32 " to addr 0x%04" PRIx32 "\n",
+ __FUNCTION__, data, addr);
+ }
+#endif
+
+ return status;
+}
+
+int nios_wishbone_master_read(struct bladerf *dev, uint32_t addr, uint32_t *data)
+{
+ int status;
+
+ status = nios_32x32_read(dev, NIOS_PKT_32x32_TARGET_WB_MSTR, addr, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Read 0x%08" PRIx32 " from addr 0x%04" PRIx32 "\n",
+ __FUNCTION__, *data, addr);
+ }
+#endif
+
+ return status;
+}
+
+int nios_wishbone_master_write(struct bladerf *dev, uint32_t addr, uint32_t data)
+{
+ int status;
+
+ status = nios_32x32_write(dev, NIOS_PKT_32x32_TARGET_WB_MSTR, addr, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%08" PRIx32 " to addr 0x%04" PRIx32 "\n",
+ __FUNCTION__, data, addr);
+ }
+#endif
+
+ return status;
+}
+
+int nios_rfic_command_read(struct bladerf *dev, uint16_t cmd, uint64_t *data)
+{
+ int status;
+
+ status = nios_16x64_read(dev, NIOS_PKT_16x64_TARGET_RFIC, cmd, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Read 0x%04x 0x%08x\n", __FUNCTION__, cmd, *data);
+ }
+#endif
+
+ return status;
+}
+
+int nios_rfic_command_write(struct bladerf *dev, uint16_t cmd, uint64_t data)
+{
+ int status;
+
+ status = nios_16x64_write(dev, NIOS_PKT_16x64_TARGET_RFIC, cmd, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Write 0x%04x 0x%08x\n", __FUNCTION__, cmd, data);
+ }
+#endif
+
+ return status;
+}
+
+int nios_rffe_control_read(struct bladerf *dev, uint32_t *value)
+{
+ int status;
+
+ status = nios_8x32_read(dev, NIOS_PKT_8x32_TARGET_RFFE_CSR, 0, value);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Read 0x%08x\n", __FUNCTION__, *value);
+ }
+#endif
+
+ return status;
+}
+
+int nios_rffe_control_write(struct bladerf *dev, uint32_t value)
+{
+ int status;
+
+ status = nios_8x32_write(dev, NIOS_PKT_8x32_TARGET_RFFE_CSR, 0, value);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%08x\n", __FUNCTION__, value);
+ }
+#endif
+
+ return status;
+}
+
+int nios_rffe_fastlock_save(struct bladerf *dev, bool is_tx,
+ uint8_t rffe_profile, uint16_t nios_profile)
+{
+ int status;
+ uint8_t addr;
+ uint32_t data = 0;
+
+ addr = is_tx ? 1 : 0;
+ data = (rffe_profile << 16) | nios_profile;
+
+ status = nios_8x32_write(dev, NIOS_PKT_8x32_TARGET_FASTLOCK, addr, data);
+
+#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%08x\n", __FUNCTION__, data);
+ }
+#endif
+
+ return status;
+}
+
+int nios_ad56x1_vctcxo_trim_dac_read(struct bladerf *dev, uint16_t *value)
+{
+ int status;
+
+ status = nios_8x16_read(dev, NIOS_PKT_8x16_TARGET_AD56X1_DAC, 0, value);
+ if (status == 0) {
+ log_verbose("%s: Read 0x%04x\n", __FUNCTION__, *value);
+ }
+
+ return status;
+}
+
+int nios_ad56x1_vctcxo_trim_dac_write(struct bladerf *dev, uint16_t value)
+{
+ int status;
+
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_AD56X1_DAC, 0, value);
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%04x\n", __FUNCTION__, value);
+ }
+
+ return status;
+}
+
+int nios_adf400x_read(struct bladerf *dev, uint8_t addr, uint32_t *data)
+{
+ int status;
+
+ status = nios_8x32_read(dev, NIOS_PKT_8x32_TARGET_ADF400X, addr, data);
+ if (status == 0) {
+ log_verbose("%s: Read 0x%08x from addr 0x%02x\n", __FUNCTION__, *data, addr);
+ }
+
+ return status;
+}
+
+int nios_adf400x_write(struct bladerf *dev, uint8_t addr, uint32_t data)
+{
+ int status;
+
+ data &= ~(0x3);
+
+ status = nios_8x32_write(dev, NIOS_PKT_8x32_TARGET_ADF400X, 0, data | (addr & 0x3));
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%08x to addr 0x%02x\n", __FUNCTION__, data, addr);
+ }
+
+ return status;
+}
+
+int nios_vctcxo_trim_dac_write(struct bladerf *dev, uint8_t addr, uint16_t value)
+{
+ return nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_VCTCXO_DAC, addr, value);
+}
+
+int nios_vctcxo_trim_dac_read(struct bladerf *dev, uint8_t addr, uint16_t *value)
+{
+ return nios_8x16_read(dev, NIOS_PKT_8x16_TARGET_VCTCXO_DAC, addr, value);
+}
+
+int nios_set_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode mode)
+{
+ int status;
+
+ status = nios_8x8_write(dev,
+ NIOS_PKT_8x8_TARGET_VCTCXO_TAMER, 0xff,
+ (uint8_t) mode);
+
+ if (status == 0) {
+ log_verbose("%s: Wrote mode=0x%02x\n", __FUNCTION__, mode);
+ }
+
+ return status;
+}
+
+int nios_get_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode *mode)
+{
+ int status;
+ uint8_t tmp;
+
+ *mode = BLADERF_VCTCXO_TAMER_INVALID;
+
+ status = nios_8x8_read(dev, NIOS_PKT_8x8_TARGET_VCTCXO_TAMER, 0xff, &tmp);
+ if (status == 0) {
+ log_verbose("%s: Read mode=0x%02x\n", __FUNCTION__, tmp);
+
+ switch ((bladerf_vctcxo_tamer_mode) tmp) {
+ case BLADERF_VCTCXO_TAMER_DISABLED:
+ case BLADERF_VCTCXO_TAMER_1_PPS:
+ case BLADERF_VCTCXO_TAMER_10_MHZ:
+ *mode = (bladerf_vctcxo_tamer_mode) tmp;
+ break;
+
+ default:
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+ }
+
+ return status;
+}
+
+
+int nios_get_iq_gain_correction(struct bladerf *dev, bladerf_channel ch,
+ int16_t *value)
+{
+ int status = BLADERF_ERR_INVAL;
+ uint16_t tmp = 0;
+
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ status = nios_8x16_read(dev, NIOS_PKT_8x16_TARGET_IQ_CORR,
+ NIOS_PKT_8x16_ADDR_IQ_CORR_RX_GAIN, &tmp);
+ break;
+
+ case BLADERF_CHANNEL_TX(0):
+ status = nios_8x16_read(dev, NIOS_PKT_8x16_TARGET_IQ_CORR,
+ NIOS_PKT_8x16_ADDR_IQ_CORR_TX_GAIN, &tmp);
+ break;
+
+ default:
+ log_debug("Invalid channel: 0x%x\n", ch);
+ }
+
+ *value = (int16_t) tmp;
+
+ if (status == 0) {
+ log_verbose("%s: Read %s %d\n",
+ __FUNCTION__, channel2str(ch), *value);
+ }
+
+ return status;
+}
+
+int nios_get_iq_phase_correction(struct bladerf *dev, bladerf_channel ch,
+ int16_t *value)
+{
+ int status = BLADERF_ERR_INVAL;
+ uint16_t tmp = 0;
+
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ status = nios_8x16_read(dev, NIOS_PKT_8x16_TARGET_IQ_CORR,
+ NIOS_PKT_8x16_ADDR_IQ_CORR_RX_PHASE, &tmp);
+ break;
+
+ case BLADERF_CHANNEL_TX(0):
+ status = nios_8x16_read(dev, NIOS_PKT_8x16_TARGET_IQ_CORR,
+ NIOS_PKT_8x16_ADDR_IQ_CORR_TX_PHASE, &tmp);
+ break;
+
+ default:
+ log_debug("Invalid channel: 0x%x\n", ch);
+ }
+
+ *value = (int16_t) tmp;
+
+ if (status == 0) {
+ log_verbose("%s: Read %s %d\n",
+ __FUNCTION__, channel2str(ch), *value);
+ }
+
+ return status;
+}
+
+int nios_set_iq_gain_correction(struct bladerf *dev, bladerf_channel ch,
+ int16_t value)
+{
+ int status = BLADERF_ERR_INVAL;
+
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ log_verbose("Setting RX IQ Correction gain: %d\n", value);
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_IQ_CORR,
+ NIOS_PKT_8x16_ADDR_IQ_CORR_RX_GAIN, value);
+ break;
+
+ case BLADERF_CHANNEL_TX(0):
+ log_verbose("Setting TX IQ Correction gain: %d\n", value);
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_IQ_CORR,
+ NIOS_PKT_8x16_ADDR_IQ_CORR_TX_GAIN, value);
+ break;
+
+ default:
+ log_debug("Invalid channel: 0x%x\n", ch);
+ }
+
+ if (status == 0) {
+ log_verbose("%s: Wrote %s %d\n",
+ __FUNCTION__, channel2str(ch), value);
+ }
+
+ return status;
+}
+
+int nios_set_iq_phase_correction(struct bladerf *dev, bladerf_channel ch,
+ int16_t value)
+{
+ int status = BLADERF_ERR_INVAL;
+
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ log_verbose("Setting RX IQ Correction phase: %d\n", value);
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_IQ_CORR,
+ NIOS_PKT_8x16_ADDR_IQ_CORR_RX_PHASE, value);
+ break;
+
+ case BLADERF_CHANNEL_TX(0):
+ log_verbose("Setting TX IQ Correction phase: %d\n", value);
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_IQ_CORR,
+ NIOS_PKT_8x16_ADDR_IQ_CORR_TX_PHASE, value);
+ break;
+
+ default:
+ log_debug("Invalid channel: 0x%x\n", ch);
+ }
+
+ if (status == 0) {
+ log_verbose("%s: Wrote %s %d\n",
+ __FUNCTION__, channel2str(ch), value);
+ }
+
+
+ return status;
+}
+
+int nios_set_agc_dc_correction(struct bladerf *dev, int16_t q_max, int16_t i_max,
+ int16_t q_mid, int16_t i_mid,
+ int16_t q_low, int16_t i_low)
+{
+ int status;
+
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_AGC_CORR, NIOS_PKT_8x16_ADDR_AGC_DC_Q_MAX, q_max);
+
+ if (!status)
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_AGC_CORR, NIOS_PKT_8x16_ADDR_AGC_DC_I_MAX, i_max);
+ if (!status)
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_AGC_CORR, NIOS_PKT_8x16_ADDR_AGC_DC_Q_MID, q_mid);
+ if (!status)
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_AGC_CORR, NIOS_PKT_8x16_ADDR_AGC_DC_I_MID, i_mid);
+ if (!status)
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_AGC_CORR, NIOS_PKT_8x16_ADDR_AGC_DC_Q_MIN, q_low);
+ if (!status)
+ status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_AGC_CORR, NIOS_PKT_8x16_ADDR_AGC_DC_I_MIN, i_low);
+
+ return status;
+}
+
+int nios_xb200_synth_write(struct bladerf *dev, uint32_t value)
+{
+ int status = nios_8x32_write(dev, NIOS_PKT_8x32_TARGET_ADF4351, 0, value);
+
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%08x\n", __FUNCTION__, value);
+ }
+
+ return status;
+}
+
+int nios_expansion_gpio_read(struct bladerf *dev, uint32_t *val)
+{
+ int status = nios_32x32_masked_read(dev, NIOS_PKT_32x32_TARGET_EXP,
+ 0xffffffff, val);
+
+ if (status == 0) {
+ log_verbose("%s: Read 0x%08x\n", __FUNCTION__, *val);
+ }
+
+ return status;
+}
+
+int nios_expansion_gpio_write(struct bladerf *dev, uint32_t mask, uint32_t val)
+{
+ int status = nios_32x32_masked_write(dev, NIOS_PKT_32x32_TARGET_EXP,
+ mask, val);
+
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%08x (with mask 0x%08x)\n",
+ __FUNCTION__, val, mask);
+ }
+
+ return status;
+}
+
+int nios_expansion_gpio_dir_read(struct bladerf *dev, uint32_t *val)
+{
+ int status = nios_32x32_masked_read(dev, NIOS_PKT_32x32_TARGET_EXP_DIR,
+ 0xffffffff, val);
+
+ if (status == 0) {
+ log_verbose("%s: Read 0x%08x\n", __FUNCTION__, *val);
+ }
+
+ return status;
+}
+
+int nios_expansion_gpio_dir_write(struct bladerf *dev,
+ uint32_t mask, uint32_t val)
+{
+ int status = nios_32x32_masked_write(dev, NIOS_PKT_32x32_TARGET_EXP_DIR,
+ mask, val);
+
+ if (status == 0) {
+ log_verbose("%s: Wrote 0x%08x (with mask 0x%08x)\n",
+ __FUNCTION__, val, mask);
+ }
+
+ return status;
+}
+
+int nios_retune(struct bladerf *dev, bladerf_channel ch,
+ uint64_t timestamp, uint16_t nint, uint32_t nfrac,
+ uint8_t freqsel, uint8_t vcocap, bool low_band,
+ uint8_t xb_gpio, bool quick_tune)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+
+ uint8_t resp_flags;
+ uint64_t duration;
+
+ if (timestamp == NIOS_PKT_RETUNE_CLEAR_QUEUE) {
+ log_verbose("Clearing %s retune queue.\n", channel2str(ch));
+ } else {
+ log_verbose("%s: channel=%s timestamp=%"PRIu64" nint=%u nfrac=%u\n\t\t\t\t"
+ "freqsel=0x%02x vcocap=0x%02x low_band=%d quick_tune=%d\n",
+ __FUNCTION__, channel2str(ch), timestamp, nint, nfrac,
+ freqsel, vcocap, low_band, quick_tune);
+ }
+
+ nios_pkt_retune_pack(buf, ch, timestamp,
+ nint, nfrac, freqsel, vcocap, low_band,
+ xb_gpio, quick_tune);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_retune_resp_unpack(buf, &duration, &vcocap, &resp_flags);
+
+ if (resp_flags & NIOS_PKT_RETUNERESP_FLAG_TSVTUNE_VALID) {
+ log_verbose("%s retune operation: vcocap=%u, duration=%"PRIu64"\n",
+ channel2str(ch), vcocap, duration);
+ } else {
+ log_verbose("%s operation duration: %"PRIu64"\n",
+ channel2str(ch), duration);
+ }
+
+ if ((resp_flags & NIOS_PKT_RETUNERESP_FLAG_SUCCESS) == 0) {
+ if (timestamp == BLADERF_RETUNE_NOW) {
+ log_debug("FPGA tuning reported failure.\n");
+ status = BLADERF_ERR_UNEXPECTED;
+ } else {
+ log_debug("The FPGA's retune queue is full. Try again after "
+ "a previous request has completed.\n");
+ status = BLADERF_ERR_QUEUE_FULL;
+ }
+ }
+
+ return status;
+}
+
+int nios_retune2(struct bladerf *dev, bladerf_channel ch,
+ uint64_t timestamp, uint16_t nios_profile,
+ uint8_t rffe_profile, uint8_t port,
+ uint8_t spdt)
+{
+ int status;
+ uint8_t buf[NIOS_PKT_LEN];
+
+ uint8_t resp_flags;
+ uint64_t duration;
+
+ if (timestamp == NIOS_PKT_RETUNE2_CLEAR_QUEUE) {
+ log_verbose("Clearing %s retune queue.\n", channel2str(ch));
+ } else {
+ log_verbose("%s: channel=%s timestamp=%"PRIu64" nios_profile=%u "
+ "rffe_profile=%u\n\t\t\t\tport=0x%02x spdt=0x%02x\n",
+ __FUNCTION__, channel2str(ch), timestamp, nios_profile,
+ rffe_profile, port, spdt);
+ }
+
+ nios_pkt_retune2_pack(buf, ch, timestamp, nios_profile, rffe_profile,
+ port, spdt);
+
+ status = nios_access(dev, buf);
+ if (status != 0) {
+ return status;
+ }
+
+ nios_pkt_retune2_resp_unpack(buf, &duration, &resp_flags);
+
+ if (resp_flags & NIOS_PKT_RETUNE2_RESP_FLAG_TSVTUNE_VALID) {
+ log_verbose("%s retune operation: duration=%"PRIu64"\n",
+ channel2str(ch), duration);
+ } else {
+ log_verbose("%s operation duration: %"PRIu64"\n",
+ channel2str(ch), duration);
+ }
+
+ if ((resp_flags & NIOS_PKT_RETUNE2_RESP_FLAG_SUCCESS) == 0) {
+ if (timestamp == BLADERF_RETUNE_NOW) {
+ log_debug("FPGA tuning reported failure.\n");
+ status = BLADERF_ERR_UNEXPECTED;
+ } else {
+ log_debug("The FPGA's retune queue is full. Try again after "
+ "a previous request has completed.\n");
+ status = BLADERF_ERR_QUEUE_FULL;
+ }
+ }
+
+ return status;
+}
+
+int nios_read_trigger(struct bladerf *dev, bladerf_channel ch,
+ bladerf_trigger_signal trigger, uint8_t *value)
+{
+ int status;
+ uint8_t nios_id;
+
+ switch (ch) {
+ case BLADERF_CHANNEL_TX(0):
+ nios_id = NIOS_PKT_8x8_TX_TRIGGER_CTL;
+ break;
+
+ case BLADERF_CHANNEL_RX(0):
+ nios_id = NIOS_PKT_8x8_RX_TRIGGER_CTL;
+ break;
+
+ default:
+ log_debug("Invalid channel: 0x%x\n", ch);
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Only 1 external trigger is currently supported */
+ switch (trigger) {
+ case BLADERF_TRIGGER_J71_4:
+ case BLADERF_TRIGGER_J51_1:
+ case BLADERF_TRIGGER_MINI_EXP_1:
+ break;
+
+ default:
+ log_debug("Invalid trigger: %d\n", trigger);
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = nios_8x8_read(dev, nios_id, 0, value);
+ if (status == 0) {
+ log_verbose("%s trigger read value 0x%02x\n", channel2str(ch), *value);
+ }
+
+ return status;
+}
+
+int nios_write_trigger(struct bladerf *dev, bladerf_channel ch,
+ bladerf_trigger_signal trigger, uint8_t value)
+{
+ int status;
+ uint8_t nios_id;
+
+ switch (ch) {
+ case BLADERF_CHANNEL_TX(0):
+ nios_id = NIOS_PKT_8x8_TX_TRIGGER_CTL;
+ break;
+
+ case BLADERF_CHANNEL_RX(0):
+ nios_id = NIOS_PKT_8x8_RX_TRIGGER_CTL;
+ break;
+
+ default:
+ log_debug("Invalid channel: 0x%x\n", ch);
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Only 1 external trigger is currently supported */
+ switch (trigger) {
+ case BLADERF_TRIGGER_J71_4:
+ case BLADERF_TRIGGER_J51_1:
+ case BLADERF_TRIGGER_MINI_EXP_1:
+ break;
+
+ default:
+ log_debug("Invalid trigger: %d\n", trigger);
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = nios_8x8_write(dev, nios_id, 0, value);
+ if (status == 0) {
+ log_verbose("%s trigger write value 0x%02x\n", channel2str(ch), value);
+ }
+
+ return status;
+}
diff --git a/Radio/HW/BladeRF/src/backend/usb/nios_access.h b/Radio/HW/BladeRF/src/backend/usb/nios_access.h
new file mode 100644
index 0000000..f02270e
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/usb/nios_access.h
@@ -0,0 +1,582 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2015 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* This file defines functionality used to communicate with the NIOS II
+ * soft-core processor running on the FPGA versions >= v0.3.0
+ *
+ * The host communicates with the NIOS II via USB transfers to the Cypress FX3,
+ * which then communicates with the FPGA via a UART. This module packs and
+ * submits the requests, and receives and unpacks the responses.
+ *
+ * See the files in the top-level fpga_common/ directory for description
+ * and macros pertaining to the legacy packet format.
+ */
+
+#ifndef BACKEND_USB_NIOS_ACCESS_H_
+#define BACKEND_USB_NIOS_ACCESS_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "board/board.h"
+#include "usb.h"
+
+/**
+ * Read from the FPGA's config register
+ *
+ * @param dev Device handle
+ * @param[out] val On success, updated with config register value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_config_read(struct bladerf *dev, uint32_t *val);
+
+/**
+ * Write to FPGA's config register
+ *
+ * @param dev Device handle
+ * @param[in] val Register value to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_config_write(struct bladerf *dev, uint32_t val);
+
+/**
+ * Read the FPGA version into the provided version structure
+ *
+ * @param dev Device handle
+ * @param[out] ver Updated with FPGA version (upon success)
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_get_fpga_version(struct bladerf *dev, struct bladerf_version *ver);
+
+/**
+ * Read a timestamp counter value
+ *
+ * @param dev Device handle
+ * @param[in] dir Stream direction
+ * @param[out] timestamp On success, updated with the timestamp value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_get_timestamp(struct bladerf *dev,
+ bladerf_direction dir,
+ uint64_t *timestamp);
+
+/**
+ * Read from an Si5338 register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[out] data On success, updated with read data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_si5338_read(struct bladerf *dev, uint8_t addr, uint8_t *data);
+
+/**
+ * Write to an Si5338 register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[in] data Data to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_si5338_write(struct bladerf *dev, uint8_t addr, uint8_t data);
+
+/**
+ * Read from an LMS6002D register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[out] data On success, updated with data read from the device
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_lms6_read(struct bladerf *dev, uint8_t addr, uint8_t *data);
+
+/**
+ * Write to an LMS6002D register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[out] data Register data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_lms6_write(struct bladerf *dev, uint8_t addr, uint8_t data);
+
+/**
+ * Read from an INA219 register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_ina219_read(struct bladerf *dev, uint8_t addr, uint16_t *data);
+
+/**
+ * Write to an INA219 register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_ina219_write(struct bladerf *dev, uint8_t addr, uint16_t data);
+
+/**
+ * Read the AD9361 over SPI.
+ *
+ * @param dev Device handle
+ * @param[in] cmd Command
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_ad9361_spi_read(struct bladerf *dev, uint16_t cmd, uint64_t *data);
+
+/**
+ * Write to the AD9361 over SPI.
+ *
+ * @param dev Device handle
+ * @param[in] cmd Command
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_ad9361_spi_write(struct bladerf *dev, uint16_t cmd, uint64_t data);
+
+/**
+ * Read the ADI AXI memory mapped region.
+ *
+ * @param dev Device handle
+ * @param[in] addr Address
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_adi_axi_read(struct bladerf *dev, uint32_t cmd, uint32_t *data);
+
+/**
+ * Write to the ADI AXI memory mapped region.
+ *
+ * @param dev Device handle
+ * @param[in] addr Address
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_adi_axi_write(struct bladerf *dev, uint32_t cmd, uint32_t data);
+
+/**
+ * Read the Wishbone Master memory mapped region.
+ *
+ * @param dev Device handle
+ * @param[in] addr Address
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_wishbone_master_read(struct bladerf *dev, uint32_t addr, uint32_t *data);
+
+/**
+ * Write to the Wishbone Master memory mapped region.
+ *
+ * @param dev Device handle
+ * @param[in] addr Address
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_wishbone_master_write(struct bladerf *dev, uint32_t addr, uint32_t data);
+
+/**
+ * Read RFIC command
+ *
+ * @param dev Device handle
+ * @param[in] cmd Command: `(command & 0xFF) + ((channel & 0xF) << 8)`
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_rfic_command_read(struct bladerf *dev, uint16_t cmd, uint64_t *data);
+
+/**
+ * Write RFIC command
+ *
+ * @param dev Device handle
+ * @param[in] cmd Command: `(command & 0xFF) + ((channel & 0xF) << 8)`
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_rfic_command_write(struct bladerf *dev, uint16_t cmd, uint64_t data);
+
+/**
+ * Read RFFE control register.
+ *
+ * @param dev Device handle
+ * @param[in] len Data buffer length
+ * @param[out] value Value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_rffe_control_read(struct bladerf *dev, uint32_t *value);
+
+/**
+ * Write RFFE control register.
+ *
+ * @param dev Device handle
+ * @param[in] len Data buffer length
+ * @param[in] value Value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_rffe_control_write(struct bladerf *dev, uint32_t value);
+
+/**
+ * Save an RFFE fast lock profile to the Nios.
+ *
+ * @param dev Device handle
+ * @param[in] is_tx True if TX profile, false if RX profile
+ * @param[in] rffe_profile RFFE profile to save
+ * @param[in] nios_profile Where to save the profile in the Nios
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_rffe_fastlock_save(struct bladerf *dev, bool is_tx,
+ uint8_t rffe_profile, uint16_t nios_profile);
+
+/**
+ * Write to the AD56X1 VCTCXO trim DAC.
+ *
+ * @param dev Device handle
+ * @param[in] value Value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_ad56x1_vctcxo_trim_dac_write(struct bladerf *dev, uint16_t value);
+
+/**
+ * Read the AD56X1 VCTCXO trim DAC.
+ *
+ * @param dev Device handle
+ * @param[out] value Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_ad56x1_vctcxo_trim_dac_read(struct bladerf *dev, uint16_t *value);
+
+/**
+ * Write to the ADF400X.
+ *
+ * @param dev Device handle
+ * @param[in] addr Address
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_adf400x_write(struct bladerf *dev, uint8_t addr, uint32_t data);
+
+/**
+ * Read from the ADF400X.
+ *
+ * @param dev Device handle
+ * @param[in] addr Address
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_adf400x_read(struct bladerf *dev, uint8_t addr, uint32_t *data);
+
+/**
+ * Write to a VCTCXO trim DAC register.
+ *
+ * @param dev Device handle
+ * @param[in] addr Register
+ * @param[in] value Value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_vctcxo_trim_dac_write(struct bladerf *dev,
+ uint8_t addr,
+ uint16_t value);
+
+/**
+ * Read from a VCTCXO trim DAC register.
+ *
+ * @param dev Device handle
+ * @param[in] addr Register
+ * @param[out] value Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_vctcxo_trim_dac_read(struct bladerf *dev,
+ uint8_t addr,
+ uint16_t *value);
+
+/**
+ * Write VCTCXO tamer mode selection
+ *
+ * @param dev Device handle
+ * @param[in] mode Tamer mode to use, or BLADERF_VCTCXO_TAMER_DISABLED
+ * to disable the tamer.
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_set_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode mode);
+
+/**
+ * Read the current VCTCXO mode selection
+ *
+ * @param dev Device handle
+ * @param[out] mode Current tamer mode in use.
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_get_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode *mode);
+
+/**
+ * Read a IQ gain correction value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] value On success, updated with phase correction value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_get_iq_gain_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t *value);
+
+/**
+ * Read a IQ phase correction value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] value On success, updated with phase correction value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_get_iq_phase_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t *value);
+
+/**
+ * Write an IQ gain correction value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] value Correction value to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_set_iq_gain_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t value);
+
+/**
+ * Write an IQ phase correction value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] value Correction value to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_set_iq_phase_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t value);
+
+/**
+ * Write AGC DC LUT values
+ *
+ * @param[in] dev Device handle
+ * @param[in] q_max Q DC offset at Max gain
+ * @param[in] i_max I DC offset at Max gain
+ * @param[in] q_mid Q DC offset at Mid gain
+ * @param[in] i_mid I DC offset at Mid gain
+ * @param[in] q_low Q DC offset at Low gain
+ * @param[in] i_low I DC offset at Low gain
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_set_agc_dc_correction(struct bladerf *dev,
+ int16_t q_max,
+ int16_t i_max,
+ int16_t q_mid,
+ int16_t i_mid,
+ int16_t q_low,
+ int16_t i_low);
+/**
+ * Write a value to the XB-200's ADF4351 synthesizer
+ *
+ * @param dev Device handle
+ * @param[in] value Write value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_xb200_synth_write(struct bladerf *dev, uint32_t value);
+
+/**
+ * Read from expanion GPIO
+ *
+ * @param dev Device handle
+ * @param[out] value On success, updated with read value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_expansion_gpio_read(struct bladerf *dev, uint32_t *val);
+
+/**
+ * Write to expansion GPIO
+ *
+ * @param dev Device handle
+ * @param[in] mask Mask denoting bits to write
+ * @param[in] value Write value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_expansion_gpio_write(struct bladerf *dev, uint32_t mask, uint32_t val);
+
+/**
+ * Read from expansion GPIO direction register
+ *
+ * @param dev Device handle
+ * @param[out] value On success, updated with read value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_expansion_gpio_dir_read(struct bladerf *dev, uint32_t *val);
+
+/**
+ * Write to expansion GPIO direction register
+ *
+ * @param dev Device handle
+ * @param[in] mask Mask denoting bits to configure
+ * @param[in] output '1' bit denotes output, '0' bit denotes input
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_expansion_gpio_dir_write(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t outputs);
+
+/**
+ * Dummy handler for a retune request, which is not supported on
+ * earlier FPGA versions.
+ *
+ * All of the following parameters are ignored.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] timestamp Time to schedule retune at
+ * @param[in] nint Integer portion of frequency multiplier
+ * @param[in] nfrac Fractional portion of frequency multiplier
+ * @param[in] freqsel VCO and divider selection
+ * @param[in] low_band High vs low band selection
+ * @param[in] xb_gpio XB configuration bits
+ * @param[in] quick_tune Denotes quick tune should be used instead of
+ * tuning algorithm
+ *
+ * @return BLADERF_ERR_UNSUPPORTED
+ */
+int nios_retune(struct bladerf *dev,
+ bladerf_channel ch,
+ uint64_t timestamp,
+ uint16_t nint,
+ uint32_t nfrac,
+ uint8_t freqsel,
+ uint8_t vcocap,
+ bool low_band,
+ uint8_t xb_gpio,
+ bool quick_tune);
+
+/**
+ * Handler for a retune request on bladeRF2 devices. The RFFEs used in these
+ * devices have a concept called fast lock profiles that store all the VCO
+ * information needed to quickly retune to a different frequency. The RFFE
+ * can store up to 8 profiles for RX, and a separate 8 for TX. To use more
+ * profiles, they must be stored in the baseband processor (e.g. the FPGA
+ * or Nios) and loaded into one of the 8 slots as needed.
+ *
+ * Each of these profiles consumes 16 bytes not including the timestamp and
+ * RF port/switch information. This is too large to fit into a single Nios
+ * packet at this time, so to improve retune time, the profiles are stored
+ * in the Nios and the Host will schedule retunes by specifying the Nios
+ * profile to load into the specified RFFE profile slot.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] timestamp Time to schedule retune at
+ * @param[in] nios_profile Nios profile number (0-N) to load into the RFFE
+ * @param[in] rffe_profile RFFE fast lock profile number (0-7) into which
+ * the Nios profile will be loaded.
+ * @param[in] port RFFE port settings
+ * @param[in] spdt RF SPDT switch settings
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_retune2(struct bladerf *dev, bladerf_channel ch,
+ uint64_t timestamp, uint16_t nios_profile,
+ uint8_t rffe_profile, uint8_t port, uint8_t spdt);
+
+/**
+ * Read trigger register value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] trigger Trigger to read from
+ * @param[out] value On success, updated with register value
+ *
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error
+ */
+int nios_read_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t *value);
+
+/**
+ * Write trigger register value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] trigger Trigger to read
+ * @param[in] value Value to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error
+ */
+int nios_write_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t value);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.c b/Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.c
new file mode 100644
index 0000000..e4fe727
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.c
@@ -0,0 +1,744 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014-2015 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* This file implements functionality used to communicate with the NIOS II
+ * soft-core processor running on the FPGA versions prior to v0.2.x.
+ * (Although post v0.2.x FPGAs support this for reverse compatibility).
+ *
+ * The host communicates with the NIOS II via USB transfers to the Cypress FX3,
+ * which then communicates with the FPGA via a UART.
+ *
+ * See the files in the top-level fpga_common/ directory for description
+ * and macros pertaining to the legacy packet format.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "log.h"
+
+#include "usb.h"
+#include "nios_legacy_access.h"
+#include "nios_pkt_formats.h"
+
+#include "board/board.h"
+#include "board/bladerf1/capabilities.h"
+#include "helpers/version.h"
+
+#include "board/bladerf1/capabilities.h"
+
+#if 0
+static void print_buf(const char *msg, const uint8_t *buf, size_t len)
+{
+ size_t i;
+ if (msg != NULL) {
+ fputs(msg, stderr);
+ }
+
+ for (i = 0; i < len; i++) {
+ if (i == 0) {
+ fprintf(stderr, " 0x%02x,", buf[i]);
+ } else if ((i + 1) % 8 == 0) {
+ fprintf(stderr, " 0x%02x,\n ", buf[i]);
+ } else {
+ fprintf(stderr, " 0x%02x,", buf[i]);
+ }
+ }
+
+ fputc('\n', stderr);
+}
+#else
+#define print_buf(msg, data, len) do {} while(0)
+#endif
+
+/* Access device/module via the legacy NIOS II packet format. */
+static int nios_access(struct bladerf *dev, uint8_t peripheral,
+ usb_direction dir, struct uart_cmd *cmd,
+ size_t len)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+
+ int status;
+ size_t i;
+ uint8_t buf[16] = { 0 };
+ const uint8_t pkt_mode_dir = (dir == USB_DIR_HOST_TO_DEVICE) ?
+ NIOS_PKT_LEGACY_MODE_DIR_WRITE :
+ NIOS_PKT_LEGACY_MODE_DIR_READ;
+
+ assert(len <= ((sizeof(buf) - 2) / 2));
+
+ /* Populate the buffer for transfer, given address data pairs */
+ buf[0] = NIOS_PKT_LEGACY_MAGIC;
+ buf[1] = pkt_mode_dir | peripheral | (uint8_t)len;
+
+ for (i = 0; i < len; i++) {
+ buf[i * 2 + 2] = cmd[i].addr;
+ buf[i * 2 + 3] = cmd[i].data;
+ }
+
+ print_buf("NIOS II access request:\n", buf, 16);
+
+ /* Send the command */
+ status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_OUT,
+ buf, sizeof(buf),
+ PERIPHERAL_TIMEOUT_MS);
+ if (status != 0) {
+ log_debug("Failed to submit NIOS II request: %s\n",
+ bladerf_strerror(status));
+ return status;
+ }
+
+ /* Read back the ACK. The command data is only used for a read operation,
+ * and is thrown away otherwise */
+ status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_IN,
+ buf, sizeof(buf),
+ PERIPHERAL_TIMEOUT_MS);
+
+ if (dir == NIOS_PKT_LEGACY_MODE_DIR_READ && status == 0) {
+ for (i = 0; i < len; i++) {
+ cmd[i].data = buf[i * 2 + 3];
+ }
+ }
+
+ if (status == 0) {
+ print_buf("NIOS II access response:\n", buf, 16);
+ } else {
+ log_debug("Failed to receive NIOS II response: %s\n",
+ bladerf_strerror(status));
+ }
+
+
+ return status;
+}
+
+int nios_legacy_pio_read(struct bladerf *dev, uint8_t addr, uint32_t *data)
+{
+ int status;
+ size_t i;
+ struct uart_cmd cmd;
+
+ *data = 0;
+
+ for (i = 0; i < sizeof(*data); i++) {
+ assert((addr + i) <= UINT8_MAX);
+ cmd.addr = (uint8_t)(addr + i);
+ cmd.data = 0xff;
+
+ status = nios_access(dev, NIOS_PKT_LEGACY_DEV_CONFIG,
+ USB_DIR_DEVICE_TO_HOST, &cmd, 1);
+
+ if (status < 0) {
+ *data = 0;
+ return status;
+ }
+
+ *data |= (cmd.data << (i * 8));
+ }
+
+ return 0;
+}
+
+int nios_legacy_pio_write(struct bladerf *dev, uint8_t addr, uint32_t data)
+{
+ int status;
+ size_t i;
+ struct uart_cmd cmd;
+
+ for (i = 0; i < sizeof(data); i++) {
+ assert((addr + i) <= UINT8_MAX);
+ cmd.addr = (uint8_t)(addr + i);
+ cmd.data = (data >> (i * 8)) & 0xff;
+
+ status = nios_access(dev, NIOS_PKT_LEGACY_DEV_CONFIG,
+ USB_DIR_HOST_TO_DEVICE, &cmd, 1);
+
+ if (status < 0) {
+ return status;
+ }
+ }
+
+ return 0;
+}
+
+int nios_legacy_config_read(struct bladerf *dev, uint32_t *val)
+{
+ int status;
+
+ status = nios_legacy_pio_read(dev, NIOS_PKT_LEGACY_PIO_ADDR_CONTROL, val);
+ if (status == 0) {
+ log_verbose("%s: 0x%08x\n", __FUNCTION__, val);
+ }
+
+ return status;
+}
+
+int nios_legacy_config_write(struct bladerf *dev, uint32_t val)
+{
+ log_verbose("%s: Writing 0x%08x\n", __FUNCTION__, val);
+ return nios_legacy_pio_write(dev, NIOS_PKT_LEGACY_PIO_ADDR_CONTROL, val);
+}
+
+int nios_legacy_get_fpga_version(struct bladerf *dev,
+ struct bladerf_version *ver)
+{
+ int i, status;
+ struct uart_cmd cmd;
+
+ for (i = 0; i < 4; i++) {
+ cmd.addr = NIOS_PKT_LEGACY_DEV_FPGA_VERSION_ID + i;
+ cmd.data = 0xff;
+
+ status = nios_access(dev, NIOS_PKT_LEGACY_DEV_CONFIG,
+ USB_DIR_DEVICE_TO_HOST, &cmd, 1);
+
+ if (status != 0) {
+ log_debug("Failed to read FPGA version[%d]: %s\n", i,
+ bladerf_strerror(status));
+
+ return status;
+ }
+
+ switch (i) {
+ case 0:
+ ver->major = cmd.data;
+ break;
+ case 1:
+ ver->minor = cmd.data;
+ break;
+ case 2:
+ ver->patch = cmd.data;
+ break;
+ case 3:
+ ver->patch |= (cmd.data << 8);
+ break;
+ default:
+ assert(!"Bug");
+ }
+ }
+
+ snprintf((char*)ver->describe, BLADERF_VERSION_STR_MAX,
+ "%d.%d.%d", ver->major, ver->minor, ver->patch);
+
+ return status;
+}
+
+int nios_legacy_get_timestamp(struct bladerf *dev, bladerf_direction dir,
+ uint64_t *value)
+{
+ int status = 0;
+ struct uart_cmd cmds[4];
+ uint8_t timestamp_bytes[8];
+ size_t i;
+
+ /* Offset 16 is the time tamer according to the Nios firmware */
+ cmds[0].addr = (dir == BLADERF_RX ? 16 : 24);
+ cmds[1].addr = (dir == BLADERF_RX ? 17 : 25);
+ cmds[2].addr = (dir == BLADERF_RX ? 18 : 26);
+ cmds[3].addr = (dir == BLADERF_RX ? 19 : 27);
+ cmds[0].data = cmds[1].data = cmds[2].data = cmds[3].data = 0xff;
+
+ status = nios_access(dev,
+ NIOS_PKT_LEGACY_DEV_CONFIG,
+ USB_DIR_DEVICE_TO_HOST,
+ cmds, ARRAY_SIZE(cmds));
+ if (status != 0) {
+ return status;
+ } else {
+ for (i = 0; i < 4; i++) {
+ timestamp_bytes[i] = cmds[i].data;
+ }
+ }
+
+ cmds[0].addr = (dir == BLADERF_RX ? 20 : 28);
+ cmds[1].addr = (dir == BLADERF_RX ? 21 : 29);
+ cmds[2].addr = (dir == BLADERF_RX ? 22 : 30);
+ cmds[3].addr = (dir == BLADERF_RX ? 23 : 31);
+ cmds[0].data = cmds[1].data = cmds[2].data = cmds[3].data = 0xff;
+
+ status = nios_access(dev,
+ NIOS_PKT_LEGACY_DEV_CONFIG,
+ USB_DIR_DEVICE_TO_HOST,
+ cmds, ARRAY_SIZE(cmds));
+
+ if (status) {
+ return status;
+ } else {
+ for (i = 0; i < 4; i++) {
+ timestamp_bytes[i + 4] = cmds[i].data;
+ }
+ }
+
+ memcpy(value, timestamp_bytes, sizeof(*value));
+ *value = LE64_TO_HOST(*value);
+
+ return 0;
+}
+
+int nios_legacy_si5338_read(struct bladerf *dev, uint8_t addr, uint8_t *data)
+{
+ int status;
+ struct uart_cmd cmd;
+
+ cmd.addr = addr;
+ cmd.data = 0xff;
+
+ status = nios_access(dev, NIOS_PKT_LEGACY_DEV_SI5338,
+ USB_DIR_DEVICE_TO_HOST, &cmd, 1);
+
+ if (status == 0) {
+ *data = cmd.data;
+ log_verbose("%s: 0x%2.2x 0x%2.2x\n", __FUNCTION__, addr, *data);
+ }
+
+ return status;
+}
+
+int nios_legacy_si5338_write(struct bladerf *dev, uint8_t addr, uint8_t data)
+{
+ struct uart_cmd cmd;
+
+ cmd.addr = addr;
+ cmd.data = data;
+
+ log_verbose( "%s: 0x%2.2x 0x%2.2x\n", __FUNCTION__, addr, data );
+
+ return nios_access(dev, NIOS_PKT_LEGACY_DEV_SI5338,
+ USB_DIR_HOST_TO_DEVICE, &cmd, 1);
+}
+
+int nios_legacy_lms6_read(struct bladerf *dev, uint8_t addr, uint8_t *data)
+{
+ int status;
+ struct uart_cmd cmd;
+
+ cmd.addr = addr;
+ cmd.data = 0xff;
+
+ status = nios_access(dev, NIOS_PKT_LEGACY_DEV_LMS,
+ USB_DIR_DEVICE_TO_HOST, &cmd, 1);
+
+ if (status == 0) {
+ *data = cmd.data;
+ log_verbose( "%s: 0x%2.2x 0x%2.2x\n", __FUNCTION__, addr, *data );
+ }
+
+ return status;
+}
+
+int nios_legacy_lms6_write(struct bladerf *dev, uint8_t addr, uint8_t data)
+{
+ int status;
+ struct uart_cmd cmd;
+
+ cmd.addr = addr;
+ cmd.data = data;
+
+ log_verbose( "%s: 0x%2.2x 0x%2.2x\n", __FUNCTION__, addr, data );
+
+ status = nios_access(dev, NIOS_PKT_LEGACY_DEV_LMS,
+ USB_DIR_HOST_TO_DEVICE, &cmd, 1);
+
+ return status;
+}
+
+int nios_legacy_ina219_read(struct bladerf *dev, uint8_t addr, uint16_t *data)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_ina219_write(struct bladerf *dev, uint8_t addr, uint16_t data)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_ad9361_spi_read(struct bladerf *dev, uint16_t cmd,
+ uint64_t *data)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_ad9361_spi_write(struct bladerf *dev, uint16_t cmd,
+ uint64_t data)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_adi_axi_read(struct bladerf *dev, uint32_t addr,
+ uint32_t *data)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_adi_axi_write(struct bladerf *dev, uint32_t cmd,
+ uint32_t data)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_rfic_command_read(struct bladerf *dev,
+ uint16_t cmd,
+ uint64_t *data)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_rfic_command_write(struct bladerf *dev,
+ uint16_t cmd,
+ uint64_t data)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_rffe_control_read(struct bladerf *dev, uint32_t *value)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_rffe_control_write(struct bladerf *dev, uint32_t value)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_rffe_fastlock_save(struct bladerf *dev, bool is_tx,
+ uint8_t rffe_profile, uint16_t nios_profile)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_ad56x1_vctcxo_trim_dac_read(struct bladerf *dev, uint16_t *value)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_ad56x1_vctcxo_trim_dac_write(struct bladerf *dev, uint16_t value)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_adf400x_read(struct bladerf *dev, uint8_t addr, uint32_t *data)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_adf400x_write(struct bladerf *dev, uint8_t addr, uint32_t data)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_vctcxo_trim_dac_write(struct bladerf *dev, uint8_t addr, uint16_t value)
+{
+ int status;
+ struct uart_cmd cmd;
+ int base;
+
+ /* Only pass through writes of the DAC value, for compatibility with
+ * dac161s055 driver */
+ if (addr != 0x08) {
+ return 0;
+ }
+
+ /* FPGA v0.0.4 introduced a change to the location of the DAC registers */
+ const bool legacy_location =
+ !have_cap(dev->board->get_capabilities(dev), BLADERF_CAP_UPDATED_DAC_ADDR);
+
+ base = legacy_location ? 0 : 34;
+
+ cmd.addr = base;
+ cmd.data = value & 0xff;
+ status = nios_access(dev,
+ legacy_location ?
+ NIOS_PKT_LEGACY_DEV_VCTCXO :
+ NIOS_PKT_LEGACY_DEV_CONFIG,
+ USB_DIR_HOST_TO_DEVICE, &cmd, 1);
+
+ if (status < 0) {
+ return status;
+ }
+
+ cmd.addr = base + 1;
+ cmd.data = (value >> 8) & 0xff;
+ status = nios_access(dev,
+ legacy_location ?
+ NIOS_PKT_LEGACY_DEV_VCTCXO :
+ NIOS_PKT_LEGACY_DEV_CONFIG,
+ USB_DIR_HOST_TO_DEVICE, &cmd, 1);
+
+ return status;
+}
+
+int nios_legacy_set_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode mode)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_get_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode *mode)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ *mode = BLADERF_VCTCXO_TAMER_INVALID;
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int get_iq_correction(struct bladerf *dev, uint8_t addr, int16_t *value)
+{
+ int i;
+ int status;
+ struct uart_cmd cmd;
+
+ *value = 0;
+ for (i = status = 0; status == 0 && i < 2; i++) {
+ cmd.addr = i + addr;
+ cmd.data = 0xff;
+ status = nios_access(dev, NIOS_PKT_LEGACY_DEV_CONFIG,
+ USB_DIR_DEVICE_TO_HOST, &cmd, 1);
+
+ *value |= (cmd.data << (i * 8));
+ }
+
+ return status;
+}
+
+static int set_iq_correction(struct bladerf *dev, uint8_t addr, int16_t value)
+{
+ int i;
+ int status;
+ struct uart_cmd cmd;
+
+ for (i = status = 0; status == 0 && i < 2; i++) {
+ cmd.addr = i + addr;
+
+ cmd.data = (value >> (i * 8)) & 0xff;
+ status = nios_access(dev, NIOS_PKT_LEGACY_DEV_CONFIG,
+ USB_DIR_HOST_TO_DEVICE, &cmd, 1);
+ }
+
+ return status;
+}
+
+
+int nios_legacy_get_iq_gain_correction(struct bladerf *dev,
+ bladerf_channel ch, int16_t *value)
+{
+ int status;
+ uint8_t addr;
+
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ addr = NIOS_PKT_LEGACY_DEV_RX_GAIN_ADDR;
+ break;
+
+ case BLADERF_CHANNEL_TX(0):
+ addr = NIOS_PKT_LEGACY_DEV_TX_GAIN_ADDR;
+ break;
+
+ default:
+ log_debug("%s: invalid channel provided (0x%x)\n",
+ __FUNCTION__, ch);
+
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = get_iq_correction(dev, addr, value);
+
+ return status;
+}
+
+int nios_legacy_get_iq_phase_correction(struct bladerf *dev,
+ bladerf_channel ch, int16_t *value)
+{
+ uint8_t addr;
+
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ addr = NIOS_PKT_LEGACY_DEV_RX_PHASE_ADDR;
+ break;
+
+ case BLADERF_CHANNEL_TX(0):
+ addr = NIOS_PKT_LEGACY_DEV_TX_PHASE_ADDR;
+ break;
+
+ default:
+ log_debug("%s: invalid channel provided (0x%x)\n",
+ __FUNCTION__, ch);
+
+ return BLADERF_ERR_INVAL;
+ }
+
+ return get_iq_correction(dev, addr, value);
+}
+
+int nios_legacy_set_iq_gain_correction(struct bladerf *dev,
+ bladerf_channel ch, int16_t value)
+{
+ uint8_t addr;
+
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ addr = NIOS_PKT_LEGACY_DEV_RX_GAIN_ADDR;
+ log_verbose("Setting RX IQ Correction phase: %d\n", value);
+ break;
+
+ case BLADERF_CHANNEL_TX(0):
+ addr = NIOS_PKT_LEGACY_DEV_TX_GAIN_ADDR;
+ log_verbose("Setting TX IQ Correction phase: %d\n", value);
+ break;
+
+ default:
+ log_debug("%s: invalid channel provided (0x%x)\n",
+ __FUNCTION__, ch);
+
+ return BLADERF_ERR_INVAL;
+ }
+
+ return set_iq_correction(dev, addr, value);
+}
+
+int nios_legacy_set_iq_phase_correction(struct bladerf *dev,
+ bladerf_channel ch, int16_t value)
+{
+ uint8_t addr;
+
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ log_verbose("Setting RX IQ Correction phase: %d\n", value);
+ addr = NIOS_PKT_LEGACY_DEV_RX_PHASE_ADDR;
+ break;
+
+ case BLADERF_CHANNEL_TX(0):
+ log_verbose("Setting TX IQ Correction phase: %d\n", value);
+ addr = NIOS_PKT_LEGACY_DEV_TX_PHASE_ADDR;
+ break;
+
+ default:
+ log_debug("%s: invalid channel provided (0x%x)\n",
+ __FUNCTION__, ch);
+
+ return BLADERF_ERR_INVAL;
+ }
+
+ return set_iq_correction(dev, addr, value);
+}
+
+int nios_legacy_xb200_synth_write(struct bladerf *dev, uint32_t value)
+{
+ log_verbose("%s: 0x%08x\n", __FUNCTION__, value);
+ return nios_legacy_pio_write(dev,
+ NIOS_PKT_LEGACY_PIO_ADDR_XB200_SYNTH, value);
+}
+
+int nios_legacy_expansion_gpio_read(struct bladerf *dev, uint32_t *val)
+{
+ int status = nios_legacy_pio_read(dev, NIOS_PKT_LEGACY_PIO_ADDR_EXP, val);
+
+ if (status == 0) {
+ log_verbose("%s: 0x%08x\n", __FUNCTION__, val);
+ }
+
+ return status;
+}
+
+int nios_legacy_expansion_gpio_write(struct bladerf *dev,
+ uint32_t mask, uint32_t val)
+{
+ int status;
+ uint32_t tmp;
+
+ if (mask != 0xffffffff) {
+ status = nios_legacy_pio_read(dev, NIOS_PKT_LEGACY_PIO_ADDR_EXP, &tmp);
+ if (status != 0) {
+ return status;
+ }
+
+ tmp &= ~mask;
+ tmp |= (val & mask);
+ val = tmp;
+ }
+
+ log_verbose("%s: 0x%08x\n", __FUNCTION__, val);
+ return nios_legacy_pio_write(dev, NIOS_PKT_LEGACY_PIO_ADDR_EXP, val);
+}
+
+int nios_legacy_expansion_gpio_dir_read(struct bladerf *dev, uint32_t *val)
+{
+ int status = nios_legacy_pio_read(dev,
+ NIOS_PKT_LEGACY_PIO_ADDR_EXP_DIR, val);
+
+ if (status == 0) {
+ log_verbose("%s: 0x%08x\n", __FUNCTION__, val);
+ }
+
+ return status;
+}
+
+int nios_legacy_expansion_gpio_dir_write(struct bladerf *dev,
+ uint32_t mask, uint32_t val)
+{
+ int status;
+ uint32_t tmp;
+
+ if (mask != 0xffffffff) {
+ status = nios_legacy_pio_read(dev,
+ NIOS_PKT_LEGACY_PIO_ADDR_EXP_DIR, &tmp);
+
+ if (status != 0) {
+ return status;
+ }
+
+ tmp &= ~mask;
+ tmp |= (val & mask);
+ val = tmp;
+ }
+
+ log_verbose("%s: 0x%08\n", __FUNCTION__, val);
+ return nios_legacy_pio_write(dev, NIOS_PKT_LEGACY_PIO_ADDR_EXP_DIR, val);
+}
+
+int nios_legacy_read_trigger(struct bladerf *dev, bladerf_channel ch,
+ bladerf_trigger_signal trigger, uint8_t *value)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+int nios_legacy_write_trigger(struct bladerf *dev, bladerf_channel ch,
+ bladerf_trigger_signal trigger, uint8_t value)
+{
+ log_debug("This operation is not supported by the legacy NIOS packet format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
diff --git a/Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.h b/Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.h
new file mode 100644
index 0000000..92f4b77
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.h
@@ -0,0 +1,488 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014-2015 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* This file defines functionality used to communicate with the NIOS II
+ * soft-core processor running on the FPGA versions prior to v0.2.x.
+ * (Although post v0.2.x FPGAs support this for reverse compatibility).
+ *
+ * The host communicates with the NIOS II via USB transfers to the Cypress FX3,
+ * which then communicates with the FPGA via a UART. This module packs and
+ * submits the requests, and receives and unpacks the responses.
+ *
+ * See the files in the top-level fpga_common/ directory for description
+ * and macros pertaining to the legacy packet format.
+ */
+
+#ifndef BACKEND_USB_NIOS_LEGACY_ACCESS_H_
+#define BACKEND_USB_NIOS_LEGACY_ACCESS_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "board/board.h"
+#include "usb.h"
+
+/**
+ * Perform a read from device/blocks connected via NIOS II PIO.
+ *
+ * @param dev Device handle
+ * @param[in] addr Virtual "register address." Use NIOS_LEGACY_PIO_ADDR_*
+ * definitions.
+ * @param[out] data Read data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_pio_read(struct bladerf *dev, uint8_t addr, uint32_t *data);
+
+/**
+ * Perform a write to device/blocks connected via NIOS II PIO.
+ *
+ * @param dev Device handle
+ * @param[in] addr Virtual "register address." Use NIOS_LEGACY_PIO_ADDR_*
+ * definitions.
+ * @param[in] data Data to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_pio_write(struct bladerf *dev, uint8_t addr, uint32_t data);
+
+/**
+ * Read from the FPGA's config register
+ *
+ * @param dev Device handle
+ * @param[out] val On success, updated with config register value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_config_read(struct bladerf *dev, uint32_t *val);
+
+/**
+ * Write to FPGA's config register
+ *
+ * @param dev Device handle
+ * @param[in] val Register value to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_config_write(struct bladerf *dev, uint32_t val);
+
+/**
+ * Read the FPGA version into the provided version structure
+ *
+ * @param dev Device handle
+ * @param[out] ver Updated with FPGA version (upon success)
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_get_fpga_version(struct bladerf *dev,
+ struct bladerf_version *ver);
+
+/**
+ * Read a timestamp counter value
+ *
+ * @param dev Device handle
+ * @param[in] dir Stream direction
+ * @param[out] timestamp On success, updated with the timestamp value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_get_timestamp(struct bladerf *dev,
+ bladerf_direction dir,
+ uint64_t *timestamp);
+
+/**
+ * Read from an Si5338 register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[out] data On success, updated with read data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_si5338_read(struct bladerf *dev, uint8_t addr, uint8_t *data);
+
+/**
+ * Write to an Si5338 register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[in] data Data to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_si5338_write(struct bladerf *dev, uint8_t addr, uint8_t data);
+
+/**
+ * Read from an LMS6002D register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[out] data On success, updated with data read from the device
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_lms6_read(struct bladerf *dev, uint8_t addr, uint8_t *data);
+
+/**
+ * Write to an LMS6002D register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[out] data Register data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_lms6_write(struct bladerf *dev, uint8_t addr, uint8_t data);
+
+/**
+ * Read from an INA219 register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_ina219_read(struct bladerf *dev, uint8_t addr, uint16_t *data);
+
+/**
+ * Write to an INA219 register
+ *
+ * @param dev Device handle
+ * @param[in] addr Register address
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_ina219_write(struct bladerf *dev, uint8_t addr, uint16_t data);
+
+/**
+ * Read the AD9361 over SPI.
+ *
+ * @param dev Device handle
+ * @param[in] cmd Command
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_ad9361_spi_read(struct bladerf *dev,
+ uint16_t cmd,
+ uint64_t *data);
+
+/**
+ * Write to the AD9361 over SPI.
+ *
+ * @param dev Device handle
+ * @param[in] cmd Command
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_ad9361_spi_write(struct bladerf *dev,
+ uint16_t cmd,
+ uint64_t data);
+
+/**
+ * Read the ADI AXI memory mapped region.
+ *
+ * @param dev Device handle
+ * @param[in] addr Address
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_adi_axi_read(struct bladerf *dev,
+ uint32_t addr,
+ uint32_t *data);
+
+/**
+ * Write to the ADI AXI memory mapped region.
+ *
+ * @param dev Device handle
+ * @param[in] addr Address
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_adi_axi_write(struct bladerf *dev,
+ uint32_t addr,
+ uint32_t data);
+
+/**
+ * Read RFIC command
+ *
+ * @param dev Device handle
+ * @param[in] cmd Command
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_rfic_command_read(struct bladerf *dev,
+ uint16_t cmd,
+ uint64_t *data);
+
+/**
+ * Write RFIC command
+ *
+ * @param dev Device handle
+ * @param[in] cmd Command
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_rfic_command_write(struct bladerf *dev,
+ uint16_t cmd,
+ uint64_t data);
+
+/**
+ * Read RFFE control register.
+ *
+ * @param dev Device handle
+ * @param[in] len Data buffer length
+ * @param[out] value Value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_rffe_control_read(struct bladerf *dev, uint32_t *value);
+
+/**
+ * Write RFFE control register.
+ *
+ * @param dev Device handle
+ * @param[in] len Data buffer length
+ * @param[in] value Value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_rffe_control_write(struct bladerf *dev, uint32_t value);
+
+/**
+ * Save an RFFE fast lock profile to the Nios.
+ *
+ * @param dev Device handle
+ * @param[in] is_tx True if TX profile, false if RX profile
+ * @param[in] rffe_profile RFFE profile to save
+ * @param[in] nios_profile Where to save the profile in the Nios
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_rffe_fastlock_save(struct bladerf *dev, bool is_tx,
+ uint8_t rffe_profile,
+ uint16_t nios_profile);
+
+/**
+ * Write to the AD56X1 VCTCXO trim DAC.
+ *
+ * @param dev Device handle
+ * @param[in] value Value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_ad56x1_vctcxo_trim_dac_write(struct bladerf *dev,
+ uint16_t value);
+
+/**
+ * Read the AD56X1 VCTCXO trim DAC.
+ *
+ * @param dev Device handle
+ * @param[out] value Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_ad56x1_vctcxo_trim_dac_read(struct bladerf *dev,
+ uint16_t *value);
+
+/**
+ * Write to the ADF400X.
+ *
+ * @param dev Device handle
+ * @param[in] addr Address
+ * @param[in] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_adf400x_write(struct bladerf *dev, uint8_t addr, uint32_t data);
+
+/**
+ * Read from the ADF400X.
+ *
+ * @param dev Device handle
+ * @param[in] addr Address
+ * @param[out] data Data
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_adf400x_read(struct bladerf *dev, uint8_t addr, uint32_t *data);
+
+/**
+ * Write to the VCTCXO trim DAC.
+ *
+ * @param dev Device handle
+ * @param[in] addr Register (should be 0x08)
+ * @param[in] value Value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_vctcxo_trim_dac_write(struct bladerf *dev,
+ uint8_t addr,
+ uint16_t value);
+
+/**
+ * Read a IQ gain correction value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] value On success, updated with phase correction value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_get_iq_gain_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t *value);
+
+/**
+ * Read a IQ phase correction value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] value On success, updated with phase correction value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_get_iq_phase_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t *value);
+
+/**
+ * Write an IQ gain correction value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] value Correction value to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_set_iq_gain_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t value);
+
+/**
+ * Write an IQ phase correction value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] value Correction value to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_set_iq_phase_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ int16_t value);
+
+/**
+ * Write a value to the XB-200's ADF4351 synthesizer
+ *
+ * @param dev Device handle
+ * @param[in] value Write value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_xb200_synth_write(struct bladerf *dev, uint32_t value);
+
+/**
+ * Read from expanion GPIO
+ *
+ * @param dev Device handle
+ * @param[out] value On success, updated with read value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_expansion_gpio_read(struct bladerf *dev, uint32_t *val);
+
+/**
+ * Write to expansion GPIO
+ *
+ * @param dev Device handle
+ * @param[in[ mask Mask denoting bits to write
+ * @param[in] value Value to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_expansion_gpio_write(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t val);
+
+/**
+ * Read from expansion GPIO direction register
+ *
+ * @param dev Device handle
+ * @param[out] value On success, updated with read value
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_expansion_gpio_dir_read(struct bladerf *dev, uint32_t *val);
+
+/**
+ * Write to expansion GPIO direction register
+ *
+ * @param dev Device handle
+ * @param[in] mask Mask denoting bits to configure
+ * @param[in] output '1' bit denotes an output, '0' bit denotes an input
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error.
+ */
+int nios_legacy_expansion_gpio_dir_write(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t outputs);
+
+/**
+ * Read trigger register value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] trigger Trigger to read from
+ * @param[out] value On success, updated with register value
+ *
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error
+ */
+int nios_legacy_read_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t *value);
+
+/**
+ * Write trigger register value
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] trigger Trigger to read
+ * @param[in] value Value to write
+ *
+ * @return 0 on success, BLADERF_ERR_* code on error
+ */
+int nios_legacy_write_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t value);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/backend/usb/usb.c b/Radio/HW/BladeRF/src/backend/usb/usb.c
new file mode 100644
index 0000000..fba2c33
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/usb/usb.c
@@ -0,0 +1,1430 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014-2017 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "rel_assert.h"
+#include "log.h"
+#include "minmax.h"
+#include "conversions.h"
+#include "usb.h"
+
+#include "board/board.h"
+#include "backend/backend.h"
+#include "backend/backend_config.h"
+#include "backend/usb/usb.h"
+#include "driver/fx3_fw.h"
+#include "streaming/async.h"
+#include "helpers/version.h"
+
+#include "bladeRF.h"
+#include "nios_pkt_formats.h"
+#include "nios_legacy_access.h"
+#include "nios_access.h"
+
+#if ENABLE_USB_DEV_RESET_ON_OPEN
+bool bladerf_usb_reset_device_on_open = true;
+#endif
+
+static const struct usb_driver *usb_driver_list[] = BLADERF_USB_BACKEND_LIST;
+
+/* FW declaration of fn table declared at the end of this file */
+const struct backend_fns backend_fns_usb_legacy;
+
+/* Vendor command wrapper to gets a 32-bit integer and supplies a wIndex */
+static inline int vendor_cmd_int_windex(struct bladerf *dev, uint8_t cmd,
+ uint16_t windex, int32_t *val)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+
+ return usb->fn->control_transfer(usb->driver,
+ USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ USB_DIR_DEVICE_TO_HOST,
+ cmd, 0, windex,
+ val, sizeof(uint32_t),
+ CTRL_TIMEOUT_MS);
+}
+
+/* Vendor command wrapper to get a 32-bit integer and supplies wValue */
+static inline int vendor_cmd_int_wvalue(struct bladerf *dev, uint8_t cmd,
+ uint16_t wvalue, int32_t *val)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+
+ return usb->fn->control_transfer(usb->driver,
+ USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ USB_DIR_DEVICE_TO_HOST,
+ cmd, wvalue, 0,
+ val, sizeof(uint32_t),
+ CTRL_TIMEOUT_MS);
+}
+
+
+/* Vendor command that gets/sets a 32-bit integer value */
+static inline int vendor_cmd_int(struct bladerf *dev, uint8_t cmd,
+ usb_direction dir, int32_t *val)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+
+ return usb->fn->control_transfer(usb->driver,
+ USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ dir, cmd, 0, 0,
+ val, sizeof(int32_t),
+ CTRL_TIMEOUT_MS);
+}
+
+static inline int change_setting(struct bladerf *dev, uint8_t setting)
+{
+ int status;
+ struct bladerf_usb *usb = dev->backend_data;
+
+ log_verbose("Changing to USB alt setting %u\n", setting);
+
+ status = usb->fn->change_setting(usb->driver, setting);
+ if (status != 0) {
+ log_debug("Failed to change setting: %s\n", bladerf_strerror(status));
+ }
+
+ return status;
+}
+
+static int usb_is_fpga_configured(struct bladerf *dev)
+{
+ int result = -1;
+ int status;
+
+ /* This environment variable provides a means to force libbladeRF to not
+ * attempt to access the FPGA.
+ *
+ * This provides a workaround for the situation where a user did not remove
+ * an FPGA in SPI flash prior to flashing new firmware and updating
+ * libbladeRF. Specifically, this has proven to be a problem with pre-v0.0.1
+ * FPGA images, as they do not provide version readback functionality.
+ */
+ if (getenv("BLADERF_FORCE_NO_FPGA_PRESENT")) {
+ log_debug("Reporting no FPGA present - "
+ "BLADERF_FORCE_NO_FPGA_PRESENT is set.\n");
+ return 0;
+ }
+
+ status = vendor_cmd_int(dev, BLADE_USB_CMD_QUERY_FPGA_STATUS,
+ USB_DIR_DEVICE_TO_HOST, &result);
+
+ if (status < 0) {
+ return status;
+ } else if (result == 0 || result == 1) {
+ return result;
+ } else {
+ log_debug("Unexpected result from FPGA status query: %d\n", result);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+}
+
+static int usb_set_fpga_protocol(struct bladerf *dev, backend_fpga_protocol fpga_protocol)
+{
+ if (fpga_protocol == BACKEND_FPGA_PROTOCOL_NIOSII_LEGACY) {
+ dev->backend = &backend_fns_usb_legacy;
+ } else if (fpga_protocol == BACKEND_FPGA_PROTOCOL_NIOSII) {
+ dev->backend = &backend_fns_usb;
+ } else {
+ log_error("Unknown FPGA protocol: %d\n", fpga_protocol);
+ return BLADERF_ERR_INVAL;
+ }
+
+ return 0;
+}
+
+static bladerf_fpga_source usb_get_fpga_source(struct bladerf *dev)
+{
+ int result = -1;
+ int status;
+
+ status = vendor_cmd_int(dev, BLADE_USB_CMD_QUERY_FPGA_SOURCE,
+ USB_DIR_DEVICE_TO_HOST, &result);
+
+ if (status < 0) {
+ log_debug("%s: vendor_cmd_int returned %s\n", __FUNCTION__,
+ bladerf_strerror(status));
+ return BLADERF_FPGA_SOURCE_UNKNOWN;
+ } else if (0 == result || 1 == result || 2 == result) {
+ return (bladerf_fpga_source)result;
+ } else {
+ log_debug("Unexpected result from FPGA source query: %d\n", result);
+ return BLADERF_FPGA_SOURCE_UNKNOWN;
+ }
+}
+
+/* After performing a flash operation, switch back to either RF_LINK or the
+ * FPGA loader.
+ */
+static int restore_post_flash_setting(struct bladerf *dev)
+{
+ int fpga_loaded = usb_is_fpga_configured(dev);
+ int status;
+
+ if (fpga_loaded < 0) {
+ status = fpga_loaded;
+ log_debug("Failed to determine if FPGA is loaded (%d)\n", fpga_loaded);
+ } else if (fpga_loaded) {
+ status = change_setting(dev, USB_IF_RF_LINK);
+ } else {
+ status = change_setting(dev, USB_IF_CONFIG);
+ }
+
+ if (status < 0) {
+ log_debug("Failed to restore alt setting: %s\n",
+ bladerf_strerror(status));
+ }
+ return status;
+}
+
+static bool usb_matches(bladerf_backend backend)
+{
+ return backend == BLADERF_BACKEND_ANY ||
+ backend == BLADERF_BACKEND_LINUX ||
+ backend == BLADERF_BACKEND_LIBUSB ||
+ backend == BLADERF_BACKEND_CYPRESS;
+}
+
+static int usb_probe(backend_probe_target probe_target,
+ struct bladerf_devinfo_list *info_list)
+{
+ int status;
+ size_t i;
+
+ for (i = status = 0; i < ARRAY_SIZE(usb_driver_list); i++) {
+ status = usb_driver_list[i]->fn->probe(probe_target, info_list);
+ }
+
+ return status;
+}
+
+static void usb_close(struct bladerf *dev)
+{
+ int status;
+ struct bladerf_usb *usb = dev->backend_data;
+
+ if (usb != NULL) {
+ /* It seems we need to switch back to our NULL interface before closing,
+ * or else our device doesn't close upon exit in OSX and then fails to
+ * re-open cleanly */
+ status = usb->fn->change_setting(usb->driver, USB_IF_NULL);
+ if (status != 0) {
+ log_error("Failed to switch to NULL interface: %s\n",
+ bladerf_strerror(status));
+ }
+
+ usb->fn->close(usb->driver);
+ free(usb);
+ dev->backend_data = NULL;
+ }
+}
+
+static int usb_is_fw_ready(struct bladerf *dev)
+{
+ int status;
+ int result;
+
+ status = vendor_cmd_int(dev, BLADE_USB_CMD_QUERY_DEVICE_READY,
+ USB_DIR_DEVICE_TO_HOST, &result);
+ if (status < 0) {
+ return status;
+ } else if (result == 0 || result == 1) {
+ return result;
+ } else {
+ log_debug("Unexpected result from firmware status query: %d\n", result);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+}
+
+static int usb_get_handle(struct bladerf *dev,
+ void **handle)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+ int status;
+
+ status = usb->fn->get_handle(usb->driver, handle);
+
+ return status;
+}
+
+static int usb_get_fw_version(struct bladerf *dev,
+ struct bladerf_version *version)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+ int status;
+
+ status = usb->fn->get_string_descriptor(usb->driver,
+ BLADE_USB_STR_INDEX_FW_VER,
+ (unsigned char *)version->describe,
+ BLADERF_VERSION_STR_MAX);
+ if (status == 0) {
+ status = str2version(version->describe, version);
+ } else {
+ log_warning("Failed to retrieve firmware version. This may be due "
+ "to an old firmware version that does not support "
+ "this request. A firmware update via the bootloader is "
+ "required.\n\n");
+ status = BLADERF_ERR_UPDATE_FW;
+ }
+
+ return status;
+}
+
+static int usb_get_fpga_version(struct bladerf *dev,
+ struct bladerf_version *version)
+{
+ int status;
+
+ status = change_setting(dev, USB_IF_RF_LINK);
+ if (status < 0) {
+ return status;
+ }
+
+ /* Read and store FPGA version info. This is only possible after
+ * we've entered RF link mode.
+ *
+ * The legacy mode is used here since we can't yet determine if
+ * the FPGA is capable of using the newer packet formats. */
+ return nios_legacy_get_fpga_version(dev, version);
+}
+
+static int usb_open(struct bladerf *dev, struct bladerf_devinfo *info)
+{
+ int status;
+ size_t i;
+ struct bladerf_usb *usb;
+
+ usb = malloc(sizeof(*usb));
+ if (usb == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Try each matching usb driver */
+ for (i = 0; i < ARRAY_SIZE(usb_driver_list); i++) {
+ if (info->backend == BLADERF_BACKEND_ANY
+ || usb_driver_list[i]->id == info->backend) {
+ usb->fn = usb_driver_list[i]->fn;
+ status = usb->fn->open(&usb->driver, info, &dev->ident);
+ if (status == 0) {
+ break;
+ } else if (status == BLADERF_ERR_NODEV) {
+ continue;
+ } else {
+ free(usb);
+ return status;
+ }
+ }
+ }
+
+ /* If no usb driver was found */
+ if (i == ARRAY_SIZE(usb_driver_list)) {
+ free(usb);
+ return BLADERF_ERR_NODEV;
+ }
+
+ /* Default to legacy-mode access until we determine the FPGA is
+ * capable of handling newer request formats */
+ dev->backend = &backend_fns_usb_legacy;
+ dev->backend_data = usb;
+
+ /* Just out of paranoia, put the device into a known state */
+ status = change_setting(dev, USB_IF_NULL);
+ if (status < 0) {
+ log_debug("Failed to switch to USB_IF_NULL\n");
+ goto error;
+ }
+
+error:
+ if (status != 0) {
+ usb_close(dev);
+ }
+
+ return status;
+}
+
+static int usb_get_vid_pid(struct bladerf *dev, uint16_t *vid, uint16_t *pid)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+
+ return usb->fn->get_vid_pid(usb->driver, vid, pid);
+}
+
+static int usb_get_flash_id(struct bladerf *dev, uint8_t *mid, uint8_t *did)
+{
+ int status;
+ int result;
+
+ status = vendor_cmd_int(dev, BLADE_USB_CMD_QUERY_FLASH_ID,
+ USB_DIR_DEVICE_TO_HOST, &result);
+ if (status < 0) {
+ log_debug("Could not read flash manufacturer ID and/or device ID. %s.\n",
+ bladerf_strerror(status));
+ } else {
+ *did = result & 0xFF;
+ *mid = (result >> 8) & 0xFF;
+ }
+ return status;
+}
+
+static int begin_fpga_programming(struct bladerf *dev)
+{
+ int result;
+ int status = vendor_cmd_int(dev, BLADE_USB_CMD_BEGIN_PROG,
+ USB_DIR_DEVICE_TO_HOST, &result);
+
+ if (status != 0) {
+ return status;
+ } else if (result != 0) {
+ log_debug("Startg fpga programming, result = %d\n", result);
+ return BLADERF_ERR_UNEXPECTED;
+ } else {
+ return 0;
+ }
+}
+
+static int usb_load_fpga(struct bladerf *dev, const uint8_t *image, size_t image_size)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+
+ unsigned int wait_count;
+ const unsigned int timeout_ms = (3 * CTRL_TIMEOUT_MS);
+ int status;
+
+ /* Switch to the FPGA configuration interface */
+ status = change_setting(dev, USB_IF_CONFIG);
+ if(status < 0) {
+ log_debug("Failed to switch to FPGA config setting: %s\n",
+ bladerf_strerror(status));
+ return status;
+ }
+
+ /* Begin programming */
+ status = begin_fpga_programming(dev);
+ if (status < 0) {
+ log_debug("Failed to initiate FPGA programming: %s\n",
+ bladerf_strerror(status));
+ return status;
+ }
+
+ /* Send the file down */
+ assert(image_size <= UINT32_MAX);
+ status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_OUT,
+ (void *)image,
+ (uint32_t)image_size,
+ timeout_ms);
+ if (status < 0) {
+ log_debug("Failed to write FPGA bitstream to FPGA: %s\n",
+ bladerf_strerror(status));
+ return status;
+ }
+
+ /* Poll FPGA status to determine if programming was a success */
+ wait_count = 10;
+ status = 0;
+
+ while (wait_count > 0 && status == 0) {
+ status = usb_is_fpga_configured(dev);
+ if (status == 1) {
+ break;
+ }
+
+ usleep(200000);
+ wait_count--;
+ }
+
+ /* Failed to determine if FPGA is loaded */
+ if (status < 0) {
+ log_debug("Failed to determine if FPGA is loaded: %s\n",
+ bladerf_strerror(status));
+ return status;
+ } else if (wait_count == 0 && status != 0) {
+ log_debug("Timeout while waiting for FPGA configuration status\n");
+ return BLADERF_ERR_TIMEOUT;
+ }
+
+ return 0;
+}
+
+static inline int perform_erase(struct bladerf *dev, uint16_t block)
+{
+ int status, erase_ret;
+ struct bladerf_usb *usb = dev->backend_data;
+
+ status = usb->fn->control_transfer(usb->driver,
+ USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ USB_DIR_DEVICE_TO_HOST,
+ BLADE_USB_CMD_FLASH_ERASE,
+ 0, block,
+ &erase_ret, sizeof(erase_ret),
+ CTRL_TIMEOUT_MS);
+
+
+ return status;
+}
+
+static int usb_erase_flash_blocks(struct bladerf *dev,
+ uint32_t eb,
+ uint16_t count)
+{
+ int status, restore_status;
+ uint16_t i;
+
+ status = change_setting(dev, USB_IF_SPI_FLASH);
+ if (status != 0) {
+ return status;
+ }
+
+ log_info("Erasing %u block%s starting at block %u\n", count,
+ 1 == count ? "" : "s", eb);
+
+ for (i = 0; i < count; i++) {
+ log_info("Erasing block %u (%u%%)...%c", eb + i,
+ (i + 1) == count ? 100 : 100 * i / count,
+ (i + 1) == count ? '\n' : '\r');
+
+ status = perform_erase(dev, eb + i);
+ if (status != 0) {
+ log_debug("Failed to erase block %u: %s\n", eb + i,
+ bladerf_strerror(status));
+ goto error;
+ }
+ }
+
+ log_info("Done erasing %u block%s\n", count, 1 == count ? "" : "s");
+
+error:
+ restore_status = restore_post_flash_setting(dev);
+ return status != 0 ? status : restore_status;
+}
+
+static inline int read_page(struct bladerf *dev, uint8_t read_operation,
+ uint16_t page, uint8_t *buf)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+ bladerf_dev_speed usb_speed;
+ int status;
+ int32_t op_status;
+ uint16_t read_size;
+ uint16_t offset;
+ uint8_t request;
+
+ if (usb->fn->get_speed(usb->driver, &usb_speed) != 0) {
+ log_debug("Error getting USB speed in %s\n", __FUNCTION__);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ if (usb_speed == BLADERF_DEVICE_SPEED_SUPER) {
+ read_size = dev->flash_arch->psize_bytes;
+ } else if (usb_speed == BLADERF_DEVICE_SPEED_HIGH) {
+ read_size = 64;
+ } else {
+ log_debug("Encountered unknown USB speed in %s\n", __FUNCTION__);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ if (read_operation == BLADE_USB_CMD_FLASH_READ ||
+ read_operation == BLADE_USB_CMD_READ_OTP) {
+
+ status = vendor_cmd_int_windex(dev, read_operation, page, &op_status);
+ if (status != 0) {
+ return status;
+ } else if (op_status != 0) {
+ log_error("Firmware page read (op=%d) failed at page %u: %d\n",
+ read_operation, page, op_status);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* Both of these operations require a read from the FW's page buffer */
+ request = BLADE_USB_CMD_READ_PAGE_BUFFER;
+
+ } else if (read_operation == BLADE_USB_CMD_READ_CAL_CACHE) {
+ request = read_operation;
+ } else {
+ assert(!"Bug - invalid read_operation value");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* Retrieve data from the firmware page buffer */
+ for (offset = 0; offset < dev->flash_arch->psize_bytes; offset += read_size) {
+ status = usb->fn->control_transfer(usb->driver,
+ USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ USB_DIR_DEVICE_TO_HOST,
+ request,
+ 0,
+ offset, /* in bytes */
+ buf + offset,
+ read_size,
+ CTRL_TIMEOUT_MS);
+
+ if(status < 0) {
+ log_debug("Failed to read page buffer at offset 0x%02x: %s\n",
+ offset, bladerf_strerror(status));
+ return status;
+ }
+ }
+
+ return 0;
+}
+
+static int usb_read_flash_pages(struct bladerf *dev,
+ uint8_t *buf,
+ uint32_t page_u32,
+ uint32_t count_u32)
+{
+ int status;
+ size_t n_read;
+ uint16_t i;
+
+ /* 16-bit control transfer fields are used for these.
+ * The current bladeRF build only has a 4MiB flash, anyway. */
+ const uint16_t page = (uint16_t)page_u32;
+ const uint16_t count = (uint16_t)count_u32;
+
+ assert(page == page_u32);
+ assert(count == count_u32);
+
+ status = change_setting(dev, USB_IF_SPI_FLASH);
+ if (status != 0) {
+ return status;
+ }
+
+ log_info("Reading %u page%s starting at page %u\n", count,
+ 1 == count ? "" : "s", page);
+
+ for (n_read = i = 0; i < count; i++) {
+ log_info("Reading page %u (%u%%)...%c", page + i,
+ (i + 1) == count ? 100 : 100 * i / count,
+ (i + 1) == count ? '\n' : '\r');
+
+ status =
+ read_page(dev, BLADE_USB_CMD_FLASH_READ, page + i, buf + n_read);
+ if (status != 0) {
+ goto error;
+ }
+
+ n_read += dev->flash_arch->psize_bytes;
+ }
+
+ log_info("Done reading %u page%s\n", count, 1 == count ? "" : "s");
+
+error:
+ status = restore_post_flash_setting(dev);
+ return status;
+}
+
+static int write_page(struct bladerf *dev, uint8_t write_operation,
+ uint16_t page, const uint8_t *buf)
+{
+ int status;
+ int32_t commit_status;
+ uint16_t offset;
+ uint16_t write_size;
+ struct bladerf_usb *usb = dev->backend_data;
+ bladerf_dev_speed usb_speed;
+
+ if (usb->fn->get_speed(usb->driver, &usb_speed) != 0) {
+ log_debug("Error getting USB speed in %s\n", __FUNCTION__);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ if (usb_speed == BLADERF_DEVICE_SPEED_SUPER) {
+ write_size = dev->flash_arch->psize_bytes;
+ } else if (usb_speed == BLADERF_DEVICE_SPEED_HIGH) {
+ write_size = 64;
+ } else {
+ assert(!"BUG - unexpected device speed");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* Write the data to the firmware's page buffer.
+ * Casting away the buffer's const-ness here is gross, but this buffer
+ * will not be written to on an out transfer. */
+ for (offset = 0; offset < dev->flash_arch->psize_bytes; offset += write_size) {
+ status = usb->fn->control_transfer(usb->driver,
+ USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ USB_DIR_HOST_TO_DEVICE,
+ BLADE_USB_CMD_WRITE_PAGE_BUFFER,
+ 0,
+ offset,
+ (uint8_t*)&buf[offset],
+ write_size,
+ CTRL_TIMEOUT_MS);
+
+ if(status < 0) {
+ log_error("Failed to write page buffer at offset 0x%02x "
+ "for page %u: %s\n",
+ offset, page, bladerf_strerror(status));
+ return status;
+ }
+ }
+
+ /* Commit the page to flash */
+ status = vendor_cmd_int_windex(dev, write_operation, page, &commit_status);
+
+ if (status != 0) {
+ log_error("Failed to commit page %u: %s\n", page,
+ bladerf_strerror(status));
+ return status;
+
+ } else if (commit_status != 0) {
+ log_error("Failed to commit page %u, FW returned %d\n", page,
+ commit_status);
+
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ return 0;
+}
+
+static int usb_write_flash_pages(struct bladerf *dev,
+ const uint8_t *buf,
+ uint32_t page_u32,
+ uint32_t count_u32)
+
+{
+ int status, restore_status;
+ uint16_t i;
+ size_t n_written;
+
+ /* 16-bit control transfer fields are used for these.
+ * The current bladeRF build only has a 4MiB flash, anyway. */
+ const uint16_t page = (uint16_t)page_u32;
+ const uint16_t count = (uint16_t)count_u32;
+
+ assert(page == page_u32);
+ assert(count == count_u32);
+
+ status = change_setting(dev, USB_IF_SPI_FLASH);
+ if (status != 0) {
+ return status;
+ }
+
+ log_info("Writing %u page%s starting at page %u\n", count,
+ 1 == count ? "" : "s", page);
+
+ n_written = 0;
+ for (i = 0; i < count; i++) {
+ log_info("Writing page %u (%u%%)...%c", page + i,
+ (i + 1) == count ? 100 : 100 * i / count,
+ (i + 1) == count ? '\n' : '\r');
+
+ status = write_page(dev, BLADE_USB_CMD_FLASH_WRITE, page + i, buf + n_written);
+ if (status) {
+ goto error;
+ }
+
+ n_written += dev->flash_arch->psize_bytes;
+ }
+ log_info("Done writing %u page%s\n", count, 1 == count ? "" : "s");
+
+error:
+ restore_status = restore_post_flash_setting(dev);
+ if (status != 0) {
+ return status;
+ } else if (restore_status != 0) {
+ return restore_status;
+ } else {
+ return 0;
+ }
+}
+
+static int usb_device_reset(struct bladerf *dev)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+
+ return usb->fn->control_transfer(usb->driver, USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ USB_DIR_HOST_TO_DEVICE,
+ BLADE_USB_CMD_RESET,
+ 0, 0, 0, 0, CTRL_TIMEOUT_MS);
+
+}
+
+static int usb_jump_to_bootloader(struct bladerf *dev)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+
+ return usb->fn->control_transfer(usb->driver, USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ USB_DIR_HOST_TO_DEVICE,
+ BLADE_USB_CMD_JUMP_TO_BOOTLOADER,
+ 0, 0, 0, 0, CTRL_TIMEOUT_MS);
+}
+
+static int usb_get_cal(struct bladerf *dev, char *cal)
+{
+ const uint16_t dummy_page = 0;
+ int status, restore_status;
+
+ assert(CAL_BUFFER_SIZE == dev->flash_arch->psize_bytes);
+
+ status = change_setting(dev, USB_IF_SPI_FLASH);
+ if (status) {
+ return status;
+ }
+
+ status = read_page(dev, BLADE_USB_CMD_READ_CAL_CACHE,
+ dummy_page, (uint8_t*)cal);
+
+ restore_status = restore_post_flash_setting(dev);
+ return status == 0 ? restore_status : status;
+}
+
+static int usb_get_otp(struct bladerf *dev, char *otp)
+{
+ int status, restore_status;
+ const uint16_t dummy_page = 0;
+
+ status = change_setting(dev, USB_IF_SPI_FLASH);
+ if (status) {
+ return status;
+ }
+
+ status = read_page(dev, BLADE_USB_CMD_READ_OTP, dummy_page, (uint8_t*)otp);
+ restore_status = restore_post_flash_setting(dev);
+ return status == 0 ? restore_status : status;
+}
+
+static int usb_write_otp(struct bladerf *dev, char *otp)
+{
+ int status, restore_status;
+ const uint16_t dummy_page = 0;
+
+ status = change_setting(dev, USB_IF_SPI_FLASH);
+ if (status) {
+ return status;
+ }
+
+ status = write_page(dev, BLADE_USB_CMD_WRITE_OTP, dummy_page, (uint8_t*)otp);
+ restore_status = restore_post_flash_setting(dev);
+ return status == 0 ? restore_status : status;
+}
+
+static int usb_lock_otp(struct bladerf *dev)
+{
+ int status, restore_status, commit_status;
+
+ status = change_setting(dev, USB_IF_SPI_FLASH);
+ if (status) {
+ return status;
+ }
+
+ status = vendor_cmd_int_windex(dev, BLADE_USB_CMD_LOCK_OTP,
+ 0, &commit_status);
+
+ if (commit_status != 0) {
+ log_error("Failed to lock OTP, FW returned %d\n", commit_status);
+ if (status == 0)
+ status = commit_status;
+ }
+
+ restore_status = restore_post_flash_setting(dev);
+ return status == 0 ? restore_status : status;
+}
+
+static int usb_get_device_speed(struct bladerf *dev, bladerf_dev_speed *speed)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+
+ return usb->fn->get_speed(usb->driver, speed);
+}
+
+static int usb_set_firmware_loopback(struct bladerf *dev, bool enable) {
+ int result;
+ int status;
+
+ status = vendor_cmd_int_wvalue(dev, BLADE_USB_CMD_SET_LOOPBACK,
+ enable, &result);
+ if (status != 0) {
+ return status;
+ }
+
+
+ status = change_setting(dev, USB_IF_NULL);
+ if (status == 0) {
+ status = change_setting(dev, USB_IF_RF_LINK);
+ }
+
+ return status;
+}
+
+static int usb_get_firmware_loopback(struct bladerf *dev, bool *is_enabled)
+{
+ int status, result;
+
+ status = vendor_cmd_int(dev, BLADE_USB_CMD_GET_LOOPBACK,
+ USB_DIR_DEVICE_TO_HOST, &result);
+
+ if (status == 0) {
+ *is_enabled = (result != 0);
+ }
+
+ return status;
+}
+
+static int usb_enable_module(struct bladerf *dev, bladerf_direction dir, bool enable)
+{
+ int status;
+ int32_t fx3_ret = -1;
+ const uint16_t val = enable ? 1 : 0;
+ const uint8_t cmd = (dir == BLADERF_RX) ?
+ BLADE_USB_CMD_RF_RX : BLADE_USB_CMD_RF_TX;
+
+ status = vendor_cmd_int_wvalue(dev, cmd, val, &fx3_ret);
+ if (status != 0) {
+ log_debug("Could not enable RF %s (%d): %s\n",
+ (dir == BLADERF_RX) ? "RX" : "TX",
+ status, bladerf_strerror(status));
+
+ } else if (fx3_ret != 0) {
+ log_warning("FX3 reported error=0x%x when %s RF %s\n",
+ fx3_ret,
+ enable ? "enabling" : "disabling",
+ (dir == BLADERF_RX) ? "RX" : "TX");
+
+ /* FIXME: Work around what seems to be a harmless failure.
+ * It appears that in firmware or in the lib, we may be
+ * attempting to disable an already disabled channel, or
+ * enabling an already enabled channel.
+ *
+ * Further investigation required
+ *
+ * 0x44 corresponds to CY_U3P_ERROR_ALREADY_STARTED
+ */
+ if (fx3_ret != 0x44) {
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+ }
+
+ return status;
+}
+
+static int usb_init_stream(struct bladerf_stream *stream, size_t num_transfers)
+{
+ struct bladerf_usb *usb = stream->dev->backend_data;
+ return usb->fn->init_stream(usb->driver, stream, num_transfers);
+}
+
+static int usb_stream(struct bladerf_stream *stream, bladerf_channel_layout layout)
+{
+ struct bladerf_usb *usb = stream->dev->backend_data;
+ return usb->fn->stream(usb->driver, stream, layout);
+}
+
+int usb_submit_stream_buffer(struct bladerf_stream *stream, void *buffer,
+ size_t *length, unsigned int timeout_ms, bool nonblock)
+{
+ struct bladerf_usb *usb = stream->dev->backend_data;
+ return usb->fn->submit_stream_buffer(usb->driver, stream, buffer,
+ length, timeout_ms, nonblock);
+}
+
+static void usb_deinit_stream(struct bladerf_stream *stream)
+{
+ struct bladerf_usb *usb = stream->dev->backend_data;
+ usb->fn->deinit_stream(usb->driver, stream);
+}
+
+/*
+ * Information about the boot image format and boot over USB can be found in
+ * Cypress AN76405: EZ-USB (R) FX3 (TM) Boot Options:
+ * http://www.cypress.com/?docID=49862
+ *
+ * There's a request (bRequset = 0xc0) for the bootloader revision.
+ * However, there doesn't appear to be any documented reason to check this and
+ * behave differently depending upon the returned value.
+ */
+
+/* Command fields for FX3 firmware upload vendor requests */
+#define FX3_BOOTLOADER_LOAD_BREQUEST 0xa0
+#define FX3_BOOTLOADER_ADDR_WVALUE(addr) (HOST_TO_LE16(addr & 0xffff))
+#define FX3_BOOTLOADER_ADDR_WINDEX(addr) (HOST_TO_LE16(((addr >> 16) & 0xffff)))
+#define FX3_BOOTLOADER_MAX_LOAD_LEN 4096
+
+static int write_and_verify_fw_chunk(struct bladerf_usb *usb, uint32_t addr,
+ uint8_t *data, uint32_t len,
+ uint8_t *readback_buf) {
+
+ int status;
+ log_verbose("Writing %u bytes to bootloader @ 0x%08x\n", len, addr);
+ status = usb->fn->control_transfer(usb->driver,
+ USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ USB_DIR_HOST_TO_DEVICE,
+ FX3_BOOTLOADER_LOAD_BREQUEST,
+ FX3_BOOTLOADER_ADDR_WVALUE(addr),
+ FX3_BOOTLOADER_ADDR_WINDEX(addr),
+ data,
+ len,
+ CTRL_TIMEOUT_MS);
+
+ if (status != 0) {
+ log_debug("Failed to write FW chunk (%d)\n", status);
+ return status;
+ }
+
+ log_verbose("Reading back %u bytes from bootloader @ 0x%08x\n", len, addr);
+ status = usb->fn->control_transfer(usb->driver,
+ USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ USB_DIR_DEVICE_TO_HOST,
+ FX3_BOOTLOADER_LOAD_BREQUEST,
+ FX3_BOOTLOADER_ADDR_WVALUE(addr),
+ FX3_BOOTLOADER_ADDR_WINDEX(addr),
+ readback_buf,
+ len,
+ CTRL_TIMEOUT_MS);
+
+ if (status != 0) {
+ log_debug("Failed to read back FW chunk (%d)\n", status);
+ return status;
+ }
+
+ if (memcmp(data, readback_buf, len) != 0) {
+ log_debug("Readback did match written data.\n");
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ return status;
+}
+
+static int execute_fw_from_bootloader(struct bladerf_usb *usb, uint32_t addr)
+{
+ int status;
+
+ status = usb->fn->control_transfer(usb->driver,
+ USB_TARGET_DEVICE,
+ USB_REQUEST_VENDOR,
+ USB_DIR_HOST_TO_DEVICE,
+ FX3_BOOTLOADER_LOAD_BREQUEST,
+ FX3_BOOTLOADER_ADDR_WVALUE(addr),
+ FX3_BOOTLOADER_ADDR_WINDEX(addr),
+ NULL,
+ 0,
+ CTRL_TIMEOUT_MS);
+
+ if (status != 0 && status != BLADERF_ERR_IO) {
+ log_debug("Failed to exec firmware: %s\n:",
+ bladerf_strerror(status));
+
+ } else if (status == BLADERF_ERR_IO) {
+ /* The device might drop out from underneath us as it starts executing
+ * the new firmware */
+ log_verbose("Device returned IO error due to FW boot.\n");
+ status = 0;
+ } else {
+ log_verbose("Booting new FW.\n");
+ }
+
+ return status;
+}
+
+static int write_fw_to_bootloader(void *driver, struct fx3_firmware *fw)
+{
+ int status = 0;
+ uint32_t to_write;
+ uint32_t data_len;
+ uint32_t addr;
+ uint8_t *data;
+ bool got_section;
+
+ uint8_t *readback = malloc(FX3_BOOTLOADER_MAX_LOAD_LEN);
+ if (readback == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ do {
+ got_section = fx3_fw_next_section(fw, &addr, &data, &data_len);
+ if (got_section) {
+ /* data_len should never be zero, as fw->num_sections should NOT
+ * include the terminating section in its count */
+ assert(data_len != 0);
+
+ do {
+ to_write = u32_min(data_len, FX3_BOOTLOADER_MAX_LOAD_LEN);
+
+ status = write_and_verify_fw_chunk(driver,
+ addr, data, to_write,
+ readback);
+
+ data_len -= to_write;
+ addr += to_write;
+ data += to_write;
+ } while (data_len != 0 && status == 0);
+ }
+ } while (got_section && status == 0);
+
+ if (status == 0) {
+ status = execute_fw_from_bootloader(driver, fx3_fw_entry_point(fw));
+ }
+
+ free(readback);
+ return status;
+}
+
+static int usb_load_fw_from_bootloader(bladerf_backend backend,
+ uint8_t bus, uint8_t addr,
+ struct fx3_firmware *fw)
+{
+ int status = 0;
+ size_t i;
+ struct bladerf_usb usb;
+
+ for (i = 0; i < ARRAY_SIZE(usb_driver_list); i++) {
+
+ if ((backend == BLADERF_BACKEND_ANY) ||
+ (usb_driver_list[i]->id == backend)) {
+
+ usb.fn = usb_driver_list[i]->fn;
+ status = usb.fn->open_bootloader(&usb.driver, bus, addr);
+ if (status == 0) {
+ status = write_fw_to_bootloader(&usb, fw);
+ usb.fn->close_bootloader(usb.driver);
+ break;
+ }
+ }
+ }
+
+ return status;
+}
+
+/* Default handlers for operations unsupported by the NIOS II legacy packet
+ * format */
+static int set_vctcxo_tamer_mode_unsupported(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode mode)
+{
+ log_debug("Operation not supported with legacy NIOS packet format.\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int get_vctcxo_tamer_mode_unsupported(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode *mode)
+{
+ *mode = BLADERF_VCTCXO_TAMER_INVALID;
+ log_debug("Operation not supported with legacy NIOS packet format.\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int usb_read_fw_log(struct bladerf *dev, logger_entry *e)
+{
+ int status;
+ *e = LOG_EOF;
+
+ status = vendor_cmd_int(dev, BLADE_USB_CMD_READ_LOG_ENTRY,
+ USB_DIR_DEVICE_TO_HOST, (int32_t*) e);
+
+ return status;
+}
+
+static int config_gpio_write(struct bladerf *dev, uint32_t val)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+ bladerf_dev_speed usb_speed;
+
+ if (usb->fn->get_speed(usb->driver, &usb_speed) != 0) {
+ log_debug("Error getting USB speed in %s\n", __FUNCTION__);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* If we're connected at HS, we need to use smaller DMA transfers */
+ if (usb_speed == BLADERF_DEVICE_SPEED_HIGH) {
+ val |= BLADERF_GPIO_FEATURE_SMALL_DMA_XFER;
+ } else if (usb_speed == BLADERF_DEVICE_SPEED_SUPER) {
+ val &= ~BLADERF_GPIO_FEATURE_SMALL_DMA_XFER;
+ } else {
+ assert(!"Encountered unknown USB speed");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ return nios_config_write(dev, val);
+}
+
+static int set_agc_dc_correction_unsupported(struct bladerf *dev,
+ int16_t q_max, int16_t i_max,
+ int16_t q_mid, int16_t i_mid,
+ int16_t q_low, int16_t i_low)
+{
+ log_debug("Operation not supported with legacy NIOS packet format.\n");
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int legacy_config_gpio_write(struct bladerf *dev, uint32_t val)
+{
+ struct bladerf_usb *usb = dev->backend_data;
+ bladerf_dev_speed usb_speed;
+
+ if (usb->fn->get_speed(usb->driver, &usb_speed) != 0) {
+ log_debug("Error getting USB speed in %s\n", __FUNCTION__);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* If we're connected at HS, we need to use smaller DMA transfers */
+ if (usb_speed == BLADERF_DEVICE_SPEED_HIGH) {
+ val |= BLADERF_GPIO_FEATURE_SMALL_DMA_XFER;
+ } else if (usb_speed == BLADERF_DEVICE_SPEED_SUPER) {
+ val &= ~BLADERF_GPIO_FEATURE_SMALL_DMA_XFER;
+ } else {
+ assert(!"Encountered unknown USB speed");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ return nios_legacy_config_write(dev, val);
+}
+
+/* USB backend that used legacy format for communicating with NIOS II */
+const struct backend_fns backend_fns_usb_legacy = {
+ FIELD_INIT(.matches, usb_matches),
+
+ FIELD_INIT(.probe, usb_probe),
+
+ FIELD_INIT(.get_vid_pid, usb_get_vid_pid),
+ FIELD_INIT(.get_flash_id, usb_get_flash_id),
+ FIELD_INIT(.open, usb_open),
+ FIELD_INIT(.set_fpga_protocol, usb_set_fpga_protocol),
+ FIELD_INIT(.close, usb_close),
+
+ FIELD_INIT(.is_fw_ready, usb_is_fw_ready),
+
+ FIELD_INIT(.get_handle, usb_get_handle),
+
+ FIELD_INIT(.load_fpga, usb_load_fpga),
+ FIELD_INIT(.is_fpga_configured, usb_is_fpga_configured),
+ FIELD_INIT(.get_fpga_source, usb_get_fpga_source),
+
+ FIELD_INIT(.get_fw_version, usb_get_fw_version),
+ FIELD_INIT(.get_fpga_version, usb_get_fpga_version),
+
+ FIELD_INIT(.erase_flash_blocks, usb_erase_flash_blocks),
+ FIELD_INIT(.read_flash_pages, usb_read_flash_pages),
+ FIELD_INIT(.write_flash_pages, usb_write_flash_pages),
+
+ FIELD_INIT(.device_reset, usb_device_reset),
+ FIELD_INIT(.jump_to_bootloader, usb_jump_to_bootloader),
+
+ FIELD_INIT(.get_cal, usb_get_cal),
+ FIELD_INIT(.get_otp, usb_get_otp),
+ FIELD_INIT(.write_otp, usb_write_otp),
+ FIELD_INIT(.lock_otp, usb_lock_otp),
+ FIELD_INIT(.get_device_speed, usb_get_device_speed),
+
+ FIELD_INIT(.config_gpio_write, legacy_config_gpio_write),
+ FIELD_INIT(.config_gpio_read, nios_legacy_config_read),
+
+ FIELD_INIT(.expansion_gpio_write, nios_legacy_expansion_gpio_write),
+ FIELD_INIT(.expansion_gpio_read, nios_legacy_expansion_gpio_read),
+ FIELD_INIT(.expansion_gpio_dir_write, nios_legacy_expansion_gpio_dir_write),
+ FIELD_INIT(.expansion_gpio_dir_read, nios_legacy_expansion_gpio_dir_read),
+
+ FIELD_INIT(.set_iq_gain_correction, nios_legacy_set_iq_gain_correction),
+ FIELD_INIT(.set_iq_phase_correction, nios_legacy_set_iq_phase_correction),
+ FIELD_INIT(.get_iq_gain_correction, nios_legacy_get_iq_gain_correction),
+ FIELD_INIT(.get_iq_phase_correction, nios_legacy_get_iq_phase_correction),
+
+ FIELD_INIT(.set_agc_dc_correction, set_agc_dc_correction_unsupported),
+
+ FIELD_INIT(.get_timestamp, nios_legacy_get_timestamp),
+
+ FIELD_INIT(.si5338_write, nios_legacy_si5338_write),
+ FIELD_INIT(.si5338_read, nios_legacy_si5338_read),
+
+ FIELD_INIT(.lms_write, nios_legacy_lms6_write),
+ FIELD_INIT(.lms_read, nios_legacy_lms6_read),
+
+ FIELD_INIT(.ina219_write, nios_legacy_ina219_write),
+ FIELD_INIT(.ina219_read, nios_legacy_ina219_read),
+
+ FIELD_INIT(.ad9361_spi_write, nios_legacy_ad9361_spi_write),
+ FIELD_INIT(.ad9361_spi_read, nios_legacy_ad9361_spi_read),
+
+ FIELD_INIT(.adi_axi_write, nios_legacy_adi_axi_write),
+ FIELD_INIT(.adi_axi_read, nios_legacy_adi_axi_read),
+
+ FIELD_INIT(.rfic_command_write, nios_legacy_rfic_command_write),
+ FIELD_INIT(.rfic_command_read, nios_legacy_rfic_command_read),
+
+ FIELD_INIT(.rffe_control_write, nios_legacy_rffe_control_write),
+ FIELD_INIT(.rffe_control_read, nios_legacy_rffe_control_read),
+
+ FIELD_INIT(.rffe_fastlock_save, nios_legacy_rffe_fastlock_save),
+
+ FIELD_INIT(.ad56x1_vctcxo_trim_dac_write, nios_legacy_ad56x1_vctcxo_trim_dac_write),
+ FIELD_INIT(.ad56x1_vctcxo_trim_dac_read, nios_legacy_ad56x1_vctcxo_trim_dac_read),
+
+ FIELD_INIT(.adf400x_write, nios_legacy_adf400x_write),
+ FIELD_INIT(.adf400x_read, nios_legacy_adf400x_read),
+
+ FIELD_INIT(.vctcxo_dac_write, nios_legacy_vctcxo_trim_dac_write),
+ FIELD_INIT(.vctcxo_dac_read, nios_vctcxo_trim_dac_read),
+
+ FIELD_INIT(.set_vctcxo_tamer_mode, set_vctcxo_tamer_mode_unsupported),
+ FIELD_INIT(.get_vctcxo_tamer_mode, get_vctcxo_tamer_mode_unsupported),
+
+ FIELD_INIT(.xb_spi, nios_legacy_xb200_synth_write),
+
+ FIELD_INIT(.set_firmware_loopback, usb_set_firmware_loopback),
+ FIELD_INIT(.get_firmware_loopback, usb_get_firmware_loopback),
+
+ FIELD_INIT(.enable_module, usb_enable_module),
+
+ FIELD_INIT(.init_stream, usb_init_stream),
+ FIELD_INIT(.stream, usb_stream),
+ FIELD_INIT(.submit_stream_buffer, usb_submit_stream_buffer),
+ FIELD_INIT(.deinit_stream, usb_deinit_stream),
+
+ FIELD_INIT(.retune, nios_retune),
+ FIELD_INIT(.retune2, nios_retune2),
+
+ FIELD_INIT(.load_fw_from_bootloader, usb_load_fw_from_bootloader),
+
+ FIELD_INIT(.read_fw_log, usb_read_fw_log),
+
+ FIELD_INIT(.read_trigger, nios_legacy_read_trigger),
+ FIELD_INIT(.write_trigger, nios_legacy_write_trigger),
+
+ FIELD_INIT(.name, "usb"),
+};
+
+/* USB backend for use with FPGA supporting update NIOS II packet formats */
+const struct backend_fns backend_fns_usb = {
+ FIELD_INIT(.matches, usb_matches),
+
+ FIELD_INIT(.probe, usb_probe),
+
+ FIELD_INIT(.get_vid_pid, usb_get_vid_pid),
+ FIELD_INIT(.get_flash_id, usb_get_flash_id),
+ FIELD_INIT(.open, usb_open),
+ FIELD_INIT(.set_fpga_protocol, usb_set_fpga_protocol),
+ FIELD_INIT(.close, usb_close),
+
+ FIELD_INIT(.is_fw_ready, usb_is_fw_ready),
+
+ FIELD_INIT(.get_handle, usb_get_handle),
+
+ FIELD_INIT(.load_fpga, usb_load_fpga),
+ FIELD_INIT(.is_fpga_configured, usb_is_fpga_configured),
+ FIELD_INIT(.get_fpga_source, usb_get_fpga_source),
+
+ FIELD_INIT(.get_fw_version, usb_get_fw_version),
+ FIELD_INIT(.get_fpga_version, usb_get_fpga_version),
+
+ FIELD_INIT(.erase_flash_blocks, usb_erase_flash_blocks),
+ FIELD_INIT(.read_flash_pages, usb_read_flash_pages),
+ FIELD_INIT(.write_flash_pages, usb_write_flash_pages),
+
+ FIELD_INIT(.device_reset, usb_device_reset),
+ FIELD_INIT(.jump_to_bootloader, usb_jump_to_bootloader),
+
+ FIELD_INIT(.get_cal, usb_get_cal),
+ FIELD_INIT(.get_otp, usb_get_otp),
+ FIELD_INIT(.write_otp, usb_write_otp),
+ FIELD_INIT(.lock_otp, usb_lock_otp),
+ FIELD_INIT(.get_device_speed, usb_get_device_speed),
+
+ FIELD_INIT(.config_gpio_write, config_gpio_write),
+ FIELD_INIT(.config_gpio_read, nios_config_read),
+
+ FIELD_INIT(.expansion_gpio_write, nios_expansion_gpio_write),
+ FIELD_INIT(.expansion_gpio_read, nios_expansion_gpio_read),
+ FIELD_INIT(.expansion_gpio_dir_write, nios_expansion_gpio_dir_write),
+ FIELD_INIT(.expansion_gpio_dir_read, nios_expansion_gpio_dir_read),
+
+ FIELD_INIT(.set_iq_gain_correction, nios_set_iq_gain_correction),
+ FIELD_INIT(.set_iq_phase_correction, nios_set_iq_phase_correction),
+ FIELD_INIT(.get_iq_gain_correction, nios_get_iq_gain_correction),
+ FIELD_INIT(.get_iq_phase_correction, nios_get_iq_phase_correction),
+
+ FIELD_INIT(.set_agc_dc_correction, nios_set_agc_dc_correction),
+
+ FIELD_INIT(.get_timestamp, nios_get_timestamp),
+
+ FIELD_INIT(.si5338_write, nios_si5338_write),
+ FIELD_INIT(.si5338_read, nios_si5338_read),
+
+ FIELD_INIT(.lms_write, nios_lms6_write),
+ FIELD_INIT(.lms_read, nios_lms6_read),
+
+ FIELD_INIT(.ina219_write, nios_ina219_write),
+ FIELD_INIT(.ina219_read, nios_ina219_read),
+
+ FIELD_INIT(.ad9361_spi_write, nios_ad9361_spi_write),
+ FIELD_INIT(.ad9361_spi_read, nios_ad9361_spi_read),
+
+ FIELD_INIT(.adi_axi_write, nios_adi_axi_write),
+ FIELD_INIT(.adi_axi_read, nios_adi_axi_read),
+
+ FIELD_INIT(.wishbone_master_write, nios_wishbone_master_write),
+ FIELD_INIT(.wishbone_master_read, nios_wishbone_master_read),
+
+ FIELD_INIT(.rfic_command_write, nios_rfic_command_write),
+ FIELD_INIT(.rfic_command_read, nios_rfic_command_read),
+
+ FIELD_INIT(.rffe_control_write, nios_rffe_control_write),
+ FIELD_INIT(.rffe_control_read, nios_rffe_control_read),
+
+ FIELD_INIT(.rffe_fastlock_save, nios_rffe_fastlock_save),
+
+ FIELD_INIT(.ad56x1_vctcxo_trim_dac_write, nios_ad56x1_vctcxo_trim_dac_write),
+ FIELD_INIT(.ad56x1_vctcxo_trim_dac_read, nios_ad56x1_vctcxo_trim_dac_read),
+
+ FIELD_INIT(.adf400x_write, nios_adf400x_write),
+ FIELD_INIT(.adf400x_read, nios_adf400x_read),
+
+ FIELD_INIT(.vctcxo_dac_write, nios_vctcxo_trim_dac_write),
+ FIELD_INIT(.vctcxo_dac_read, nios_vctcxo_trim_dac_read),
+
+ FIELD_INIT(.set_vctcxo_tamer_mode, nios_set_vctcxo_tamer_mode),
+ FIELD_INIT(.get_vctcxo_tamer_mode, nios_get_vctcxo_tamer_mode),
+
+ FIELD_INIT(.xb_spi, nios_xb200_synth_write),
+
+ FIELD_INIT(.set_firmware_loopback, usb_set_firmware_loopback),
+ FIELD_INIT(.get_firmware_loopback, usb_get_firmware_loopback),
+
+ FIELD_INIT(.enable_module, usb_enable_module),
+
+ FIELD_INIT(.init_stream, usb_init_stream),
+ FIELD_INIT(.stream, usb_stream),
+ FIELD_INIT(.submit_stream_buffer, usb_submit_stream_buffer),
+ FIELD_INIT(.deinit_stream, usb_deinit_stream),
+
+ FIELD_INIT(.retune, nios_retune),
+ FIELD_INIT(.retune2, nios_retune2),
+
+ FIELD_INIT(.load_fw_from_bootloader, usb_load_fw_from_bootloader),
+
+ FIELD_INIT(.read_fw_log, usb_read_fw_log),
+
+ FIELD_INIT(.read_trigger, nios_read_trigger),
+ FIELD_INIT(.write_trigger, nios_write_trigger),
+
+ FIELD_INIT(.name, "usb"),
+};
diff --git a/Radio/HW/BladeRF/src/backend/usb/usb.h b/Radio/HW/BladeRF/src/backend/usb/usb.h
new file mode 100644
index 0000000..644ed7a
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/usb/usb.h
@@ -0,0 +1,168 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014-2015 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef BACKEND_USB_H_
+#define BACKEND_USB_H_
+
+#include "host_config.h"
+
+#include "board/board.h"
+
+#if ENABLE_USB_DEV_RESET_ON_OPEN
+extern bool bladerf_usb_reset_device_on_open;
+#endif
+
+#ifndef SAMPLE_EP_IN
+#define SAMPLE_EP_IN 0x81
+#endif
+
+#ifndef SAMPLE_EP_OUT
+#define SAMPLE_EP_OUT 0x01
+#endif
+
+#ifndef PERIPHERAL_EP_IN
+#define PERIPHERAL_EP_IN 0x82
+#endif
+
+#ifndef PERIPHERAL_EP_OUT
+#define PERIPHERAL_EP_OUT 0x02
+#endif
+
+#ifndef PERIPHERAL_TIMEOUT_MS
+#define PERIPHERAL_TIMEOUT_MS 250
+#endif
+
+/* Be careful when lowering this value. The control request for flash erase
+ * operations take some time */
+#ifndef CTRL_TIMEOUT_MS
+#define CTRL_TIMEOUT_MS 1000
+#endif
+
+#ifndef BULK_TIMEOUT_MS
+#define BULK_TIMEOUT_MS 1000
+#endif
+
+/* Size of a host<->FPGA message in BYTES */
+#define USB_MSG_SIZE_SS 2048
+#define USB_MSG_SIZE_HS 1024
+
+typedef enum {
+ USB_TARGET_DEVICE,
+ USB_TARGET_INTERFACE,
+ USB_TARGET_ENDPOINT,
+ USB_TARGET_OTHER
+} usb_target;
+
+typedef enum {
+ USB_REQUEST_STANDARD,
+ USB_REQUEST_CLASS,
+ USB_REQUEST_VENDOR
+} usb_request;
+
+typedef enum {
+ USB_DIR_HOST_TO_DEVICE = 0x00,
+ USB_DIR_DEVICE_TO_HOST = 0x80
+} usb_direction;
+
+/**
+ * USB backend driver function table
+ *
+ * All return values are expected to be 0 on success, or a BLADERF_ERR_*
+ * value on failure
+ */
+struct usb_fns {
+ int (*probe)(backend_probe_target probe_target,
+ struct bladerf_devinfo_list *info_list);
+
+ /* Populates the `driver` pointer with a handle for the specific USB driver.
+ * `info_in` describes the device to open, and may contain wildcards.
+ * On success, the driver should fill in `info_out` with the complete
+ * details of the device. */
+ int (*open)(void **driver,
+ struct bladerf_devinfo *info_in,
+ struct bladerf_devinfo *info_out);
+
+ void (*close)(void *driver);
+
+ int (*get_vid_pid)(void *driver, uint16_t *vid, uint16_t *pid);
+
+ int (*get_flash_id)(void *driver, uint8_t *mid, uint8_t *did);
+
+ int (*get_handle)(void *driver, void **handle);
+
+ int (*get_speed)(void *driver, bladerf_dev_speed *speed);
+
+ int (*change_setting)(void *driver, uint8_t setting);
+
+ int (*control_transfer)(void *driver,
+ usb_target target_type,
+ usb_request req_type,
+ usb_direction direction,
+ uint8_t request,
+ uint16_t wvalue,
+ uint16_t windex,
+ void *buffer,
+ uint32_t buffer_len,
+ uint32_t timeout_ms);
+
+ int (*bulk_transfer)(void *driver,
+ uint8_t endpoint,
+ void *buffer,
+ uint32_t buffer_len,
+ uint32_t timeout_ms);
+
+ int (*get_string_descriptor)(void *driver,
+ uint8_t index,
+ void *buffer,
+ uint32_t buffer_len);
+
+ int (*init_stream)(void *driver,
+ struct bladerf_stream *stream,
+ size_t num_transfers);
+
+ int (*stream)(void *driver,
+ struct bladerf_stream *stream,
+ bladerf_channel_layout layout);
+
+ int (*submit_stream_buffer)(void *driver,
+ struct bladerf_stream *stream,
+ void *buffer,
+ size_t *length,
+ unsigned int timeout_ms,
+ bool nonblock);
+
+ int (*deinit_stream)(void *driver, struct bladerf_stream *stream);
+
+ int (*open_bootloader)(void **driver, uint8_t bus, uint8_t addr);
+ void (*close_bootloader)(void *driver);
+};
+
+struct usb_driver {
+ const struct usb_fns *fn;
+ bladerf_backend id;
+};
+
+struct bladerf_usb {
+ const struct usb_fns *fn;
+ void *driver;
+};
+
+#endif
diff --git a/Radio/HW/BladeRF/src/bladerf.c b/Radio/HW/BladeRF/src/bladerf.c
new file mode 100644
index 0000000..f19c3a6
--- /dev/null
+++ b/Radio/HW/BladeRF/src/bladerf.c
@@ -0,0 +1,2387 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013-2016 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libbladeRF.h>
+
+#include "log.h"
+#include "rel_assert.h"
+#define LOGGER_ID_STRING
+#include "logger_entry.h"
+#include "logger_id.h"
+
+#include "backend/backend.h"
+#include "backend/usb/usb.h"
+#include "board/board.h"
+#include "conversions.h"
+#include "driver/fx3_fw.h"
+#include "device_calibration.h"
+#include "streaming/async.h"
+#include "version.h"
+
+#include "expansion/xb100.h"
+#include "expansion/xb200.h"
+#include "expansion/xb300.h"
+
+#include "devinfo.h"
+#include "helpers/configfile.h"
+#include "helpers/file.h"
+#include "helpers/have_cap.h"
+#include "helpers/interleave.h"
+
+#define CHECK_NULL(...) do { \
+ const void* _args[] = { __VA_ARGS__, NULL }; \
+ for (size_t _i = 0; _args[_i] != NULL; ++_i) { \
+ if (_args[_i] == NULL) { \
+ log_error("%s:%d: Argument %zu is a NULL pointer\n", __FILE__, __LINE__, _i + 1); \
+ return BLADERF_ERR_INVAL; \
+ } \
+ } \
+} while (0)
+
+#define CHECK_STATUS(fn) \
+ do { \
+ status = fn; \
+ if (status != 0) { \
+ log_error("%s: %s %s\n", __FUNCTION__, #fn, \
+ bladerf_strerror(status)); \
+ goto error; \
+ } \
+ } while (0)
+
+
+/******************************************************************************/
+/* Private Function Declarations */
+/******************************************************************************/
+
+/**
+ * Sets up the register configuration for oversample feature.
+ *
+ * @param dev Device handle
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+int bladerf_set_oversample_register_config(struct bladerf *dev);
+
+/******************************************************************************/
+/* Open / Close */
+/******************************************************************************/
+
+/* dev path becomes device specifier string (osmosdr-like) */
+int bladerf_open(struct bladerf **dev, const char *dev_id)
+{
+ struct bladerf_devinfo devinfo;
+ int status;
+
+ *dev = NULL;
+
+ /* Populate dev-info from string */
+ status = str2devinfo(dev_id, &devinfo);
+ if (!status) {
+ status = bladerf_open_with_devinfo(dev, &devinfo);
+ }
+
+ return status;
+}
+
+int bladerf_open_with_devinfo(struct bladerf **opened_device,
+ struct bladerf_devinfo *devinfo)
+{
+ struct bladerf *dev;
+ struct bladerf_devinfo any_device;
+ unsigned int i;
+ int status;
+
+ if (devinfo == NULL) {
+ bladerf_init_devinfo(&any_device);
+ devinfo = &any_device;
+ }
+
+ *opened_device = NULL;
+
+ dev = calloc(1, sizeof(struct bladerf));
+ if (dev == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Open backend */
+ status = backend_open(dev, devinfo);
+ if (status != 0) {
+ free(dev);
+ return status;
+ }
+
+ /* Find matching board */
+ for (i = 0; i < bladerf_boards_len; i++) {
+ if (bladerf_boards[i]->matches(dev)) {
+ dev->board = bladerf_boards[i];
+ break;
+ }
+ }
+ /* If no matching board was found */
+ if (i == bladerf_boards_len) {
+ dev->backend->close(dev);
+ free(dev);
+ return BLADERF_ERR_NODEV;
+ }
+
+ MUTEX_INIT(&dev->lock);
+
+ /* Open board */
+ status = dev->board->open(dev, devinfo);
+
+ if (status < 0) {
+ bladerf_close(dev);
+ return status;
+ }
+
+ /* Load configuration file */
+ status = config_load_options_file(dev);
+
+ if (status < 0) {
+ bladerf_close(dev);
+ return status;
+ }
+
+ *opened_device = dev;
+
+ return 0;
+}
+
+int bladerf_get_devinfo(struct bladerf *dev, struct bladerf_devinfo *info)
+{
+ if (dev) {
+ MUTEX_LOCK(&dev->lock);
+ memcpy(info, &dev->ident, sizeof(struct bladerf_devinfo));
+ MUTEX_UNLOCK(&dev->lock);
+ return 0;
+ } else {
+ return BLADERF_ERR_INVAL;
+ }
+}
+
+int bladerf_get_backendinfo(struct bladerf *dev, struct bladerf_backendinfo *info)
+{
+ if (dev) {
+ MUTEX_LOCK(&dev->lock);
+ info->lock_count = 1;
+ info->lock = &dev->lock;
+
+ info->handle_count = 1;
+ dev->backend->get_handle(dev, &info->handle);
+ MUTEX_UNLOCK(&dev->lock);
+ return 0;
+ } else {
+ return BLADERF_ERR_INVAL;
+ }
+}
+
+void bladerf_close(struct bladerf *dev)
+{
+ if (dev) {
+ MUTEX_LOCK(&dev->lock);
+
+ dev->board->close(dev);
+
+ if (dev->backend) {
+ dev->backend->close(dev);
+ }
+
+ /** Free gain table entries */
+ for (int i = 0; i < NUM_GAIN_CAL_TBLS; i++) {
+ gain_cal_tbl_free(&dev->gain_tbls[i]);
+ }
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ free(dev);
+ }
+}
+
+/******************************************************************************/
+/* FX3 Firmware (common to bladerf1 and bladerf2) */
+/******************************************************************************/
+
+int bladerf_jump_to_bootloader(struct bladerf *dev)
+{
+ int status;
+
+ if (!dev->backend->jump_to_bootloader) {
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->backend->jump_to_bootloader(dev);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_get_bootloader_list(struct bladerf_devinfo **devices)
+{
+ return probe(BACKEND_PROBE_FX3_BOOTLOADER, devices);
+}
+
+int bladerf_load_fw_from_bootloader(const char *device_identifier,
+ bladerf_backend backend,
+ uint8_t bus,
+ uint8_t addr,
+ const char *file)
+{
+ int status;
+ uint8_t *buf;
+ size_t buf_len;
+ struct fx3_firmware *fw = NULL;
+ struct bladerf_devinfo devinfo;
+
+ if (device_identifier == NULL) {
+ bladerf_init_devinfo(&devinfo);
+ devinfo.backend = backend;
+ devinfo.usb_bus = bus;
+ devinfo.usb_addr = addr;
+ } else {
+ status = str2devinfo(device_identifier, &devinfo);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ status = file_read_buffer(file, &buf, &buf_len);
+ if (status != 0) {
+ return status;
+ }
+
+ status = fx3_fw_parse(&fw, buf, buf_len);
+ free(buf);
+ if (status != 0) {
+ return status;
+ }
+
+ assert(fw != NULL);
+
+ status = backend_load_fw_from_bootloader(devinfo.backend, devinfo.usb_bus,
+ devinfo.usb_addr, fw);
+
+ fx3_fw_free(fw);
+
+ return status;
+}
+
+int bladerf_get_fw_log(struct bladerf *dev, const char *filename)
+{
+ int status;
+ FILE *f = NULL;
+ logger_entry e;
+
+ MUTEX_LOCK(&dev->lock);
+
+ if (!have_cap(dev->board->get_capabilities(dev),
+ BLADERF_CAP_READ_FW_LOG_ENTRY)) {
+ struct bladerf_version fw_version;
+
+ if (dev->board->get_fw_version(dev, &fw_version) == 0) {
+ log_debug("FX3 FW v%s does not support log retrieval.\n",
+ fw_version.describe);
+ }
+
+ status = BLADERF_ERR_UNSUPPORTED;
+ goto error;
+ }
+
+ if (filename != NULL) {
+ f = fopen(filename, "w");
+ if (f == NULL) {
+ switch (errno) {
+ case ENOENT:
+ status = BLADERF_ERR_NO_FILE;
+ break;
+ case EACCES:
+ status = BLADERF_ERR_PERMISSION;
+ break;
+ default:
+ status = BLADERF_ERR_IO;
+ break;
+ }
+ goto error;
+ }
+ } else {
+ f = stdout;
+ }
+
+ do {
+ status = dev->backend->read_fw_log(dev, &e);
+ if (status != 0) {
+ log_debug("Failed to read FW log: %s\n", bladerf_strerror(status));
+ goto error;
+ }
+
+ if (e == LOG_ERR) {
+ fprintf(f, "<Unexpected error>,,\n");
+ } else if (e != LOG_EOF) {
+ uint8_t file_id;
+ uint16_t line;
+ uint16_t data;
+ const char *src_file;
+
+ logger_entry_unpack(e, &file_id, &line, &data);
+ src_file = logger_id_string(file_id);
+
+ fprintf(f, "%s, %u, 0x%04x\n", src_file, line, data);
+ }
+ } while (e != LOG_EOF && e != LOG_ERR);
+
+error:
+ MUTEX_UNLOCK(&dev->lock);
+
+ if (f != NULL && f != stdout) {
+ fclose(f);
+ }
+
+ return status;
+}
+
+/******************************************************************************/
+/* Properties */
+/******************************************************************************/
+
+bladerf_dev_speed bladerf_device_speed(struct bladerf *dev)
+{
+ bladerf_dev_speed speed;
+ MUTEX_LOCK(&dev->lock);
+
+ speed = dev->board->device_speed(dev);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return speed;
+}
+
+int bladerf_get_serial(struct bladerf *dev, char *serial)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ /** TODO: add deprecation warning */
+
+ status = dev->board->get_serial(dev, serial);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_serial_struct(struct bladerf *dev,
+ struct bladerf_serial *serial)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ char serialstr[sizeof(serial->serial)];
+
+ status = dev->board->get_serial(dev, serialstr);
+
+ if (status >= 0) {
+ strncpy(serial->serial, serialstr, sizeof(serial->serial));
+ serial->serial[sizeof(serial->serial)-1] = '\0';
+ }
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_fpga_size(struct bladerf *dev, bladerf_fpga_size *size)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_fpga_size(dev, size);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_fpga_bytes(struct bladerf *dev, size_t *size)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_fpga_bytes(dev, size);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_flash_size(struct bladerf *dev, uint32_t *size, bool *is_guess)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_flash_size(dev, size, is_guess);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_is_fpga_configured(struct bladerf *dev)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->is_fpga_configured(dev);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_fpga_source(struct bladerf *dev, bladerf_fpga_source *source)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_fpga_source(dev, source);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+const char *bladerf_get_board_name(struct bladerf *dev)
+{
+ return dev->board->name;
+}
+
+size_t bladerf_get_channel_count(struct bladerf *dev, bladerf_direction dir)
+{
+ return dev->board->get_channel_count(dev, dir);
+}
+
+/******************************************************************************/
+/* Versions */
+/******************************************************************************/
+
+int bladerf_fpga_version(struct bladerf *dev, struct bladerf_version *version)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_fpga_version(dev, version);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_fw_version(struct bladerf *dev, struct bladerf_version *version)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_fw_version(dev, version);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+void bladerf_version(struct bladerf_version *version)
+{
+/* Sanity checks for version reporting mismatches */
+#ifndef LIBBLADERF_API_VERSION
+#error LIBBLADERF_API_VERSION is missing
+#endif
+#if LIBBLADERF_VERSION_MAJOR != ((LIBBLADERF_API_VERSION >> 24) & 0xff)
+#error LIBBLADERF_API_VERSION: Major version mismatch
+#endif
+#if LIBBLADERF_VERSION_MINOR != ((LIBBLADERF_API_VERSION >> 16) & 0xff)
+#error LIBBLADERF_API_VERSION: Minor version mismatch
+#endif
+#if LIBBLADERF_VERSION_PATCH != ((LIBBLADERF_API_VERSION >> 8) & 0xff)
+#error LIBBLADERF_API_VERSION: Patch version mismatch
+#endif
+ version->major = LIBBLADERF_VERSION_MAJOR;
+ version->minor = LIBBLADERF_VERSION_MINOR;
+ version->patch = LIBBLADERF_VERSION_PATCH;
+ version->describe = LIBBLADERF_VERSION;
+}
+
+/******************************************************************************/
+/* Enable/disable */
+/******************************************************************************/
+
+int bladerf_enable_module(struct bladerf *dev, bladerf_channel ch, bool enable)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->enable_module(dev, ch, enable);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Gain */
+/******************************************************************************/
+
+int bladerf_set_gain(struct bladerf *dev, bladerf_channel ch, int gain)
+{
+ int status;
+ bladerf_gain_mode gain_mode;
+ bladerf_frequency freq;
+ bladerf_gain assigned_gain = gain;
+ MUTEX_LOCK(&dev->lock);
+
+ /* Change gain mode to manual if ch = RX */
+ if (BLADERF_CHANNEL_IS_TX(ch) == false) {
+ status = dev->board->get_gain_mode(dev, ch, &gain_mode);
+ if (status != 0) {
+ log_error("Failed to get gain mode\n");
+ goto error;
+ }
+
+ if (gain_mode != BLADERF_GAIN_MGC) {
+ log_warning("Setting gain mode to manual\n");
+ status = dev->board->set_gain_mode(dev, ch, BLADERF_GAIN_MGC);
+ if (status != 0) {
+ log_error("Failed to set gain mode\n");
+ goto error;
+ }
+ }
+ }
+
+ dev->gain_tbls[ch].gain_target = gain;
+
+ if (dev->gain_tbls[ch].enabled == true) {
+ dev->board->get_frequency(dev, ch, &freq);
+ get_gain_correction(dev, freq, ch, &assigned_gain);
+ }
+
+ status = dev->board->set_gain(dev, ch, assigned_gain);
+ if (status != 0) {
+ log_error("Failed to set gain\n");
+ goto error;
+ }
+
+error:
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_gain(struct bladerf *dev, bladerf_channel ch, int *gain)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_gain(dev, ch, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_set_gain_mode(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode mode)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_gain_mode(dev, ch, mode);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_gain_mode(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode *mode)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_gain_mode(dev, ch, mode);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_gain_modes(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_gain_modes const **modes)
+{
+ return dev->board->get_gain_modes(dev, ch, modes);
+}
+
+int bladerf_get_gain_range(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_range const **range)
+{
+ return dev->board->get_gain_range(dev, ch, range);
+}
+
+int bladerf_set_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *stage,
+ bladerf_gain gain)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_gain_stage(dev, ch, stage, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *stage,
+ bladerf_gain *gain)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_gain_stage(dev, ch, stage, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_gain_stage_range(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *stage,
+ struct bladerf_range const **range)
+{
+ return dev->board->get_gain_stage_range(dev, ch, stage, range);
+}
+
+int bladerf_get_gain_stages(struct bladerf *dev,
+ bladerf_channel ch,
+ const char **stages,
+ size_t count)
+{
+ return dev->board->get_gain_stages(dev, ch, stages, count);
+}
+
+/******************************************************************************/
+/* Sample Rate */
+/******************************************************************************/
+
+int bladerf_set_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate rate,
+ bladerf_sample_rate *actual)
+{
+ int status;
+ bladerf_feature feature = dev->feature;
+
+ MUTEX_LOCK(&dev->lock);
+ status = dev->board->set_sample_rate(dev, ch, rate, actual);
+ MUTEX_UNLOCK(&dev->lock);
+
+ /*****************************************************
+ Sample rate assignments clear previous register
+ values. We must reassign oversample register config
+ for every set_samplerate().
+ *******************************************************/
+ if ((feature & BLADERF_FEATURE_OVERSAMPLE)) {
+ status = bladerf_set_oversample_register_config(dev);
+ if (status != 0) {
+ log_error("Oversample register config failure\n");
+ }
+ }
+
+ return status;
+}
+
+int bladerf_get_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate *rate)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_sample_rate(dev, ch, rate);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_sample_rate_range(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range)
+{
+ return dev->board->get_sample_rate_range(dev, ch, range);
+}
+
+int bladerf_set_rational_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_rational_rate *rate,
+ struct bladerf_rational_rate *actual)
+{
+ int status;
+ bladerf_feature feature = dev->feature;
+
+ MUTEX_LOCK(&dev->lock);
+ status = dev->board->set_rational_sample_rate(dev, ch, rate, actual);
+ MUTEX_UNLOCK(&dev->lock);
+
+ /*****************************************************
+ Register config for OVERSAMPLE operation
+
+ Sample rate assignments clear previous register
+ values. We must reassign for every set_samplerate().
+
+ Note: bladerf_set_rfic_register is mutex locked. Must
+ be placed outside of a mutex lock like above.
+ *******************************************************/
+ if ((feature & BLADERF_FEATURE_OVERSAMPLE)) {
+ status = bladerf_set_oversample_register_config(dev);
+ if (status != 0) {
+ log_error("Oversample register config failure\n");
+ }
+ }
+
+ return status;
+}
+
+int bladerf_get_rational_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_rational_rate *rate)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_rational_sample_rate(dev, ch, rate);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Bandwidth */
+/******************************************************************************/
+
+int bladerf_set_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth bandwidth,
+ bladerf_bandwidth *actual)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_bandwidth(dev, ch, bandwidth, actual);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth *bandwidth)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_bandwidth(dev, ch, bandwidth);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_bandwidth_range(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range)
+{
+ return dev->board->get_bandwidth_range(dev, ch, range);
+}
+
+/******************************************************************************/
+/* Frequency */
+/******************************************************************************/
+
+int bladerf_set_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_frequency(dev, ch, frequency);
+
+ if (dev->gain_tbls[ch].enabled && status == 0) {
+ status = apply_gain_correction(dev, ch, frequency);
+ if (status != 0) {
+ log_error("Failed to set gain correction\n");
+ }
+ }
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency *frequency)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_frequency(dev, ch, frequency);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_frequency_range(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range)
+{
+ return dev->board->get_frequency_range(dev, ch, range);
+}
+
+int bladerf_select_band(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->select_band(dev, ch, frequency);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* RF Ports*/
+/******************************************************************************/
+
+int bladerf_set_rf_port(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *port)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_rf_port(dev, ch, port);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_rf_port(struct bladerf *dev,
+ bladerf_channel ch,
+ const char **port)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_rf_port(dev, ch, port);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_rf_ports(struct bladerf *dev,
+ bladerf_channel ch,
+ const char **ports,
+ unsigned int count)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_rf_ports(dev, ch, ports, count);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Scheduled Tuning */
+/******************************************************************************/
+
+int bladerf_get_quick_tune(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_quick_tune *quick_tune)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_quick_tune(dev, ch, quick_tune);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_print_quick_tune(struct bladerf *dev, const struct bladerf_quick_tune *qt) {
+ if (dev == NULL || qt == NULL) {
+ log_error("Device handle or quick tune structure is NULL.\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ const char *board_name = bladerf_get_board_name(dev);
+ if (board_name == NULL) {
+ log_error("Failed to get board name.\n");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ printf("board: %s\n", board_name);
+ if (strcmp(board_name, "bladerf1") == 0) {
+ printf("freqsel: %u\n", qt->freqsel);
+ printf("vcocap: %u\n", qt->vcocap);
+ printf("nint: %u\n", qt->nint);
+ printf("nfrac: %u\n", qt->nfrac);
+ printf("flags: %u\n", qt->flags);
+ printf("xb_gpio: %u\n", qt->xb_gpio);
+ } else if (strcmp(board_name, "bladerf2") == 0) {
+ printf("nios_profile: %u\n", qt->nios_profile);
+ printf("rffe_profile: %u\n", qt->rffe_profile);
+ printf("port: %u\n", qt->port);
+ printf("spdt: %u\n", qt->spdt);
+ } else {
+ log_error("Unknown bladeRF board name: %s\n", board_name);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ return 0;
+}
+
+int bladerf_schedule_retune(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_timestamp timestamp,
+ bladerf_frequency frequency,
+ struct bladerf_quick_tune *quick_tune)
+
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status =
+ dev->board->schedule_retune(dev, ch, timestamp, frequency, quick_tune);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_cancel_scheduled_retunes(struct bladerf *dev, bladerf_channel ch)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->cancel_scheduled_retunes(dev, ch);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* DC/Phase/Gain Correction */
+/******************************************************************************/
+
+int bladerf_get_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_correction corr,
+ bladerf_correction_value *value)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_correction(dev, ch, corr, value);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_set_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_correction corr,
+ bladerf_correction_value value)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_correction(dev, ch, corr, value);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Trigger */
+/******************************************************************************/
+
+int bladerf_trigger_init(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal signal,
+ struct bladerf_trigger *trigger)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->trigger_init(dev, ch, signal, trigger);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_trigger_arm(struct bladerf *dev,
+ const struct bladerf_trigger *trigger,
+ bool arm,
+ uint64_t resv1,
+ uint64_t resv2)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->trigger_arm(dev, trigger, arm, resv1, resv2);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_trigger_fire(struct bladerf *dev,
+ const struct bladerf_trigger *trigger)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->trigger_fire(dev, trigger);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_trigger_state(struct bladerf *dev,
+ const struct bladerf_trigger *trigger,
+ bool *is_armed,
+ bool *has_fired,
+ bool *fire_requested,
+ uint64_t *reserved1,
+ uint64_t *reserved2)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->trigger_state(dev, trigger, is_armed, has_fired,
+ fire_requested, reserved1, reserved2);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Streaming */
+/******************************************************************************/
+
+int bladerf_init_stream(struct bladerf_stream **stream,
+ struct bladerf *dev,
+ bladerf_stream_cb callback,
+ void ***buffers,
+ size_t num_buffers,
+ bladerf_format format,
+ size_t samples_per_buffer,
+ size_t num_transfers,
+ void *data)
+{
+ int status;
+ bladerf_sample_rate tx_samp_rate;
+ bladerf_sample_rate rx_samp_rate;
+ MUTEX_LOCK(&dev->lock);
+
+ if (format == BLADERF_FORMAT_SC8_Q7 || format == BLADERF_FORMAT_SC8_Q7_META) {
+ if (strcmp(bladerf_get_board_name(dev), "bladerf2") != 0) {
+ log_error("bladeRF 2.0 required for 8bit format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+ }
+
+ status = dev->board->init_stream(stream, dev, callback, buffers,
+ num_buffers, format, samples_per_buffer,
+ num_transfers, data);
+
+ dev->board->get_sample_rate(dev, BLADERF_MODULE_TX, &tx_samp_rate);
+ if (tx_samp_rate) {
+ if (tx_samp_rate < num_transfers * samples_per_buffer / (*stream)->transfer_timeout) {
+ log_warning("TX samples may be dropped.\n");
+ log_warning("Condition to meet: samp_rate > num_transfers * samples_per_buffer / transfer_timeout\n");
+ }
+ }
+
+ dev->board->get_sample_rate(dev, BLADERF_MODULE_RX, &rx_samp_rate);
+ if (rx_samp_rate) {
+ if (rx_samp_rate < num_transfers * samples_per_buffer / (*stream)->transfer_timeout) {
+ log_warning("RX samples may be dropped.\n");
+ log_warning("Condition to meet: samp_rate > num_transfers * samples_per_buffer / transfer_timeout\n");
+ }
+ }
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_stream(struct bladerf_stream *stream, bladerf_channel_layout layout)
+{
+ return stream->dev->board->stream(stream, layout);
+}
+
+int bladerf_submit_stream_buffer(struct bladerf_stream *stream,
+ void *buffer,
+ unsigned int timeout_ms)
+{
+ return stream->dev->board->submit_stream_buffer(stream, buffer, timeout_ms,
+ false);
+}
+
+int bladerf_submit_stream_buffer_nb(struct bladerf_stream *stream, void *buffer)
+{
+ return stream->dev->board->submit_stream_buffer(stream, buffer, 0, true);
+}
+
+void bladerf_deinit_stream(struct bladerf_stream *stream)
+{
+ if (stream) {
+ stream->dev->board->deinit_stream(stream);
+ }
+}
+
+int bladerf_set_stream_timeout(struct bladerf *dev,
+ bladerf_direction dir,
+ unsigned int timeout)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_stream_timeout(dev, dir, timeout);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_stream_timeout(struct bladerf *dev,
+ bladerf_direction dir,
+ unsigned int *timeout)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_stream_timeout(dev, dir, timeout);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_sync_config(struct bladerf *dev,
+ bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int num_buffers,
+ unsigned int buffer_size,
+ unsigned int num_transfers,
+ unsigned int stream_timeout)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ if (format == BLADERF_FORMAT_SC8_Q7 || format == BLADERF_FORMAT_SC8_Q7_META) {
+ if (strcmp(bladerf_get_board_name(dev), "bladerf2") != 0) {
+ log_error("bladeRF 2.0 required for 8bit format\n");
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+ }
+
+ status =
+ dev->board->sync_config(dev, layout, format, num_buffers, buffer_size,
+ num_transfers, stream_timeout);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_sync_tx(struct bladerf *dev,
+ void const *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms)
+{
+ CHECK_NULL(samples);
+ return dev->board->sync_tx(dev, samples, num_samples, metadata, timeout_ms);
+}
+
+int bladerf_sync_rx(struct bladerf *dev,
+ void *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms)
+{
+ return dev->board->sync_rx(dev, samples, num_samples, metadata, timeout_ms);
+}
+
+int bladerf_get_timestamp(struct bladerf *dev,
+ bladerf_direction dir,
+ bladerf_timestamp *timestamp)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_timestamp(dev, dir, timestamp);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_interleave_stream_buffer(bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int buffer_size,
+ void *samples)
+{
+ return _interleave_interleave_buf(layout, format, buffer_size, samples);
+}
+
+int bladerf_deinterleave_stream_buffer(bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int buffer_size,
+ void *samples)
+{
+ return _interleave_deinterleave_buf(layout, format, buffer_size, samples);
+}
+
+/******************************************************************************/
+/* FPGA/Firmware Loading/Flashing */
+/******************************************************************************/
+
+int bladerf_load_fpga(struct bladerf *dev, const char *fpga_file)
+{
+ uint8_t *buf = NULL;
+ size_t buf_size;
+ int status;
+
+ status = file_read_buffer(fpga_file, &buf, &buf_size);
+ if (status != 0) {
+ log_error("Failed to read FPGA image: %s\n", bladerf_strerror(status));
+ goto exit;
+ }
+
+ status = dev->board->load_fpga(dev, buf, buf_size);
+
+exit:
+ free(buf);
+ return status;
+}
+
+int bladerf_flash_fpga(struct bladerf *dev, const char *fpga_file)
+{
+ uint8_t *buf = NULL;
+ size_t buf_size;
+ int status;
+
+ status = file_read_buffer(fpga_file, &buf, &buf_size);
+ if (status != 0) {
+ goto exit;
+ }
+
+ MUTEX_LOCK(&dev->lock);
+ status = dev->board->flash_fpga(dev, buf, buf_size);
+ MUTEX_UNLOCK(&dev->lock);
+
+exit:
+ free(buf);
+ return status;
+}
+
+int bladerf_erase_stored_fpga(struct bladerf *dev)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->erase_stored_fpga(dev);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_flash_firmware(struct bladerf *dev, const char *firmware_file)
+{
+ uint8_t *buf = NULL;
+ size_t buf_size;
+ int status;
+
+ status = file_read_buffer(firmware_file, &buf, &buf_size);
+ if (status != 0) {
+ goto exit;
+ }
+
+ MUTEX_LOCK(&dev->lock);
+ status = dev->board->flash_firmware(dev, buf, buf_size);
+ MUTEX_UNLOCK(&dev->lock);
+
+exit:
+ free(buf);
+ return status;
+}
+
+int bladerf_device_reset(struct bladerf *dev)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->device_reset(dev);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Tuning mode */
+/******************************************************************************/
+
+int bladerf_set_tuning_mode(struct bladerf *dev, bladerf_tuning_mode mode)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_tuning_mode(dev, mode);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_tuning_mode(struct bladerf *dev, bladerf_tuning_mode *mode)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_tuning_mode(dev, mode);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Loopback */
+/******************************************************************************/
+
+int bladerf_get_loopback_modes(struct bladerf *dev,
+ struct bladerf_loopback_modes const **modes)
+{
+ int status;
+
+ status = dev->board->get_loopback_modes(dev, modes);
+
+ return status;
+}
+
+bool bladerf_is_loopback_mode_supported(struct bladerf *dev,
+ bladerf_loopback mode)
+{
+ struct bladerf_loopback_modes modes;
+ struct bladerf_loopback_modes const *modesptr = &modes;
+ int i, count;
+
+ count = bladerf_get_loopback_modes(dev, &modesptr);
+
+ for (i = 0; i < count; ++i) {
+ if (modesptr[i].mode == mode) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int bladerf_set_loopback(struct bladerf *dev, bladerf_loopback l)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_loopback(dev, l);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_loopback(struct bladerf *dev, bladerf_loopback *l)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_loopback(dev, l);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Sample RX FPGA Mux */
+/******************************************************************************/
+
+int bladerf_set_rx_mux(struct bladerf *dev, bladerf_rx_mux mux)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_rx_mux(dev, mux);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_rx_mux(struct bladerf *dev, bladerf_rx_mux *mux)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_rx_mux(dev, mux);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Low-level VCTCXO Tamer Mode */
+/******************************************************************************/
+
+int bladerf_set_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode mode)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->set_vctcxo_tamer_mode(dev, mode);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode *mode)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_vctcxo_tamer_mode(dev, mode);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Low-level VCTCXO Trim DAC access */
+/******************************************************************************/
+
+int bladerf_get_vctcxo_trim(struct bladerf *dev, uint16_t *trim)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->get_vctcxo_trim(dev, trim);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_trim_dac_read(struct bladerf *dev, uint16_t *trim)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->trim_dac_read(dev, trim);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_trim_dac_write(struct bladerf *dev, uint16_t trim)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->trim_dac_write(dev, trim);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_dac_read(struct bladerf *dev, uint16_t *trim)
+{
+ return bladerf_trim_dac_read(dev, trim);
+}
+int bladerf_dac_write(struct bladerf *dev, uint16_t trim)
+{
+ return bladerf_trim_dac_write(dev, trim);
+}
+
+/******************************************************************************/
+/* Low-level Trigger control access */
+/******************************************************************************/
+
+int bladerf_read_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t *val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->read_trigger(dev, ch, trigger, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_write_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->write_trigger(dev, ch, trigger, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Low-level Wishbone Master access */
+/******************************************************************************/
+int bladerf_wishbone_master_read(struct bladerf *dev, uint32_t addr, uint32_t *data)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->wishbone_master_read(dev, addr, data);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_wishbone_master_write(struct bladerf *dev, uint32_t addr, uint32_t data)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->wishbone_master_write(dev, addr, data);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Low-level Configuration GPIO access */
+/******************************************************************************/
+
+int bladerf_config_gpio_read(struct bladerf *dev, uint32_t *val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->config_gpio_read(dev, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_config_gpio_write(struct bladerf *dev, uint32_t val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->config_gpio_write(dev, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Low-level SPI Flash access */
+/******************************************************************************/
+
+int bladerf_erase_flash(struct bladerf *dev,
+ uint32_t erase_block,
+ uint32_t count)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->erase_flash(dev, erase_block, count);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_erase_flash_bytes(struct bladerf *dev,
+ uint32_t address,
+ uint32_t length)
+{
+ int status;
+ uint32_t eb;
+ uint32_t count;
+
+ /* Make sure address is aligned to an erase block boundary */
+ if( (address % dev->flash_arch->ebsize_bytes) == 0 ) {
+ /* Convert into units of flash pages */
+ eb = address / dev->flash_arch->ebsize_bytes;
+ } else {
+ log_error("Address or length not aligned on a flash page boundary.\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Check for the case of erasing less than 1 erase block.
+ * For example, the calibration data. If so, round up to 1 EB.
+ * If erasing more than 1 EB worth of data, make sure the length
+ * is aligned to an EB boundary. */
+ if( (length > 0) && (length < dev->flash_arch->ebsize_bytes) ) {
+ count = 1;
+ } else if ((length % dev->flash_arch->ebsize_bytes) == 0) {
+ /* Convert into units of flash pages */
+ count = length / dev->flash_arch->ebsize_bytes;
+ } else {
+ log_error("Address or length not aligned on a flash page boundary.\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_erase_flash(dev, eb, count);
+
+ return status;
+}
+
+int bladerf_read_flash(struct bladerf *dev,
+ uint8_t *buf,
+ uint32_t page,
+ uint32_t count)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->read_flash(dev, buf, page, count);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_read_flash_bytes(struct bladerf *dev,
+ uint8_t *buf,
+ uint32_t address,
+ uint32_t length)
+{
+ int status;
+ uint32_t page;
+ uint32_t count;
+
+ /* Check alignment */
+ if( ((address % dev->flash_arch->psize_bytes) != 0) ||
+ ((length % dev->flash_arch->psize_bytes) != 0) ) {
+ log_error("Address or length not aligned on a flash page boundary.\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Convert into units of flash pages */
+ page = address / dev->flash_arch->psize_bytes;
+ count = length / dev->flash_arch->psize_bytes;
+
+ status = bladerf_read_flash(dev, buf, page, count);
+
+ return status;
+}
+
+int bladerf_write_flash(struct bladerf *dev,
+ const uint8_t *buf,
+ uint32_t page,
+ uint32_t count)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->write_flash(dev, buf, page, count);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_write_flash_bytes(struct bladerf *dev,
+ const uint8_t *buf,
+ uint32_t address,
+ uint32_t length)
+{
+ int status;
+ uint32_t page;
+ uint32_t count;
+
+ /* Check alignment */
+ if( ((address % dev->flash_arch->psize_bytes) != 0) ||
+ ((length % dev->flash_arch->psize_bytes) != 0) ) {
+ log_error("Address or length not aligned on a flash page boundary.\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Convert address and length into units of flash pages */
+ page = address / dev->flash_arch->psize_bytes;
+ count = length / dev->flash_arch->psize_bytes;
+
+ status = bladerf_write_flash(dev, buf, page, count);
+ return status;
+}
+
+int bladerf_read_otp(struct bladerf *dev,
+ uint8_t *buf)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->backend->get_otp(dev, (char *)buf);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_write_otp(struct bladerf *dev,
+ uint8_t *buf)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->backend->write_otp(dev, (char *)buf);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_lock_otp(struct bladerf *dev)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->backend->lock_otp(dev);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Helpers & Miscellaneous */
+/******************************************************************************/
+
+const char *bladerf_strerror(int error)
+{
+ switch (error) {
+ case BLADERF_ERR_UNEXPECTED:
+ return "An unexpected error occurred";
+ case BLADERF_ERR_RANGE:
+ return "Provided parameter was out of the allowable range";
+ case BLADERF_ERR_INVAL:
+ return "Invalid operation or parameter";
+ case BLADERF_ERR_MEM:
+ return "A memory allocation error occurred";
+ case BLADERF_ERR_IO:
+ return "File or device I/O failure";
+ case BLADERF_ERR_TIMEOUT:
+ return "Operation timed out";
+ case BLADERF_ERR_NODEV:
+ return "No devices available";
+ case BLADERF_ERR_UNSUPPORTED:
+ return "Operation not supported";
+ case BLADERF_ERR_MISALIGNED:
+ return "Misaligned flash access";
+ case BLADERF_ERR_CHECKSUM:
+ return "Invalid checksum";
+ case BLADERF_ERR_NO_FILE:
+ return "File not found";
+ case BLADERF_ERR_UPDATE_FPGA:
+ return "An FPGA update is required";
+ case BLADERF_ERR_UPDATE_FW:
+ return "A firmware update is required";
+ case BLADERF_ERR_TIME_PAST:
+ return "Requested timestamp is in the past";
+ case BLADERF_ERR_QUEUE_FULL:
+ return "Could not enqueue data into full queue";
+ case BLADERF_ERR_FPGA_OP:
+ return "An FPGA operation reported a failure";
+ case BLADERF_ERR_PERMISSION:
+ return "Insufficient permissions for the requested operation";
+ case BLADERF_ERR_WOULD_BLOCK:
+ return "The operation would block, but has been requested to be "
+ "non-blocking";
+ case BLADERF_ERR_NOT_INIT:
+ return "Insufficient initialization for the requested operation";
+ case 0:
+ return "Success";
+ default:
+ return "Unknown error code";
+ }
+}
+
+const char *bladerf_backend_str(bladerf_backend backend)
+{
+ return backend2str(backend);
+}
+
+void bladerf_log_set_verbosity(bladerf_log_level level)
+{
+ log_set_verbosity(level);
+#if defined(LOG_SYSLOG_ENABLED)
+ log_debug("Log verbosity has been set to: %d", level);
+#endif
+}
+
+void bladerf_set_usb_reset_on_open(bool enabled)
+{
+#if ENABLE_USB_DEV_RESET_ON_OPEN
+ bladerf_usb_reset_device_on_open = enabled;
+
+ log_verbose("USB reset on open %s\n", enabled ? "enabled" : "disabled");
+#else
+ log_verbose("%s has no effect. "
+ "ENABLE_USB_DEV_RESET_ON_OPEN not set at compile-time.\n",
+ __FUNCTION__);
+#endif
+}
+
+/******************************************************************************/
+/* Expansion board APIs */
+/******************************************************************************/
+
+int bladerf_expansion_attach(struct bladerf *dev, bladerf_xb xb)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->expansion_attach(dev, xb);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_expansion_get_attached(struct bladerf *dev, bladerf_xb *xb)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->board->expansion_get_attached(dev, xb);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/* XB100 */
+
+int bladerf_expansion_gpio_read(struct bladerf *dev, uint32_t *val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb100_gpio_read(dev, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_expansion_gpio_write(struct bladerf *dev, uint32_t val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb100_gpio_write(dev, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_expansion_gpio_masked_write(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb100_gpio_masked_write(dev, mask, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_expansion_gpio_dir_read(struct bladerf *dev, uint32_t *val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb100_gpio_dir_read(dev, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_expansion_gpio_dir_write(struct bladerf *dev, uint32_t val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb100_gpio_dir_write(dev, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_expansion_gpio_dir_masked_write(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb100_gpio_dir_masked_write(dev, mask, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/* XB200 */
+
+int bladerf_xb200_set_filterbank(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_filter filter)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb200_set_filterbank(dev, ch, filter);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_xb200_get_filterbank(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_filter *filter)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb200_get_filterbank(dev, ch, filter);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_xb200_set_path(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_path path)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb200_set_path(dev, ch, path);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_xb200_get_path(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_path *path)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb200_get_path(dev, ch, path);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/* XB300 */
+
+int bladerf_xb300_set_trx(struct bladerf *dev, bladerf_xb300_trx trx)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb300_set_trx(dev, trx);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_xb300_get_trx(struct bladerf *dev, bladerf_xb300_trx *trx)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb300_get_trx(dev, trx);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_xb300_set_amplifier_enable(struct bladerf *dev,
+ bladerf_xb300_amplifier amp,
+ bool enable)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb300_set_amplifier_enable(dev, amp, enable);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_xb300_get_amplifier_enable(struct bladerf *dev,
+ bladerf_xb300_amplifier amp,
+ bool *enable)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb300_get_amplifier_enable(dev, amp, enable);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_xb300_get_output_power(struct bladerf *dev, float *val)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = xb300_get_output_power(dev, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+/******************************************************************************/
+/* Features */
+/******************************************************************************/
+
+int bladerf_enable_feature(struct bladerf *dev, bladerf_feature feature, bool enable)
+{
+ int status;
+ MUTEX_LOCK(&dev->lock);
+
+ status = 0;
+
+ if(feature == BLADERF_FEATURE_DEFAULT) {
+ dev->feature = 0;
+ } else {
+ if(feature == BLADERF_FEATURE_OVERSAMPLE) {
+ if (strcmp(bladerf_get_board_name(dev), "bladerf2") != 0) {
+ log_error("BladeRF2 required for OVERSAMPLE feature\n");
+ status = BLADERF_ERR_UNSUPPORTED;
+ }
+ } else {
+ /* Unknown / Unsupported feature */
+ status = BLADERF_ERR_UNSUPPORTED;
+ }
+
+ if (status == 0) {
+ if (enable) {
+ dev->feature |= feature;
+ } else {
+ dev->feature &= ~feature;
+ }
+ }
+ }
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_get_feature(struct bladerf *dev, bladerf_feature* feature)
+{
+ MUTEX_LOCK(&dev->lock);
+ *feature = dev->feature;
+ MUTEX_UNLOCK(&dev->lock);
+
+ return 0;
+}
+
+int bladerf_set_oversample_register_config(struct bladerf *dev) {
+ const char *board_name;
+ board_name = bladerf_get_board_name(dev);
+
+ if (strcmp(board_name, "bladerf2") != 0) {
+ log_error("Oversample register config only applicable\n"
+ "for the bladeRF 2.0");
+
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ bladerf_set_rfic_register(dev,0x003,0x54); // OC Register
+
+ /* TX Register Assignments */
+ bladerf_set_rfic_register(dev,0x02,0xc0); // TX Enable and Filter Control
+ bladerf_set_rfic_register(dev,0xc2,0x9f); // TX BBF R1
+ bladerf_set_rfic_register(dev,0xc3,0x9f); // TX baseband filter R2
+ bladerf_set_rfic_register(dev,0xc4,0x9f); // TX baseband filter R3
+ bladerf_set_rfic_register(dev,0xc5,0x9f); // TX baseband filter R4
+ bladerf_set_rfic_register(dev,0xc6,0x9f); // TX baseband filter real pole word
+ bladerf_set_rfic_register(dev,0xc7,0x00); // TX baseband filter C1
+ bladerf_set_rfic_register(dev,0xc8,0x00); // TX baseband filter C2
+ bladerf_set_rfic_register(dev,0xc9,0x00); // TX baseband filter real pole word
+
+ /* RX Register Assignments */
+ // Gain and calibration
+ bladerf_set_rfic_register(dev,0x1e0,0x00); // RX1 BBF R1A
+ bladerf_set_rfic_register(dev,0x1e1,0x00); // RX2 BBF R1A
+ bladerf_set_rfic_register(dev,0x1e2,0x00); // RX1 tune control
+ bladerf_set_rfic_register(dev,0x1e3,0x00); // RX2 tune control
+ bladerf_set_rfic_register(dev,0x1e4,0x00); // RX1 BBF R5
+ bladerf_set_rfic_register(dev,0x1e5,0x00); // RX2 BBF R5
+ bladerf_set_rfic_register(dev,0x1e6,0x00); // RX BBF R2346
+
+ // Miller and BBF caps
+ bladerf_set_rfic_register(dev,0x1e7,0x00); // RX BBF C1 MSB
+ bladerf_set_rfic_register(dev,0x1e8,0x00); // RX BBF C1 LSB
+ bladerf_set_rfic_register(dev,0x1e9,0x00); // RX baseband filter real pole word
+ bladerf_set_rfic_register(dev,0x1ea,0x00);
+ bladerf_set_rfic_register(dev,0x1eb,0x00);
+ bladerf_set_rfic_register(dev,0x1ec,0x00);
+ bladerf_set_rfic_register(dev,0x1ed,0x00);
+ bladerf_set_rfic_register(dev,0x1ee,0x00);
+ bladerf_set_rfic_register(dev,0x1ef,0x00);
+
+ // BIST and Data Port Test Config [D1:D0] "Must be 2’b00"
+ bladerf_set_rfic_register(dev,0x3f6,0x03);
+
+ return 0;
+}
+
+/******************************************************************************/
+/* Calibration */
+/******************************************************************************/
+
+int bladerf_load_gain_calibration(struct bladerf *dev, bladerf_channel ch, const char* cal_file_loc)
+{
+ int status = 0;
+ const char *board_name;
+ char *full_path = NULL;
+ char *full_path_bin = NULL;
+ char *ext;
+
+ size_t filename_len = PATH_MAX;
+ char *filename = (char *)calloc(1, filename_len + 1);
+ CHECK_NULL(filename);
+
+ bladerf_gain_mode gain_mode_before_gain_reset;
+
+ log_debug("Loading gain calibration\n");
+ MUTEX_LOCK(&dev->lock);
+
+ board_name = bladerf_get_board_name(dev);
+ if (strcmp(board_name, "bladerf2") != 0) {
+ log_error("Gain calibration unsupported on this device: %s\n", board_name);
+ status = BLADERF_ERR_UNSUPPORTED;
+ goto error;
+ }
+
+ if (cal_file_loc != NULL) {
+ strcpy(filename, cal_file_loc);
+ } else {
+ log_debug("No calibration file specified, using serial number\n");
+ strcpy(filename, dev->ident.serial);
+ filename_len -= strlen(filename);
+
+ if (BLADERF_CHANNEL_IS_TX(ch))
+ strncat(filename, "_tx_gain_cal.tbl", filename_len);
+ else
+ strncat(filename, "_rx_gain_cal.tbl", filename_len);
+ }
+
+ full_path = file_find(filename);
+ if (full_path == NULL) {
+ log_error("Failed to find gain calibration file: %s\n", filename);
+ status = BLADERF_ERR_NO_FILE;
+ goto error;
+ }
+
+ /** Convert to binary format if CSV */
+ full_path_bin = (char*)malloc(strlen(full_path) + 1);
+ strcpy(full_path_bin, full_path);
+ ext = strstr(full_path_bin, ".csv");
+ if (ext) {
+ log_debug("Converting gain calibration to binary format\n");
+ strcpy(ext, ".tbl");
+ status = gain_cal_csv_to_bin(dev, full_path, full_path_bin, ch);
+ if (status != 0) {
+ log_error("Failed to convert csv to binary: %s -> %s\n",
+ full_path, full_path_bin);
+ status = EXIT_FAILURE;
+ goto error;
+ }
+ }
+
+ status = load_gain_calibration(dev, ch, full_path_bin);
+ if (status != 0) {
+ log_error("Failed to load calibration\n");
+ status = BLADERF_ERR_UNEXPECTED;
+ goto error;
+ }
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ /* Save current gain mode before gain reset */
+ if (BLADERF_CHANNEL_IS_TX(ch) == false)
+ dev->board->get_gain_mode(dev, ch, &gain_mode_before_gain_reset);
+
+ /* Reset gain to ensure calibration adjustment is applied after loading */
+ status = bladerf_set_gain(dev, ch, dev->gain_tbls[ch].gain_target);
+ if (status != 0) {
+ log_error("%s: Failed to reset gain.\n", __FUNCTION__);
+ goto error;
+ }
+
+ /** Restore previous gain mode */
+ if (BLADERF_CHANNEL_IS_TX(ch) == false) {
+ status = bladerf_set_gain_mode(dev, ch, gain_mode_before_gain_reset);
+ if (status != 0) {
+ log_error("%s: Failed to reset gain mode.\n", __FUNCTION__);
+ goto error;
+ }
+ }
+
+error:
+ if (full_path)
+ free(full_path);
+ if (full_path_bin)
+ free(full_path_bin);
+ if (filename)
+ free(filename);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
+
+int bladerf_enable_gain_calibration(struct bladerf *dev, bladerf_channel ch, bool en)
+{
+ CHECK_NULL(dev);
+ int status = 0;
+
+ if (dev->gain_tbls[ch].state == BLADERF_GAIN_CAL_UNINITIALIZED) {
+ log_warning("%s: Gain calibration not loaded\n", __FUNCTION__);
+ return 0;
+ }
+
+ dev->gain_tbls[ch].enabled = en;
+ status = bladerf_set_gain(dev, ch, dev->gain_tbls[ch].gain_target);
+ if (status != 0) {
+ log_error("%s: Failed to reset gain.\n", __FUNCTION__);
+ return status;
+ }
+
+ return status;
+}
+
+int bladerf_print_gain_calibration(struct bladerf *dev, bladerf_channel ch, bool with_entries)
+{
+ CHECK_NULL(dev);
+ int status = 0;
+ const char *board_name;
+ struct bladerf_gain_cal_tbl *gain_tbls = dev->gain_tbls;
+
+ board_name = bladerf_get_board_name(dev);
+ if (strcmp(board_name, "bladerf2") != 0) {
+ log_error("Gain calibration unsupported on this device: %s\n", board_name);
+ status = BLADERF_ERR_UNSUPPORTED;
+ goto error;
+ }
+
+ if (gain_tbls[ch].state == BLADERF_GAIN_CAL_UNINITIALIZED) {
+ printf("Gain Calibration [%s]: uninitialized\n", channel2str(ch));
+ return 0;
+ }
+
+ printf("Gain Calibration [%s]: loaded\n", channel2str(ch));
+ printf(" Status: %s\n", (gain_tbls[ch].enabled) ? "enabled" : "disabled");
+ printf(" Version: %i.%i.%i\n",
+ gain_tbls[ch].version.major, gain_tbls[ch].version.minor, gain_tbls[ch].version.patch);
+ printf(" Number of Entries: %u\n", gain_tbls[ch].n_entries);
+ printf(" Start Frequency: %" PRIu64 " Hz\n", gain_tbls[ch].start_freq);
+ printf(" Stop Frequency: %" PRIu64 " Hz\n", gain_tbls[ch].stop_freq);
+ printf(" File Path: %s\n", gain_tbls[ch].file_path);
+
+ if (with_entries) {
+ for (size_t i = 0; i < gain_tbls[ch].n_entries; i++) {
+ printf("%" PRIu64 ",%f\n", gain_tbls[ch].entries[i].freq, gain_tbls[ch].entries[i].gain_corr);
+ }
+ }
+
+error:
+ return status;
+}
+
+int bladerf_get_gain_calibration(struct bladerf *dev, bladerf_channel ch, const struct bladerf_gain_cal_tbl **tbl)
+{
+ CHECK_NULL(dev);
+ MUTEX_LOCK(&dev->lock);
+
+ if (dev->gain_tbls[ch].state != BLADERF_GAIN_CAL_LOADED) {
+ log_error("%s: Gain calibration not loaded\n", __FUNCTION__);
+ MUTEX_UNLOCK(&dev->lock);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ *tbl = &(dev->gain_tbls[ch]);
+
+ MUTEX_UNLOCK(&dev->lock);
+ return 0;
+}
+
+int bladerf_get_gain_target(struct bladerf *dev, bladerf_channel ch, int *gain_target)
+{
+ int status = 0;
+ CHECK_NULL(dev);
+ MUTEX_LOCK(&dev->lock);
+ bladerf_frequency current_frequency;
+ struct bladerf_gain_cal_tbl *cal_table = &dev->gain_tbls[ch];
+ struct bladerf_gain_cal_entry current_entry;
+ bladerf_gain current_gain;
+ bladerf_gain_mode gain_mode;
+
+
+ if (dev->gain_tbls[ch].state == BLADERF_GAIN_CAL_UNINITIALIZED) {
+ log_error("Gain calibration not loaded\n");
+ status = BLADERF_ERR_UNEXPECTED;
+ goto error;
+ }
+
+ if (BLADERF_CHANNEL_IS_TX(ch) == true) {
+ *gain_target = cal_table->gain_target;
+ goto error;
+ }
+
+ dev->board->get_gain_mode(dev, ch, &gain_mode);
+
+ if (gain_mode == BLADERF_GAIN_MGC) {
+ *gain_target = cal_table->gain_target;
+ goto error;
+ }
+
+ CHECK_STATUS(dev->board->get_gain(dev, ch, &current_gain));
+ CHECK_STATUS(dev->board->get_frequency(dev, ch, &current_frequency));
+ CHECK_STATUS(get_gain_cal_entry(cal_table, current_frequency, &current_entry));
+ *gain_target = current_gain + current_entry.gain_corr;
+
+error:
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+}
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/bladerf1.c b/Radio/HW/BladeRF/src/board/bladerf1/bladerf1.c
new file mode 100644
index 0000000..85f4cce
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf1/bladerf1.c
@@ -0,0 +1,4166 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2017 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "libbladeRF.h"
+
+#include "log.h"
+#include "conversions.h"
+#include "bladeRF.h"
+
+#include "board/board.h"
+
+#include "compatibility.h"
+#include "capabilities.h"
+#include "calibration.h"
+#include "flash.h"
+
+#include "driver/smb_clock.h"
+#include "driver/si5338.h"
+#include "driver/dac161s055.h"
+#include "driver/spi_flash.h"
+#include "driver/fpga_trigger.h"
+#include "lms.h"
+#include "nios_pkt_retune.h"
+#include "band_select.h"
+
+#include "backend/usb/usb.h"
+#include "backend/backend_config.h"
+
+#include "expansion/xb100.h"
+#include "expansion/xb200.h"
+#include "expansion/xb300.h"
+
+#include "streaming/async.h"
+#include "streaming/sync.h"
+
+#include "devinfo.h"
+#include "helpers/version.h"
+#include "helpers/file.h"
+#include "version.h"
+
+/******************************************************************************
+ * bladeRF1 board state *
+ ******************************************************************************/
+
+/* 1 TX, 1 RX */
+#define NUM_MODULES 2
+
+struct bladerf1_board_data {
+ /* Board state */
+ enum {
+ STATE_UNINITIALIZED,
+ STATE_FIRMWARE_LOADED,
+ STATE_FPGA_LOADED,
+ STATE_INITIALIZED,
+ } state;
+
+ /* Bitmask of capabilities determined by version numbers */
+ uint64_t capabilities;
+
+ /* Format currently being used with a module, or -1 if module is not used */
+ bladerf_format module_format[NUM_MODULES];
+
+ /* Which mode of operation we use for tuning */
+ bladerf_tuning_mode tuning_mode;
+
+ /* Calibration data */
+ struct calibrations {
+ struct dc_cal_tbl *dc_rx;
+ struct dc_cal_tbl *dc_tx;
+ } cal;
+ uint16_t dac_trim;
+
+ /* Board properties */
+ bladerf_fpga_size fpga_size;
+ /* Data message size */
+ size_t msg_size;
+
+ /* Version information */
+ struct bladerf_version fpga_version;
+ struct bladerf_version fw_version;
+ char fpga_version_str[BLADERF_VERSION_STR_MAX+1];
+ char fw_version_str[BLADERF_VERSION_STR_MAX+1];
+
+ /* Synchronous interface handles */
+ struct bladerf_sync sync[NUM_MODULES];
+};
+
+#define _CHECK_BOARD_STATE(_state, _locked) \
+ do { \
+ struct bladerf1_board_data *board_data = dev->board_data; \
+ if (board_data->state < _state) { \
+ log_error("Board state insufficient for operation " \
+ "(current \"%s\", requires \"%s\").\n", \
+ bladerf1_state_to_string[board_data->state], \
+ bladerf1_state_to_string[_state]); \
+ if (_locked) { \
+ MUTEX_UNLOCK(&dev->lock); \
+ } \
+ return BLADERF_ERR_NOT_INIT; \
+ } \
+ } while(0)
+
+#define CHECK_BOARD_STATE(_state) _CHECK_BOARD_STATE(_state, false)
+#define CHECK_BOARD_STATE_LOCKED(_state) _CHECK_BOARD_STATE(_state, true)
+
+#define __round_int(x) (x >= 0 ? (int)(x + 0.5) : (int)(x - 0.5))
+#define __round_int64(x) (x >= 0 ? (int64_t)(x + 0.5) : (int64_t)(x - 0.5))
+
+#define __scale(r, v) ((float)(v) / (r)->scale)
+#define __scale_int(r, v) (__round_int(__scale(r, v)))
+#define __scale_int64(r, v) (__round_int64(__scale(r, v)))
+
+#define __unscale(r, v) ((float)(v) * (r)->scale)
+#define __unscale_int(r, v) (__round_int(__unscale(r, v)))
+#define __unscale_int64(r, v) (__round_int64(__unscale(r, v)))
+
+/******************************************************************************/
+/* Constants */
+/******************************************************************************/
+
+/* Board state to string map */
+
+static const char *bladerf1_state_to_string[] = {
+ [STATE_UNINITIALIZED] = "Uninitialized",
+ [STATE_FIRMWARE_LOADED] = "Firmware Loaded",
+ [STATE_FPGA_LOADED] = "FPGA Loaded",
+ [STATE_INITIALIZED] = "Initialized",
+};
+
+/* RX gain offset */
+#define BLADERF1_RX_GAIN_OFFSET -6.0f
+
+/* Overall RX gain range */
+static const struct bladerf_range bladerf1_rx_gain_range = {
+ FIELD_INIT(.min, __round_int64(BLADERF_RXVGA1_GAIN_MIN + BLADERF_RXVGA2_GAIN_MIN + BLADERF1_RX_GAIN_OFFSET)),
+ FIELD_INIT(.max, __round_int64(BLADERF_LNA_GAIN_MAX_DB + BLADERF_RXVGA1_GAIN_MAX + BLADERF_RXVGA2_GAIN_MAX + BLADERF1_RX_GAIN_OFFSET)),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+};
+
+/* TX gain offset: 60 dB system gain ~= 0 dBm output */
+#define BLADERF1_TX_GAIN_OFFSET 52.0f
+
+/* Overall TX gain range */
+static const struct bladerf_range bladerf1_tx_gain_range = {
+ FIELD_INIT(.min, __round_int64(BLADERF_TXVGA1_GAIN_MIN + BLADERF_TXVGA2_GAIN_MIN + BLADERF1_TX_GAIN_OFFSET)),
+ FIELD_INIT(.max, __round_int64(BLADERF_TXVGA1_GAIN_MAX + BLADERF_TXVGA2_GAIN_MAX + BLADERF1_TX_GAIN_OFFSET)),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+};
+
+/* RX gain modes */
+
+static const struct bladerf_gain_modes bladerf1_rx_gain_modes[] = {
+ {
+ FIELD_INIT(.name, "automatic"),
+ FIELD_INIT(.mode, BLADERF_GAIN_DEFAULT)
+ },
+ {
+ FIELD_INIT(.name, "manual"),
+ FIELD_INIT(.mode, BLADERF_GAIN_MGC)
+ },
+};
+
+struct bladerf_gain_stage_info {
+ const char *name;
+ struct bladerf_range range;
+};
+
+/* RX gain stages */
+
+static const struct bladerf_gain_stage_info bladerf1_rx_gain_stages[] = {
+ {
+ FIELD_INIT(.name, "lna"),
+ FIELD_INIT(.range, {
+ FIELD_INIT(.min, 0),
+ FIELD_INIT(.max, BLADERF_LNA_GAIN_MAX_DB),
+ FIELD_INIT(.step, 3),
+ FIELD_INIT(.scale, 1),
+ }),
+ },
+ {
+ FIELD_INIT(.name, "rxvga1"),
+ FIELD_INIT(.range, {
+ FIELD_INIT(.min, BLADERF_RXVGA1_GAIN_MIN),
+ FIELD_INIT(.max, BLADERF_RXVGA1_GAIN_MAX),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ },
+ {
+ FIELD_INIT(.name, "rxvga2"),
+ FIELD_INIT(.range, {
+ FIELD_INIT(.min, BLADERF_RXVGA2_GAIN_MIN),
+ FIELD_INIT(.max, BLADERF_RXVGA2_GAIN_MAX),
+ FIELD_INIT(.step, 3),
+ FIELD_INIT(.scale, 1),
+ }),
+ },
+};
+
+/* TX gain stages */
+
+static const struct bladerf_gain_stage_info bladerf1_tx_gain_stages[] = {
+ {
+ FIELD_INIT(.name, "txvga1"),
+ FIELD_INIT(.range, {
+ FIELD_INIT(.min, BLADERF_TXVGA1_GAIN_MIN),
+ FIELD_INIT(.max, BLADERF_TXVGA1_GAIN_MAX),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ },
+ {
+ FIELD_INIT(.name, "txvga2"),
+ FIELD_INIT(.range, {
+ FIELD_INIT(.min, BLADERF_TXVGA2_GAIN_MIN),
+ FIELD_INIT(.max, BLADERF_TXVGA2_GAIN_MAX),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ },
+};
+
+/* Sample Rate Range */
+
+static const struct bladerf_range bladerf1_sample_rate_range = {
+ FIELD_INIT(.min, BLADERF_SAMPLERATE_MIN),
+ FIELD_INIT(.max, BLADERF_SAMPLERATE_REC_MAX),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+};
+
+/* Bandwidth Range */
+
+static const struct bladerf_range bladerf1_bandwidth_range = {
+ FIELD_INIT(.min, BLADERF_BANDWIDTH_MIN),
+ FIELD_INIT(.max, BLADERF_BANDWIDTH_MAX),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+};
+
+/* Frequency Range */
+
+static const struct bladerf_range bladerf1_frequency_range = {
+ FIELD_INIT(.min, BLADERF_FREQUENCY_MIN),
+ FIELD_INIT(.max, BLADERF_FREQUENCY_MAX),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+};
+
+static const struct bladerf_range bladerf1_xb200_frequency_range = {
+ FIELD_INIT(.min, 0),
+ FIELD_INIT(.max, BLADERF_FREQUENCY_MAX),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+};
+
+/* Loopback modes */
+
+static const struct bladerf_loopback_modes bladerf1_loopback_modes[] = {
+ {
+ FIELD_INIT(.name, "none"),
+ FIELD_INIT(.mode, BLADERF_LB_NONE)
+ },
+ {
+ FIELD_INIT(.name, "firmware"),
+ FIELD_INIT(.mode, BLADERF_LB_FIRMWARE)
+ },
+ {
+ FIELD_INIT(.name, "bb_txlpf_rxvga2"),
+ FIELD_INIT(.mode, BLADERF_LB_BB_TXLPF_RXVGA2)
+ },
+ {
+ FIELD_INIT(.name, "bb_txlpf_rxlpf"),
+ FIELD_INIT(.mode, BLADERF_LB_BB_TXLPF_RXLPF)
+ },
+ {
+ FIELD_INIT(.name, "bb_txvga1_rxvga2"),
+ FIELD_INIT(.mode, BLADERF_LB_BB_TXVGA1_RXVGA2)
+ },
+ {
+ FIELD_INIT(.name, "bb_txvga1_rxlpf"),
+ FIELD_INIT(.mode, BLADERF_LB_BB_TXVGA1_RXLPF)
+ },
+ {
+ FIELD_INIT(.name, "rf_lna1"),
+ FIELD_INIT(.mode, BLADERF_LB_RF_LNA1)
+ },
+ {
+ FIELD_INIT(.name, "rf_lna2"),
+ FIELD_INIT(.mode, BLADERF_LB_RF_LNA2)
+ },
+ {
+ FIELD_INIT(.name, "rf_lna3"),
+ FIELD_INIT(.mode, BLADERF_LB_RF_LNA3)
+ },
+};
+
+/* RF ports */
+
+struct bladerf_lms_port_name_map {
+ const char *name;
+ union {
+ lms_lna rx_lna;
+ lms_pa tx_pa;
+ };
+};
+
+static const struct bladerf_lms_port_name_map bladerf1_rx_port_map[] = {
+ {
+ FIELD_INIT(.name, "none"),
+ FIELD_INIT(.rx_lna, LNA_NONE),
+ },
+ {
+ FIELD_INIT(.name, "lna1"),
+ FIELD_INIT(.rx_lna, LNA_1),
+ },
+ {
+ FIELD_INIT(.name, "lna2"),
+ FIELD_INIT(.rx_lna, LNA_2),
+ },
+ {
+ FIELD_INIT(.name, "lna3"),
+ FIELD_INIT(.rx_lna, LNA_3),
+ },
+};
+
+static const struct bladerf_lms_port_name_map bladerf1_tx_port_map[] = {
+ {
+ FIELD_INIT(.name, "aux"),
+ FIELD_INIT(.tx_pa, PA_AUX),
+ },
+ {
+ FIELD_INIT(.name, "pa1"),
+ FIELD_INIT(.tx_pa, PA_1),
+ },
+ {
+ FIELD_INIT(.name, "pa2"),
+ FIELD_INIT(.tx_pa, PA_2),
+ },
+ {
+ FIELD_INIT(.name, "none"),
+ FIELD_INIT(.tx_pa, PA_NONE),
+ },
+};
+
+/******************************************************************************/
+/* Low-level Initialization */
+/******************************************************************************/
+
+static bladerf_tuning_mode tuning_get_default_mode(struct bladerf *dev)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ bladerf_tuning_mode mode;
+ const char *env_var;
+
+ if (have_cap(board_data->capabilities, BLADERF_CAP_FPGA_TUNING)) {
+ mode = BLADERF_TUNING_MODE_FPGA;
+ } else {
+ mode = BLADERF_TUNING_MODE_HOST;
+ }
+
+ env_var = getenv("BLADERF_DEFAULT_TUNING_MODE");
+
+ if (env_var != NULL) {
+ if (!strcasecmp("host", env_var)) {
+ mode = BLADERF_TUNING_MODE_HOST;
+ } else if (!strcasecmp("fpga", env_var)) {
+ mode = BLADERF_TUNING_MODE_FPGA;
+
+ /* Just a friendly reminder... */
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_FPGA_TUNING)) {
+ log_warning("The loaded FPGA version (%u.%u.%u) does not "
+ "support the tuning mode being used to override "
+ "the default.\n",
+ board_data->fpga_version.major, board_data->fpga_version.minor,
+ board_data->fpga_version.patch);
+ }
+ } else {
+ log_debug("Invalid tuning mode override: %s\n", env_var);
+ }
+ }
+
+ switch (mode) {
+ case BLADERF_TUNING_MODE_HOST:
+ log_debug("Default tuning mode: host\n");
+ break;
+ case BLADERF_TUNING_MODE_FPGA:
+ log_debug("Default tuning mode: FPGA\n");
+ break;
+ default:
+ assert(!"Bug encountered.");
+ mode = BLADERF_TUNING_MODE_HOST;
+ }
+
+ return mode;
+}
+
+static int bladerf1_apply_lms_dc_cals(struct bladerf *dev)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ int status = 0;
+ struct bladerf_lms_dc_cals cals;
+ const bool have_rx = (board_data->cal.dc_rx != NULL);
+ const bool have_tx = (board_data->cal.dc_tx != NULL);
+
+ cals.lpf_tuning = -1;
+ cals.tx_lpf_i = -1;
+ cals.tx_lpf_q = -1;
+ cals.rx_lpf_i = -1;
+ cals.rx_lpf_q = -1;
+ cals.dc_ref = -1;
+ cals.rxvga2a_i = -1;
+ cals.rxvga2a_q = -1;
+ cals.rxvga2b_i = -1;
+ cals.rxvga2b_q = -1;
+
+ if (have_rx) {
+ const struct bladerf_lms_dc_cals *reg_vals =
+ &board_data->cal.dc_rx->reg_vals;
+
+ cals.lpf_tuning = reg_vals->lpf_tuning;
+ cals.rx_lpf_i = reg_vals->rx_lpf_i;
+ cals.rx_lpf_q = reg_vals->rx_lpf_q;
+ cals.dc_ref = reg_vals->dc_ref;
+ cals.rxvga2a_i = reg_vals->rxvga2a_i;
+ cals.rxvga2a_q = reg_vals->rxvga2a_q;
+ cals.rxvga2b_i = reg_vals->rxvga2b_i;
+ cals.rxvga2b_q = reg_vals->rxvga2b_q;
+
+ log_verbose("Fetched register values from RX DC cal table.\n");
+ }
+
+ if (have_tx) {
+ const struct bladerf_lms_dc_cals *reg_vals =
+ &board_data->cal.dc_tx->reg_vals;
+
+ cals.tx_lpf_i = reg_vals->tx_lpf_i;
+ cals.tx_lpf_q = reg_vals->tx_lpf_q;
+
+ if (have_rx) {
+ if (cals.lpf_tuning != reg_vals->lpf_tuning) {
+ log_warning("LPF tuning mismatch in tables. "
+ "RX=0x%04x, TX=0x%04x",
+ cals.lpf_tuning, reg_vals->lpf_tuning);
+ }
+ } else {
+ /* Have TX cal but no RX cal -- use the RX values that came along
+ * for the ride when the TX table was generated */
+ cals.rx_lpf_i = reg_vals->rx_lpf_i;
+ cals.rx_lpf_q = reg_vals->rx_lpf_q;
+ cals.dc_ref = reg_vals->dc_ref;
+ cals.rxvga2a_i = reg_vals->rxvga2a_i;
+ cals.rxvga2a_q = reg_vals->rxvga2a_q;
+ cals.rxvga2b_i = reg_vals->rxvga2b_i;
+ cals.rxvga2b_q = reg_vals->rxvga2b_q;
+ }
+
+ log_verbose("Fetched register values from TX DC cal table.\n");
+ }
+
+ /* No TX table was loaded, so load LMS TX register cals from the RX table,
+ * if available */
+ if (have_rx && !have_tx) {
+ const struct bladerf_lms_dc_cals *reg_vals =
+ &board_data->cal.dc_rx->reg_vals;
+
+ cals.tx_lpf_i = reg_vals->tx_lpf_i;
+ cals.tx_lpf_q = reg_vals->tx_lpf_q;
+ }
+
+ if (have_rx || have_tx) {
+ status = lms_set_dc_cals(dev, &cals);
+
+ /* Force a re-tune so that we can apply the appropriate I/Q DC offset
+ * values from our calibration table */
+ if (status == 0) {
+ int rx_status = 0;
+ int tx_status = 0;
+
+ if (have_rx) {
+ bladerf_frequency rx_f;
+ rx_status = dev->board->get_frequency(
+ dev, BLADERF_CHANNEL_RX(0), &rx_f);
+ if (rx_status == 0) {
+ rx_status = dev->board->set_frequency(
+ dev, BLADERF_CHANNEL_RX(0), rx_f);
+ }
+ }
+
+ if (have_tx) {
+ bladerf_frequency rx_f;
+ rx_status = dev->board->get_frequency(
+ dev, BLADERF_CHANNEL_RX(0), &rx_f);
+ if (rx_status == 0) {
+ rx_status = dev->board->set_frequency(
+ dev, BLADERF_CHANNEL_RX(0), rx_f);
+ }
+ }
+
+ /* Report the first of any failures */
+ status = (rx_status == 0) ? tx_status : rx_status;
+ if (status != 0) {
+ return status;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Initialize device registers - required after power-up, but safe
+ * to call multiple times after power-up (e.g., multiple close and reopens)
+ */
+static int bladerf1_initialize(struct bladerf *dev)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ struct bladerf_version required_fw_version;
+ struct bladerf_version required_fpga_version;
+ int status;
+ uint32_t val;
+
+ /* Read FPGA version */
+ status = dev->backend->get_fpga_version(dev, &board_data->fpga_version);
+ if (status < 0) {
+ log_debug("Failed to get FPGA version: %s\n",
+ bladerf_strerror(status));
+ return status;
+ }
+ log_verbose("Read FPGA version: %s\n", board_data->fpga_version.describe);
+
+ /* Determine FPGA capabilities */
+ board_data->capabilities |= bladerf1_get_fpga_capabilities(&board_data->fpga_version);
+ log_verbose("Capability mask after FPGA load: 0x%016"PRIx64"\n",
+ board_data->capabilities);
+
+ if (getenv("BLADERF_FORCE_LEGACY_NIOS_PKT")) {
+ board_data->capabilities &= ~BLADERF_CAP_PKT_HANDLER_FMT;
+ log_verbose("Using legacy packet handler format due to env var\n");
+ }
+
+ /* If the FPGA version check fails, just warn, but don't error out.
+ *
+ * If an error code caused this function to bail out, it would prevent a
+ * user from being able to unload and reflash a bitstream being
+ * "autoloaded" from SPI flash. */
+ status = version_check(&bladerf1_fw_compat_table, &bladerf1_fpga_compat_table,
+ &board_data->fw_version, &board_data->fpga_version,
+ &required_fw_version, &required_fpga_version);
+ if (status < 0) {
+#if LOGGING_ENABLED
+ if (status == BLADERF_ERR_UPDATE_FPGA) {
+ log_warning("FPGA v%u.%u.%u was detected. Firmware v%u.%u.%u "
+ "requires FPGA v%u.%u.%u or later. Please load a "
+ "different FPGA version before continuing.\n\n",
+ board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch,
+ board_data->fw_version.major,
+ board_data->fw_version.minor,
+ board_data->fw_version.patch,
+ required_fpga_version.major,
+ required_fpga_version.minor,
+ required_fpga_version.patch);
+ } else if (status == BLADERF_ERR_UPDATE_FW) {
+ log_warning("FPGA v%u.%u.%u was detected, which requires firmware "
+ "v%u.%u.%u or later. The device firmware is currently "
+ "v%u.%u.%u. Please upgrade the device firmware before "
+ "continuing.\n\n",
+ board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch,
+ required_fw_version.major,
+ required_fw_version.minor,
+ required_fw_version.patch,
+ board_data->fw_version.major,
+ board_data->fw_version.minor,
+ board_data->fw_version.patch);
+ }
+#endif
+ }
+
+ /* Detect AGC FPGA bug and report warning */
+ if( have_cap(board_data->capabilities, BLADERF_CAP_AGC_DC_LUT) &&
+ version_fields_less_than(&board_data->fpga_version, 0, 8, 0) ) {
+ log_warning("AGC commands for FPGA v%u.%u.%u are incompatible with "
+ "this version of libbladeRF. Please update to FPGA "
+ "v%u.%u.%u or newer to use AGC.\n",
+ board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch,
+ 0, 8, 0 );
+ }
+
+ /* Set FPGA packet protocol */
+ if (have_cap(board_data->capabilities, BLADERF_CAP_PKT_HANDLER_FMT)) {
+ status = dev->backend->set_fpga_protocol(dev, BACKEND_FPGA_PROTOCOL_NIOSII);
+ } else {
+ status = dev->backend->set_fpga_protocol(dev, BACKEND_FPGA_PROTOCOL_NIOSII_LEGACY);
+ }
+ if (status < 0) {
+ log_error("Unable to set backend FPGA protocol: %d\n", status);
+ return status;
+ }
+
+ /* Readback the GPIO values to see if they are default or already set */
+ status = dev->backend->config_gpio_read(dev, &val);
+ if (status != 0) {
+ log_debug("Failed to read GPIO config %s\n", bladerf_strerror(status));
+ return status;
+ }
+
+ if ((val & 0x7f) == 0) {
+ log_verbose( "Default GPIO value found - initializing device\n" );
+
+ /* Set the GPIO pins to enable the LMS and select the low band */
+ status = dev->backend->config_gpio_write(dev, 0x57);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Disable the front ends */
+ status = lms_enable_rffe(dev, BLADERF_CHANNEL_TX(0), false);
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_enable_rffe(dev, BLADERF_CHANNEL_RX(0), false);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Set the internal LMS register to enable RX and TX */
+ status = LMS_WRITE(dev, 0x05, 0x3e);
+ if (status != 0) {
+ return status;
+ }
+
+ /* LMS FAQ: Improve TX spurious emission performance */
+ status = LMS_WRITE(dev, 0x47, 0x40);
+ if (status != 0) {
+ return status;
+ }
+
+ /* LMS FAQ: Improve ADC performance */
+ status = LMS_WRITE(dev, 0x59, 0x29);
+ if (status != 0) {
+ return status;
+ }
+
+ /* LMS FAQ: Common mode voltage for ADC */
+ status = LMS_WRITE(dev, 0x64, 0x36);
+ if (status != 0) {
+ return status;
+ }
+
+ /* LMS FAQ: Higher LNA Gain */
+ status = LMS_WRITE(dev, 0x79, 0x37);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Power down DC calibration comparators until they are need, as they
+ * have been shown to introduce undesirable artifacts into our signals.
+ * (This is documented in the LMS6 FAQ). */
+
+ status = lms_set(dev, 0x3f, 0x80); /* TX LPF DC cal comparator */
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_set(dev, 0x5f, 0x80); /* RX LPF DC cal comparator */
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_set(dev, 0x6e, 0xc0); /* RXVGA2A/B DC cal comparators */
+ if (status != 0) {
+ return status;
+ }
+
+ /* Configure charge pump current offsets */
+ status = lms_config_charge_pumps(dev, BLADERF_CHANNEL_TX(0));
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_config_charge_pumps(dev, BLADERF_CHANNEL_RX(0));
+ if (status != 0) {
+ return status;
+ }
+
+ /* Set a default samplerate */
+ status = si5338_set_sample_rate(dev, BLADERF_CHANNEL_TX(0), 1000000, NULL);
+ if (status != 0) {
+ return status;
+ }
+
+ status = si5338_set_sample_rate(dev, BLADERF_CHANNEL_RX(0), 1000000, NULL);
+ if (status != 0) {
+ return status;
+ }
+
+ board_data->tuning_mode = tuning_get_default_mode(dev);
+
+ status = dev->board->set_frequency(dev, BLADERF_CHANNEL_TX(0), 2447000000U);
+ if (status != 0) {
+ return status;
+ }
+
+ status = dev->board->set_frequency(dev, BLADERF_CHANNEL_RX(0), 2484000000U);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Set the calibrated VCTCXO DAC value */
+ status = dac161s055_write(dev, board_data->dac_trim);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Set the default gain mode */
+ status = bladerf_set_gain_mode(dev, BLADERF_CHANNEL_RX(0), BLADERF_GAIN_DEFAULT);
+ if (status != 0 && status != BLADERF_ERR_UNSUPPORTED) {
+ return status;
+ }
+ } else {
+ board_data->tuning_mode = tuning_get_default_mode(dev);
+ }
+
+ /* Check if we have an expansion board attached */
+ status = dev->board->expansion_get_attached(dev, &dev->xb);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Update device state */
+ board_data->state = STATE_INITIALIZED;
+
+ /* Set up LMS DC offset register calibration and initial IQ settings,
+ * if any tables have been loaded already.
+ *
+ * This is done every time the device is opened (with an FPGA loaded),
+ * as the user may change/update DC calibration tables without reloading the
+ * FPGA.
+ */
+ status = bladerf1_apply_lms_dc_cals(dev);
+ if (status != 0) {
+ return status;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ * Generic Board Functions *
+ ******************************************************************************/
+
+/******************************************************************************/
+/* Matches */
+/******************************************************************************/
+
+static bool bladerf1_matches(struct bladerf *dev)
+{
+ uint16_t vid, pid;
+ int status;
+
+ status = dev->backend->get_vid_pid(dev, &vid, &pid);
+ if (status < 0) {
+ return false;
+ }
+
+ if (vid == USB_NUAND_VENDOR_ID && pid == USB_NUAND_BLADERF_PRODUCT_ID) {
+ return true;
+ } else if (vid == USB_NUAND_LEGACY_VENDOR_ID && pid == USB_NUAND_BLADERF_LEGACY_PRODUCT_ID) {
+ return true;
+ }
+
+ return false;
+}
+
+/******************************************************************************/
+/* Open/close */
+/******************************************************************************/
+
+static int bladerf1_open(struct bladerf *dev, struct bladerf_devinfo *devinfo)
+{
+ struct bladerf1_board_data *board_data;
+ struct bladerf_version required_fw_version;
+ bladerf_dev_speed usb_speed;
+ char filename[FILENAME_MAX];
+ char *full_path;
+ int status;
+
+ /* Allocate board data */
+ board_data = calloc(1, sizeof(struct bladerf1_board_data));
+ if (board_data == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+ dev->board_data = board_data;
+
+ /* Allocate flash architecture */
+ dev->flash_arch = calloc(1, sizeof(struct bladerf_flash_arch));
+ if (dev->flash_arch == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Initialize board data */
+ board_data->fpga_version.describe = board_data->fpga_version_str;
+ board_data->fw_version.describe = board_data->fw_version_str;
+
+ board_data->module_format[BLADERF_RX] = -1;
+ board_data->module_format[BLADERF_TX] = -1;
+
+ dev->flash_arch->status = STATUS_FLASH_UNINITIALIZED;
+ dev->flash_arch->manufacturer_id = 0x0;
+ dev->flash_arch->device_id = 0x0;
+
+ /* Read firmware version */
+ status = dev->backend->get_fw_version(dev, &board_data->fw_version);
+ if (status < 0) {
+ log_debug("Failed to get FW version: %s\n", bladerf_strerror(status));
+ return status;
+ }
+ log_verbose("Read Firmware version: %s\n", board_data->fw_version.describe);
+
+ /* Update device state */
+ board_data->state = STATE_FIRMWARE_LOADED;
+
+ /* Determine firmware capabilities */
+ board_data->capabilities |=
+ bladerf1_get_fw_capabilities(&board_data->fw_version);
+ log_verbose("Capability mask before FPGA load: 0x%016" PRIx64 "\n",
+ board_data->capabilities);
+
+ /* Wait until firmware is ready */
+ if (have_cap(board_data->capabilities, BLADERF_CAP_QUERY_DEVICE_READY)) {
+ const unsigned int max_retries = 30;
+ unsigned int i;
+ int ready;
+
+ for (i = 0; i < max_retries; i++) {
+ ready = dev->backend->is_fw_ready(dev);
+ if (ready != 1) {
+ if (i == 0) {
+ log_info("Waiting for device to become ready...\n");
+ } else {
+ log_debug("Retry %02u/%02u.\n", i + 1, max_retries);
+ }
+ usleep(1000000);
+ } else {
+ break;
+ }
+ }
+
+ if (i >= max_retries) {
+ log_debug("Timed out while waiting for device.\n");
+ return BLADERF_ERR_TIMEOUT;
+ }
+ } else {
+ log_info(
+ "FX3 FW v%u.%u.%u does not support the \"device ready\" query.\n"
+ "\tEnsure flash-autoloading completes before opening a device.\n"
+ "\tUpgrade the FX3 firmware to avoid this message in the future.\n"
+ "\n",
+ board_data->fw_version.major, board_data->fw_version.minor,
+ board_data->fw_version.patch);
+ }
+
+ /* Determine data message size */
+ status = dev->backend->get_device_speed(dev, &usb_speed);
+ if (status < 0) {
+ log_debug("Failed to get device speed: %s\n", bladerf_strerror(status));
+ return status;
+ }
+ switch (usb_speed) {
+ case BLADERF_DEVICE_SPEED_SUPER:
+ board_data->msg_size = USB_MSG_SIZE_SS;
+ break;
+
+ case BLADERF_DEVICE_SPEED_HIGH:
+ board_data->msg_size = USB_MSG_SIZE_HS;
+ break;
+
+ default:
+ log_error("Unsupported device speed: %d\n", usb_speed);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* Verify that we have a sufficent firmware version before continuing. */
+ status = version_check_fw(&bladerf1_fw_compat_table,
+ &board_data->fw_version, &required_fw_version);
+ if (status != 0) {
+#ifdef LOGGING_ENABLED
+ if (status == BLADERF_ERR_UPDATE_FW) {
+ log_warning("Firmware v%u.%u.%u was detected. libbladeRF v%s "
+ "requires firmware v%u.%u.%u or later. An upgrade via "
+ "the bootloader is required.\n\n",
+ board_data->fw_version.major,
+ board_data->fw_version.minor,
+ board_data->fw_version.patch, LIBBLADERF_VERSION,
+ required_fw_version.major, required_fw_version.minor,
+ required_fw_version.patch);
+ }
+#endif
+ return status;
+ }
+
+ /* Probe SPI flash architecture information */
+ if (have_cap(board_data->capabilities, BLADERF_CAP_FW_FLASH_ID)) {
+ status = spi_flash_read_flash_id(dev, &dev->flash_arch->manufacturer_id,
+ &dev->flash_arch->device_id);
+ if (status < 0) {
+ log_error("Failed to probe SPI flash ID information.\n");
+ }
+ } else {
+ log_debug("FX3 firmware v%u.%u.%u does not support SPI flash ID. A "
+ "firmware update is recommended in order to probe the SPI "
+ "flash ID information.\n",
+ board_data->fw_version.major, board_data->fw_version.minor,
+ board_data->fw_version.patch);
+ }
+
+ /* Decode SPI flash ID information to figure out its architecture.
+ * We need to know a little about the flash architecture before we can
+ * read anything from it, including FPGA size and other cal data.
+ * If the firmware does not have the capability to get the flash ID,
+ * sane defaults will be chosen.
+ *
+ * Not checking return code because it is irrelevant. */
+ spi_flash_decode_flash_architecture(dev, &board_data->fpga_size);
+
+ /* VCTCXO trim and FPGA size are non-fatal indicators that we've
+ * trashed the calibration region of flash. If these were made fatal,
+ * we wouldn't be able to open the device to restore them. */
+
+ status = spi_flash_read_vctcxo_trim(dev, &board_data->dac_trim);
+ if (status < 0) {
+ log_warning("Failed to get VCTCXO trim value: %s\n",
+ bladerf_strerror(status));
+ log_debug("Defaulting DAC trim to 0x8000.\n");
+ board_data->dac_trim = 0x8000;
+ }
+
+ status = spi_flash_read_fpga_size(dev, &board_data->fpga_size);
+ if (status < 0) {
+ log_warning("Failed to get FPGA size %s\n", bladerf_strerror(status));
+ }
+
+ /* If the flash architecture could not be decoded earlier, try again now
+ * that the FPGA size is known. */
+ if (dev->flash_arch->status != STATUS_SUCCESS) {
+ status =
+ spi_flash_decode_flash_architecture(dev, &board_data->fpga_size);
+ if (status < 0) {
+ log_debug("Assumptions were made about the SPI flash architecture! "
+ "Flash commands may not function as expected.\n");
+ }
+ }
+
+ /* Skip further work if BLADERF_FORCE_NO_FPGA_PRESENT is set */
+ if (getenv("BLADERF_FORCE_NO_FPGA_PRESENT")) {
+ log_debug("Skipping FPGA configuration and initialization - "
+ "BLADERF_FORCE_NO_FPGA_PRESENT is set.\n");
+ return 0;
+ }
+
+ /* Check for possible mismatch between the USB device identification and
+ * the board's own knowledge. We need this to be a non-fatal condition,
+ * so that the problem can be fixed easily. */
+ if (board_data->fpga_size == BLADERF_FPGA_A4 ||
+ board_data->fpga_size == BLADERF_FPGA_A5 ||
+ board_data->fpga_size == BLADERF_FPGA_A9) {
+ uint16_t vid, pid;
+
+ log_critical("Device type mismatch! FPGA size %d is a bladeRF2 "
+ "characteristic, but the USB PID indicates bladeRF1. "
+ "Initialization cannot continue.\n",
+ board_data->fpga_size);
+ log_info("You must download firmware v2.2.0 or later from "
+ "https://www.nuand.com/fx3/ and flash it (bladeRF-cli -f "
+ "/path/to/bladeRF_fw.img) before using this device.\n");
+
+ status = dev->backend->get_vid_pid(dev, &vid, &pid);
+ if (status < 0) {
+ log_error("%s: get_vid_pid returned status %s\n", __FUNCTION__,
+ bladerf_strerror(status));
+ }
+
+ log_debug("vid_pid=%04x:%04x fpga_size=%d fw_version=%u.%u.%u\n", vid,
+ pid, board_data->fpga_size, board_data->fw_version.major,
+ board_data->fw_version.minor, board_data->fw_version.patch);
+
+ log_warning("Skipping further initialization...\n");
+ return 0;
+ }
+
+ /* This will be set in initialize() after we can determine which
+ * methods the FPGA supports (based upon version number). */
+ board_data->tuning_mode = BLADERF_TUNING_MODE_INVALID;
+
+ /* Load any available calibration tables so that the LMS DC register
+ * configurations may be loaded in initialize() */
+ snprintf(filename, sizeof(filename), "%s_dc_rx.tbl", dev->ident.serial);
+ full_path = file_find(filename);
+ if (full_path != NULL) {
+ log_debug("Loading RX calibration image %s\n", full_path);
+ dc_cal_tbl_image_load(dev, &board_data->cal.dc_rx, full_path);
+ }
+ free(full_path);
+ full_path = NULL;
+
+ snprintf(filename, sizeof(filename), "%s_dc_tx.tbl", dev->ident.serial);
+ full_path = file_find(filename);
+ if (full_path != NULL) {
+ log_debug("Loading TX calibration image %s\n", full_path);
+ dc_cal_tbl_image_load(dev, &board_data->cal.dc_tx, full_path);
+ }
+ free(full_path);
+ full_path = NULL;
+
+ status = dev->backend->is_fpga_configured(dev);
+ if (status < 0) {
+ return status;
+ } else if (status == 1) {
+ board_data->state = STATE_FPGA_LOADED;
+ } else if (status != 1 && board_data->fpga_size == BLADERF_FPGA_UNKNOWN) {
+ log_warning("Unknown FPGA size. Skipping FPGA configuration...\n");
+ log_warning("Skipping further initialization...\n");
+ return 0;
+ } else if (status != 1) {
+ /* Try searching for an FPGA in the config search path */
+ if (board_data->fpga_size == BLADERF_FPGA_40KLE) {
+ full_path = file_find("hostedx40.rbf");
+ } else if (board_data->fpga_size == BLADERF_FPGA_115KLE) {
+ full_path = file_find("hostedx115.rbf");
+ } else {
+ log_error("Invalid FPGA size %d.\n", board_data->fpga_size);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ if (full_path != NULL) {
+ uint8_t *buf;
+ size_t buf_size;
+
+ log_debug("Loading FPGA from: %s\n", full_path);
+
+ status = file_read_buffer(full_path, &buf, &buf_size);
+
+ free(full_path);
+ full_path = NULL;
+
+ if (status != 0) {
+ return status;
+ }
+
+ status = dev->backend->load_fpga(dev, buf, buf_size);
+ if (status != 0) {
+ log_warning("Failure loading FPGA: %s\n",
+ bladerf_strerror(status));
+ return status;
+ }
+
+ board_data->state = STATE_FPGA_LOADED;
+ } else {
+ log_warning("FPGA bitstream file not found.\n");
+ log_warning("Skipping further initialization...\n");
+ return 0;
+ }
+ }
+
+ /* Initialize the device before we try to interact with it. In the case
+ * of an autoloaded FPGA, we need to ensure the clocks are all running
+ * before we can try to cancel any scheduled retunes or else the NIOS
+ * hangs. */
+ status = bladerf1_initialize(dev);
+ if (status != 0) {
+ return status;
+ }
+
+ if (have_cap(board_data->capabilities, BLADERF_CAP_SCHEDULED_RETUNE)) {
+ /* Cancel any pending re-tunes that may have been left over as the
+ * result of a user application crashing or forgetting to call
+ * bladerf_close() */
+ status =
+ dev->board->cancel_scheduled_retunes(dev, BLADERF_CHANNEL_RX(0));
+ if (status != 0) {
+ log_warning("Failed to cancel any pending RX retunes: %s\n",
+ bladerf_strerror(status));
+ return status;
+ }
+
+ status =
+ dev->board->cancel_scheduled_retunes(dev, BLADERF_CHANNEL_TX(0));
+ if (status != 0) {
+ log_warning("Failed to cancel any pending TX retunes: %s\n",
+ bladerf_strerror(status));
+ return status;
+ }
+ }
+
+ return 0;
+}
+
+static void bladerf1_close(struct bladerf *dev)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ struct bladerf_flash_arch *flash_arch = dev->flash_arch;
+ int status;
+
+ if (board_data) {
+ sync_deinit(&board_data->sync[BLADERF_CHANNEL_RX(0)]);
+ sync_deinit(&board_data->sync[BLADERF_CHANNEL_TX(0)]);
+
+ status = dev->backend->is_fpga_configured(dev);
+ if (status == 1 && have_cap(board_data->capabilities, BLADERF_CAP_SCHEDULED_RETUNE)) {
+ /* We cancel schedule retunes here to avoid the device retuning
+ * underneath the user, should they open it again in the future.
+ *
+ * This is intended to help developers avoid a situation during
+ * debugging where they schedule "far" into the future, but then
+ * hit a case where their program abort or exit early. If we didn't
+ * cancel these scheduled retunes, they could potentially be left
+ * wondering why the device is starting up or "unexpectedly"
+ * switching to a different frequency later.
+ */
+ dev->board->cancel_scheduled_retunes(dev, BLADERF_CHANNEL_RX(0));
+ dev->board->cancel_scheduled_retunes(dev, BLADERF_CHANNEL_TX(0));
+ }
+
+ /* Detach expansion board */
+ switch (dev->xb) {
+ case BLADERF_XB_100:
+ xb100_detach(dev);
+ break;
+ case BLADERF_XB_200:
+ xb200_detach(dev);
+ break;
+ case BLADERF_XB_300:
+ xb300_detach(dev);
+ break;
+ default:
+ break;
+ }
+
+ dc_cal_tbl_free(&board_data->cal.dc_rx);
+ dc_cal_tbl_free(&board_data->cal.dc_tx);
+
+ free(board_data);
+ board_data = NULL;
+ }
+
+ if( flash_arch != NULL ) {
+ free(flash_arch);
+ flash_arch = NULL;
+ }
+}
+
+/******************************************************************************/
+/* Properties */
+/******************************************************************************/
+
+static bladerf_dev_speed bladerf1_device_speed(struct bladerf *dev)
+{
+ int status;
+ bladerf_dev_speed usb_speed;
+
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ status = dev->backend->get_device_speed(dev, &usb_speed);
+ if (status < 0) {
+ return BLADERF_DEVICE_SPEED_UNKNOWN;
+ }
+
+ return usb_speed;
+}
+
+static int bladerf1_get_serial(struct bladerf *dev, char *serial)
+{
+ strcpy(serial, dev->ident.serial);
+
+ return 0;
+}
+
+static int bladerf1_get_fpga_size(struct bladerf *dev, bladerf_fpga_size *size)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ *size = board_data->fpga_size;
+
+ return 0;
+}
+
+static int bladerf1_get_fpga_bytes(struct bladerf *dev, size_t *size)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ switch (board_data->fpga_size) {
+ case BLADERF_FPGA_40KLE:
+ *size = 1191788;
+ break;
+
+ case BLADERF_FPGA_115KLE:
+ *size = 3571462;
+ break;
+
+ default:
+ log_debug("%s: unknown fpga_size: %x\n", board_data->fpga_size);
+ return BLADERF_ERR_INVAL;
+ }
+
+ return 0;
+}
+
+static int bladerf1_get_flash_size(struct bladerf *dev, uint32_t *size, bool *is_guess)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ *size = dev->flash_arch->tsize_bytes;
+ *is_guess = (dev->flash_arch->status != STATUS_SUCCESS);
+
+ return 0;
+}
+
+static int bladerf1_is_fpga_configured(struct bladerf *dev)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ return dev->backend->is_fpga_configured(dev);
+}
+
+static int bladerf1_get_fpga_source(struct bladerf *dev,
+ bladerf_fpga_source *source)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_FW_FPGA_SOURCE)) {
+ log_debug("%s: not supported by firmware\n", __FUNCTION__);
+ *source = BLADERF_FPGA_SOURCE_UNKNOWN;
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ *source = dev->backend->get_fpga_source(dev);
+
+ return 0;
+}
+
+static uint64_t bladerf1_get_capabilities(struct bladerf *dev)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ return board_data->capabilities;
+}
+
+static size_t bladerf1_get_channel_count(struct bladerf *dev,
+ bladerf_direction dir)
+{
+ return 1;
+}
+
+/******************************************************************************/
+/* Versions */
+/******************************************************************************/
+
+static int bladerf1_get_fpga_version(struct bladerf *dev, struct bladerf_version *version)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ memcpy(version, &board_data->fpga_version, sizeof(*version));
+
+ return 0;
+}
+
+static int bladerf1_get_fw_version(struct bladerf *dev, struct bladerf_version *version)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ memcpy(version, &board_data->fw_version, sizeof(*version));
+
+ return 0;
+}
+
+/******************************************************************************/
+/* Enable/disable */
+/******************************************************************************/
+
+static int perform_format_deconfig(struct bladerf *dev, bladerf_direction dir);
+
+static int bladerf1_enable_module(struct bladerf *dev,
+ bladerf_channel ch,
+ bool enable)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ int status;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_TX(0)) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ log_debug("Enable channel: %s - %s\n",
+ BLADERF_CHANNEL_IS_TX(ch) ? "TX" : "RX",
+ enable ? "True" : "False");
+
+ if (enable == false) {
+ sync_deinit(&board_data->sync[ch]);
+ perform_format_deconfig(
+ dev, BLADERF_CHANNEL_IS_TX(ch) ? BLADERF_TX : BLADERF_RX);
+ }
+
+ lms_enable_rffe(dev, ch, enable);
+ status = dev->backend->enable_module(
+ dev, BLADERF_CHANNEL_IS_TX(ch) ? BLADERF_TX : BLADERF_RX, enable);
+
+ return status;
+}
+
+/******************************************************************************/
+/* Gain */
+/******************************************************************************/
+
+/**
+ * @brief applies overall_gain to stage_gain, within the range max
+ *
+ * "Moves" gain from overall_gain to stage_gain, ensuring that overall_gain
+ * doesn't go negative and stage_gain doesn't exceed range->max.
+ *
+ * @param[in] range The range for stage_gain
+ * @param stage_gain The stage gain
+ * @param overall_gain The overall gain
+ */
+static void _apportion_gain(struct bladerf_range const *range,
+ int *stage_gain,
+ int *overall_gain)
+{
+ int headroom = __unscale_int(range, range->max) - *stage_gain;
+ int allotment = (headroom >= *overall_gain) ? *overall_gain : headroom;
+
+ /* Enforce step size */
+ while (0 != (allotment % range->step)) {
+ --allotment;
+ }
+
+ *stage_gain += allotment;
+ *overall_gain -= allotment;
+}
+
+static bladerf_lna_gain _convert_gain_to_lna_gain(int gain)
+{
+ if (gain >= BLADERF_LNA_GAIN_MAX_DB) {
+ return BLADERF_LNA_GAIN_MAX;
+ }
+
+ if (gain >= BLADERF_LNA_GAIN_MID_DB) {
+ return BLADERF_LNA_GAIN_MID;
+ }
+
+ return BLADERF_LNA_GAIN_BYPASS;
+}
+
+static int _convert_lna_gain_to_gain(bladerf_lna_gain lnagain)
+{
+ switch (lnagain) {
+ case BLADERF_LNA_GAIN_MAX:
+ return BLADERF_LNA_GAIN_MAX_DB;
+ case BLADERF_LNA_GAIN_MID:
+ return BLADERF_LNA_GAIN_MID_DB;
+ case BLADERF_LNA_GAIN_BYPASS:
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+static int bladerf1_get_gain_stage_range(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ struct bladerf_range const **range)
+{
+ struct bladerf_gain_stage_info const *stage_infos;
+ unsigned int stage_infos_len;
+ size_t i;
+
+ if (NULL == stage) {
+ log_error("%s: stage is null\n", __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ stage_infos = bladerf1_tx_gain_stages;
+ stage_infos_len = ARRAY_SIZE(bladerf1_tx_gain_stages);
+ } else {
+ stage_infos = bladerf1_rx_gain_stages;
+ stage_infos_len = ARRAY_SIZE(bladerf1_rx_gain_stages);
+ }
+
+ for (i = 0; i < stage_infos_len; i++) {
+ if (strcmp(stage_infos[i].name, stage) == 0) {
+ if (NULL != range) {
+ *range = &(stage_infos[i].range);
+ }
+ return 0;
+ }
+ }
+
+ return BLADERF_ERR_INVAL;
+}
+
+static int set_rx_gain(struct bladerf *dev, int gain)
+{
+ struct bladerf_range const *lna_range = NULL;
+ struct bladerf_range const *rxvga1_range = NULL;
+ struct bladerf_range const *rxvga2_range = NULL;
+ bladerf_channel const ch = BLADERF_CHANNEL_RX(0);
+ int orig_gain = gain;
+ int lna, rxvga1, rxvga2;
+ int status;
+
+ // get our gain stage ranges!
+ status = bladerf1_get_gain_stage_range(dev, ch, "lna", &lna_range);
+ if (status < 0) {
+ return status;
+ }
+
+ status = bladerf1_get_gain_stage_range(dev, ch, "rxvga1", &rxvga1_range);
+ if (status < 0) {
+ return status;
+ }
+
+ status = bladerf1_get_gain_stage_range(dev, ch, "rxvga2", &rxvga2_range);
+ if (status < 0) {
+ return status;
+ }
+
+ lna = __unscale_int(lna_range, lna_range->min);
+ rxvga1 = __unscale_int(rxvga1_range, rxvga1_range->min);
+ rxvga2 = __unscale_int(rxvga2_range, rxvga2_range->min);
+
+ // offset gain so that we can use it as a counter when apportioning gain
+ gain -= __round_int((BLADERF1_RX_GAIN_OFFSET +
+ __unscale_int(lna_range, lna_range->min) +
+ __unscale_int(rxvga1_range, rxvga1_range->min) +
+ __unscale_int(rxvga2_range, rxvga2_range->min)));
+
+ // apportion some gain to RXLNA (but only half of it for now)
+ _apportion_gain(lna_range, &lna, &gain);
+ if (lna > BLADERF_LNA_GAIN_MID_DB) {
+ gain += (lna - BLADERF_LNA_GAIN_MID_DB);
+ lna -= (lna - BLADERF_LNA_GAIN_MID_DB);
+ }
+
+ // apportion gain to RXVGA1
+ _apportion_gain(rxvga1_range, &rxvga1, &gain);
+
+ // apportion more gain to RXLNA
+ _apportion_gain(lna_range, &lna, &gain);
+
+ // apportion gain to RXVGA2
+ _apportion_gain(rxvga2_range, &rxvga2, &gain);
+
+ // if we still have remaining gain, it's because rxvga2 has a step size of
+ // 3 dB. Steal a few dB from rxvga1...
+ if (gain > 0 && rxvga1 >= __unscale_int(rxvga1_range, rxvga1_range->max)) {
+ rxvga1 -= __unscale_int(rxvga2_range, rxvga2_range->step);
+ gain += __unscale_int(rxvga2_range, rxvga2_range->step);
+
+ _apportion_gain(rxvga2_range, &rxvga2, &gain);
+ _apportion_gain(rxvga1_range, &rxvga1, &gain);
+ }
+
+ // verification
+ if (gain != 0) {
+ log_warning("%s: unable to achieve requested gain %d (missed by %d)\n",
+ __FUNCTION__, orig_gain, gain);
+ log_debug("%s: gain=%d -> rxvga1=%d lna=%d rxvga2=%d remainder=%d\n",
+ __FUNCTION__, orig_gain, rxvga1, lna, rxvga2, gain);
+ }
+
+ // that should do it. actually apply the changes:
+ status = lms_lna_set_gain(dev, _convert_gain_to_lna_gain(lna));
+ if (status < 0) {
+ return status;
+ }
+
+ status = lms_rxvga1_set_gain(dev, __scale_int(rxvga1_range, rxvga1));
+ if (status < 0) {
+ return status;
+ }
+
+ status = lms_rxvga2_set_gain(dev, __scale_int(rxvga2_range, rxvga2));
+ if (status < 0) {
+ return status;
+ }
+
+ return 0;
+}
+
+static int get_rx_gain(struct bladerf *dev, int *gain)
+{
+ int status;
+ bladerf_lna_gain lnagain;
+ int lnagain_db;
+ int rxvga1;
+ int rxvga2;
+
+ status = lms_lna_get_gain(dev, &lnagain);
+ if (status < 0) {
+ return status;
+ }
+
+ status = lms_rxvga1_get_gain(dev, &rxvga1);
+ if (status < 0) {
+ return status;
+ }
+
+ status = lms_rxvga2_get_gain(dev, &rxvga2);
+ if (status < 0) {
+ return status;
+ }
+
+ switch (lnagain) {
+ case BLADERF_LNA_GAIN_BYPASS:
+ lnagain_db = 0;
+ break;
+
+ case BLADERF_LNA_GAIN_MID:
+ lnagain_db = BLADERF_LNA_GAIN_MID_DB;
+ break;
+
+ case BLADERF_LNA_GAIN_MAX:
+ lnagain_db = BLADERF_LNA_GAIN_MAX_DB;
+ break;
+
+ default:
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ *gain = __round_int(lnagain_db + rxvga1 + rxvga2 + BLADERF1_RX_GAIN_OFFSET);
+
+ return 0;
+}
+
+static int set_tx_gain(struct bladerf *dev, int gain)
+{
+ struct bladerf_range const *txvga1_range = NULL;
+ struct bladerf_range const *txvga2_range = NULL;
+ bladerf_channel const ch = BLADERF_CHANNEL_TX(0);
+ int orig_gain = gain;
+ int txvga1, txvga2;
+ int status;
+
+ // get our gain stage ranges!
+ status = bladerf1_get_gain_stage_range(dev, ch, "txvga1", &txvga1_range);
+ if (status < 0) {
+ return status;
+ }
+
+ status = bladerf1_get_gain_stage_range(dev, ch, "txvga2", &txvga2_range);
+ if (status < 0) {
+ return status;
+ }
+
+ txvga1 = __unscale_int(txvga1_range, txvga1_range->min);
+ txvga2 = __unscale_int(txvga2_range, txvga2_range->min);
+
+ // offset gain so that we can use it as a counter when apportioning gain
+ gain -= __round_int((BLADERF1_TX_GAIN_OFFSET +
+ __unscale_int(txvga1_range, txvga1_range->min) +
+ __unscale_int(txvga2_range, txvga2_range->min)));
+
+ // apportion gain to TXVGA2
+ _apportion_gain(txvga2_range, &txvga2, &gain);
+
+ // apportion gain to TXVGA1
+ _apportion_gain(txvga1_range, &txvga1, &gain);
+
+ // verification
+ if (gain != 0) {
+ log_warning("%s: unable to achieve requested gain %d (missed by %d)\n",
+ __FUNCTION__, orig_gain, gain);
+ log_debug("%s: gain=%d -> txvga2=%d txvga1=%d remainder=%d\n",
+ __FUNCTION__, orig_gain, txvga2, txvga1, gain);
+ }
+
+ status = lms_txvga1_set_gain(dev, txvga1);
+ if (status < 0) {
+ return status;
+ }
+
+ status = lms_txvga2_set_gain(dev, txvga2);
+ if (status < 0) {
+ return status;
+ }
+
+ return 0;
+}
+
+static int get_tx_gain(struct bladerf *dev, int *gain)
+{
+ int status;
+ int txvga1;
+ int txvga2;
+
+ status = lms_txvga1_get_gain(dev, &txvga1);
+ if (status < 0) {
+ return status;
+ }
+
+ status = lms_txvga2_get_gain(dev, &txvga2);
+ if (status < 0) {
+ return status;
+ }
+
+ *gain = __round_int(txvga1 + txvga2 + BLADERF1_TX_GAIN_OFFSET);
+
+ return 0;
+}
+
+static int bladerf1_set_gain(struct bladerf *dev, bladerf_channel ch, int gain)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (BLADERF_CHANNEL_TX(0) == ch) {
+ return set_tx_gain(dev, gain);
+ } else if (BLADERF_CHANNEL_RX(0) == ch) {
+ return set_rx_gain(dev, gain);
+ }
+
+ return BLADERF_ERR_INVAL;
+}
+
+static int bladerf1_set_gain_mode(struct bladerf *dev,
+ bladerf_module mod,
+ bladerf_gain_mode mode)
+{
+ int status;
+ uint32_t config_gpio;
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ uint32_t const TABLE_VERSION = 2; /* Required RX DC cal table version */
+
+ /* Strings used in AGC-unavailable warnings */
+ char const *MGC_WARN = "Manual gain control will be used instead.";
+ char const *FPGA_STR = "download and install FPGA v0.7.0 or newer from "
+ "https://nuand.com/fpga/";
+ char const *DCCAL_STR = "see \"Generating a DC offset table\" at "
+ "https://github.com/Nuand/bladeRF/wiki/"
+ "DC-offset-and-IQ-Imbalance-Correction";
+
+ if (mod != BLADERF_MODULE_RX) {
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ if ((status = dev->backend->config_gpio_read(dev, &config_gpio))) {
+ return status;
+ }
+
+ if (mode == BLADERF_GAIN_AUTOMATIC || mode == BLADERF_GAIN_DEFAULT) {
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_AGC_DC_LUT)) {
+ log_warning("AGC not supported by FPGA. %s\n", MGC_WARN);
+ log_info("To enable AGC, %s, then %s\n", FPGA_STR, DCCAL_STR);
+ log_debug("%s: expected FPGA >= v0.7.0, got v%u.%u.%u\n",
+ __FUNCTION__, board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ if (!board_data->cal.dc_rx) {
+ log_warning("RX DC calibration table not found. %s\n", MGC_WARN);
+ log_info("To enable AGC, %s\n", DCCAL_STR);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ if (board_data->cal.dc_rx->version != TABLE_VERSION) {
+ log_warning("RX DC calibration table is out-of-date. %s\n",
+ MGC_WARN);
+ log_info("To enable AGC, %s\n", DCCAL_STR);
+ log_debug("%s: expected version %u, got %u\n", __FUNCTION__,
+ TABLE_VERSION, board_data->cal.dc_rx->version);
+
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ config_gpio |= BLADERF_GPIO_AGC_ENABLE;
+ } else if (mode == BLADERF_GAIN_MANUAL || mode == BLADERF_GAIN_MGC) {
+ config_gpio &= ~BLADERF_GPIO_AGC_ENABLE;
+ }
+
+ return dev->backend->config_gpio_write(dev, config_gpio);
+}
+
+static int bladerf1_get_gain_mode(struct bladerf *dev,
+ bladerf_module mod,
+ bladerf_gain_mode *mode)
+{
+ int status;
+ uint32_t config_gpio;
+
+ if ((status = dev->backend->config_gpio_read(dev, &config_gpio))) {
+ return status;
+ }
+
+ if (config_gpio & BLADERF_GPIO_AGC_ENABLE) {
+ *mode = BLADERF_GAIN_DEFAULT;
+ } else {
+ *mode = BLADERF_GAIN_MGC;
+ }
+
+ return status;
+}
+
+static int bladerf1_get_gain(struct bladerf *dev, bladerf_channel ch, int *gain)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (BLADERF_CHANNEL_TX(0) == ch) {
+ return get_tx_gain(dev, gain);
+ } else if (BLADERF_CHANNEL_RX(0) == ch) {
+ return get_rx_gain(dev, gain);
+ }
+
+ return BLADERF_ERR_INVAL;
+}
+
+static int bladerf1_get_gain_modes(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_gain_modes const **modes)
+{
+ struct bladerf_gain_modes const *mode_infos;
+ unsigned int mode_infos_len;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ mode_infos = NULL;
+ mode_infos_len = 0;
+ } else {
+ mode_infos = bladerf1_rx_gain_modes;
+ mode_infos_len = ARRAY_SIZE(bladerf1_rx_gain_modes);
+ }
+
+ if (modes != NULL) {
+ *modes = mode_infos;
+ }
+
+ return mode_infos_len;
+}
+
+static int bladerf1_get_gain_range(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_range const **range)
+{
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ *range = &bladerf1_tx_gain_range;
+ } else {
+ *range = &bladerf1_rx_gain_range;
+ }
+
+ return 0;
+}
+
+static int bladerf1_set_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ int gain)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ /* TODO implement gain clamping */
+
+ switch (ch) {
+ case BLADERF_CHANNEL_TX(0):
+ if (strcmp(stage, "txvga1") == 0) {
+ return lms_txvga1_set_gain(dev, gain);
+ } else if (strcmp(stage, "txvga2") == 0) {
+ return lms_txvga2_set_gain(dev, gain);
+ } else {
+ log_warning("%s: gain stage '%s' invalid\n", __FUNCTION__,
+ stage);
+ return 0;
+ }
+
+ case BLADERF_CHANNEL_RX(0):
+ if (strcmp(stage, "rxvga1") == 0) {
+ return lms_rxvga1_set_gain(dev, gain);
+ } else if (strcmp(stage, "rxvga2") == 0) {
+ return lms_rxvga2_set_gain(dev, gain);
+ } else if (strcmp(stage, "lna") == 0) {
+ return lms_lna_set_gain(dev, _convert_gain_to_lna_gain(gain));
+ } else {
+ log_warning("%s: gain stage '%s' invalid\n", __FUNCTION__,
+ stage);
+ return 0;
+ }
+
+ default:
+ log_error("%s: channel %d invalid\n", __FUNCTION__, ch);
+ return BLADERF_ERR_INVAL;
+ }
+}
+
+static int bladerf1_get_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ int *gain)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ switch (ch) {
+ case BLADERF_CHANNEL_TX(0):
+ if (strcmp(stage, "txvga1") == 0) {
+ return lms_txvga1_get_gain(dev, gain);
+ } else if (strcmp(stage, "txvga2") == 0) {
+ return lms_txvga2_get_gain(dev, gain);
+ } else {
+ log_warning("%s: gain stage '%s' invalid\n", __FUNCTION__,
+ stage);
+ return 0;
+ }
+
+ case BLADERF_CHANNEL_RX(0):
+ if (strcmp(stage, "rxvga1") == 0) {
+ return lms_rxvga1_get_gain(dev, gain);
+ } else if (strcmp(stage, "rxvga2") == 0) {
+ return lms_rxvga2_get_gain(dev, gain);
+ } else if (strcmp(stage, "lna") == 0) {
+ int status;
+ bladerf_lna_gain lnagain;
+ status = lms_lna_get_gain(dev, &lnagain);
+ if (status == 0) {
+ *gain = _convert_lna_gain_to_gain(lnagain);
+ }
+ return status;
+ } else {
+ log_warning("%s: gain stage '%s' invalid\n", __FUNCTION__,
+ stage);
+ return 0;
+ }
+
+ default:
+ log_error("%s: channel %d invalid\n", __FUNCTION__, ch);
+ return BLADERF_ERR_INVAL;
+ }
+}
+
+static int bladerf1_get_gain_stages(struct bladerf *dev,
+ bladerf_channel ch,
+ char const **stages,
+ size_t count)
+{
+ struct bladerf_gain_stage_info const *stage_infos;
+ unsigned int stage_infos_len;
+ unsigned int i;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ stage_infos = bladerf1_tx_gain_stages;
+ stage_infos_len = ARRAY_SIZE(bladerf1_tx_gain_stages);
+ } else {
+ stage_infos = bladerf1_rx_gain_stages;
+ stage_infos_len = ARRAY_SIZE(bladerf1_rx_gain_stages);
+ }
+
+ if (stages != NULL) {
+ count = (stage_infos_len < count) ? stage_infos_len : count;
+
+ for (i = 0; i < count; i++) {
+ stages[i] = stage_infos[i].name;
+ }
+ }
+
+ return stage_infos_len;
+}
+
+/******************************************************************************/
+/* Sample Rate */
+/******************************************************************************/
+
+static int bladerf1_set_sample_rate(struct bladerf *dev, bladerf_channel ch, unsigned int rate, unsigned int *actual)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ return si5338_set_sample_rate(dev, ch, rate, actual);
+}
+
+static int bladerf1_get_sample_rate(struct bladerf *dev, bladerf_channel ch, unsigned int *rate)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ return si5338_get_sample_rate(dev, ch, rate);
+}
+
+static int bladerf1_get_sample_rate_range(struct bladerf *dev, bladerf_channel ch, const struct bladerf_range **range)
+{
+ *range = &bladerf1_sample_rate_range;
+ return 0;
+}
+
+static int bladerf1_set_rational_sample_rate(struct bladerf *dev, bladerf_channel ch, struct bladerf_rational_rate *rate, struct bladerf_rational_rate *actual)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ return si5338_set_rational_sample_rate(dev, ch, rate, actual);
+}
+
+static int bladerf1_get_rational_sample_rate(struct bladerf *dev, bladerf_channel ch, struct bladerf_rational_rate *rate)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ return si5338_get_rational_sample_rate(dev, ch, rate);
+}
+
+/******************************************************************************/
+/* Bandwidth */
+/******************************************************************************/
+
+static int bladerf1_set_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth bandwidth,
+ bladerf_bandwidth *actual)
+{
+ int status;
+ lms_bw bw;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (bandwidth < BLADERF_BANDWIDTH_MIN) {
+ bandwidth = BLADERF_BANDWIDTH_MIN;
+ log_info("Clamping bandwidth to %d Hz\n", bandwidth);
+ } else if (bandwidth > BLADERF_BANDWIDTH_MAX) {
+ bandwidth = BLADERF_BANDWIDTH_MAX;
+ log_info("Clamping bandwidth to %d Hz\n", bandwidth);
+ }
+
+ bw = lms_uint2bw(bandwidth);
+
+ status = lms_lpf_enable(dev, ch, true);
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_set_bandwidth(dev, ch, bw);
+ if (actual != NULL) {
+ if (status == 0) {
+ *actual = lms_bw2uint(bw);
+ } else {
+ *actual = 0;
+ }
+ }
+
+ return status;
+}
+
+static int bladerf1_get_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth *bandwidth)
+{
+ int status;
+ lms_bw bw;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ status = lms_get_bandwidth(dev, ch, &bw);
+ if (status == 0) {
+ *bandwidth = lms_bw2uint(bw);
+ } else {
+ *bandwidth = 0;
+ }
+
+ return status;
+}
+
+static int bladerf1_get_bandwidth_range(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range)
+{
+ *range = &bladerf1_bandwidth_range;
+ return 0;
+}
+
+/******************************************************************************/
+/* Frequency */
+/******************************************************************************/
+
+static int bladerf1_set_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ const bladerf_xb attached = dev->xb;
+ int status;
+ int16_t dc_i, dc_q;
+ struct dc_cal_entry entry;
+ const struct dc_cal_tbl *dc_cal = (ch == BLADERF_CHANNEL_RX(0))
+ ? board_data->cal.dc_rx
+ : board_data->cal.dc_tx;
+
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ log_debug("Setting %s frequency to %" BLADERF_PRIuFREQ "\n",
+ channel2str(ch), frequency);
+
+ if (attached == BLADERF_XB_200) {
+ if (frequency < BLADERF_FREQUENCY_MIN) {
+ status = xb200_set_path(dev, ch, BLADERF_XB200_MIX);
+ if (status) {
+ return status;
+ }
+
+ status = xb200_auto_filter_selection(dev, ch, frequency);
+ if (status) {
+ return status;
+ }
+
+ frequency = 1248000000 - frequency;
+ } else {
+ status = xb200_set_path(dev, ch, BLADERF_XB200_BYPASS);
+ if (status) {
+ return status;
+ }
+ }
+ }
+
+ switch (board_data->tuning_mode) {
+ case BLADERF_TUNING_MODE_HOST:
+ status = lms_set_frequency(dev, ch, (uint32_t)frequency);
+ if (status != 0) {
+ return status;
+ }
+
+ status = band_select(dev, ch, frequency < BLADERF1_BAND_HIGH);
+ break;
+
+ case BLADERF_TUNING_MODE_FPGA: {
+ status = dev->board->schedule_retune(dev, ch, BLADERF_RETUNE_NOW,
+ frequency, NULL);
+ break;
+ }
+
+ default:
+ log_debug("Invalid tuning mode: %d\n", board_data->tuning_mode);
+ status = BLADERF_ERR_INVAL;
+ break;
+ }
+ if (status != 0) {
+ return status;
+ }
+
+ if (dc_cal != NULL) {
+ dc_cal_tbl_entry(dc_cal, (uint32_t)frequency, &entry);
+
+ dc_i = entry.dc_i;
+ dc_q = entry.dc_q;
+
+ status = lms_set_dc_offset_i(dev, ch, dc_i);
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_set_dc_offset_q(dev, ch, dc_q);
+ if (status != 0) {
+ return status;
+ }
+
+ if (ch == BLADERF_CHANNEL_RX(0) &&
+ have_cap(board_data->capabilities, BLADERF_CAP_AGC_DC_LUT)) {
+ status = dev->backend->set_agc_dc_correction(
+ dev, entry.max_dc_q, entry.max_dc_i, entry.mid_dc_q,
+ entry.mid_dc_i, entry.min_dc_q, entry.min_dc_i);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose("Set AGC DC offset cal (I, Q) to: Max (%d, %d) "
+ " Mid (%d, %d) Min (%d, %d)\n",
+ entry.max_dc_q, entry.max_dc_i, entry.mid_dc_q,
+ entry.mid_dc_i, entry.min_dc_q, entry.min_dc_i);
+ }
+
+ log_verbose("Set %s DC offset cal (I, Q) to: (%d, %d)\n",
+ (ch == BLADERF_CHANNEL_RX(0)) ? "RX" : "TX", dc_i, dc_q);
+ }
+
+ return 0;
+}
+
+static int bladerf1_get_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency *frequency)
+{
+ bladerf_xb200_path path;
+ struct lms_freq f;
+ int status = 0;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ status = lms_get_frequency(dev, ch, &f);
+ if (status != 0) {
+ return status;
+ }
+
+ if (f.x == 0) {
+ /* If we see this, it's most often an indication that communication
+ * with the LMS6002D is not occuring correctly */
+ *frequency = 0;
+ status = BLADERF_ERR_IO;
+ } else {
+ *frequency = lms_frequency_to_hz(&f);
+ }
+ if (status != 0) {
+ return status;
+ }
+
+ if (dev->xb == BLADERF_XB_200) {
+ status = xb200_get_path(dev, ch, &path);
+ if (status != 0) {
+ return status;
+ }
+ if (path == BLADERF_XB200_MIX) {
+ *frequency = 1248000000 - *frequency;
+ }
+ }
+
+ return 0;
+}
+
+static int bladerf1_get_frequency_range(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range)
+{
+ if (dev->xb == BLADERF_XB_200) {
+ *range = &bladerf1_xb200_frequency_range;
+ } else {
+ *range = &bladerf1_frequency_range;
+ }
+
+ return 0;
+}
+
+static int bladerf1_select_band(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ return band_select(dev, ch, frequency < BLADERF1_BAND_HIGH);
+}
+
+/******************************************************************************/
+/* RF ports */
+/******************************************************************************/
+
+static int bladerf1_set_rf_port(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *port)
+{
+ const struct bladerf_lms_port_name_map *port_map;
+ unsigned int port_map_len;
+ int status;
+ size_t i;
+
+ lms_lna rx_lna = LNA_NONE;
+ lms_pa tx_pa = PA_NONE;
+ bool ok = false;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ /* TODO: lms_pa_enable is not currently implemented */
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ log_debug("%s: not implemented for TX channels, silently ignoring\n",
+ __FUNCTION__);
+ return 0;
+ }
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ port_map = bladerf1_tx_port_map;
+ port_map_len = ARRAY_SIZE(bladerf1_tx_port_map);
+ } else {
+ port_map = bladerf1_rx_port_map;
+ port_map_len = ARRAY_SIZE(bladerf1_rx_port_map);
+ }
+
+ for (i = 0; i < port_map_len; i++) {
+ if (strcmp(port_map[i].name, port) == 0) {
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ tx_pa = port_map[i].tx_pa;
+ } else {
+ rx_lna = port_map[i].rx_lna;
+ }
+ ok = true;
+ break;
+ }
+ }
+
+ if (!ok) {
+ log_error("port '%s' not valid for channel %s\n", port,
+ channel2str(ch));
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ for (i = 0; i < port_map_len; i++) {
+ bool enable = (port_map[i].tx_pa == tx_pa);
+#if 0
+ status = lms_pa_enable(dev, port_map[i].tx_pa, enable);
+#else
+ log_verbose("%s: would %s pa %d but this is not implemented\n",
+ __FUNCTION__, enable ? "enable" : "disable", tx_pa);
+ status = 0;
+#endif // 0
+ if (status < 0) {
+ break;
+ }
+ }
+ } else {
+ status = lms_select_lna(dev, rx_lna);
+ }
+
+ return status;
+}
+
+static int bladerf1_get_rf_port(struct bladerf *dev,
+ bladerf_channel ch,
+ const char **port)
+{
+ const struct bladerf_lms_port_name_map *port_map;
+ unsigned int port_map_len;
+ int status;
+ size_t i;
+
+ lms_lna rx_lna = LNA_NONE;
+ lms_pa tx_pa = PA_NONE;
+ bool ok = false;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ /* TODO: pa getter not currently implemented */
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ log_debug("%s: not implemented for TX channels\n", __FUNCTION__);
+ if (port != NULL) {
+ *port = "auto";
+ }
+ return 0;
+ }
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ port_map = bladerf1_tx_port_map;
+ port_map_len = ARRAY_SIZE(bladerf1_tx_port_map);
+
+#if 0
+ status = lms_get_pa(dev, &tx_pa);
+#else
+ status = 0;
+#endif // 0
+ } else {
+ port_map = bladerf1_rx_port_map;
+ port_map_len = ARRAY_SIZE(bladerf1_rx_port_map);
+
+ status = lms_get_lna(dev, &rx_lna);
+ }
+
+ if (status < 0) {
+ return status;
+ }
+
+ if (port != NULL) {
+ for (i = 0; i < port_map_len; i++) {
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ if (tx_pa == port_map[i].tx_pa) {
+ *port = port_map[i].name;
+ ok = true;
+ break;
+ }
+ } else {
+ if (rx_lna == port_map[i].rx_lna) {
+ *port = port_map[i].name;
+ ok = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!ok) {
+ if (port != NULL) {
+ *port = "unknown";
+ }
+ log_error("%s: unexpected port id %d\n", __FUNCTION__,
+ BLADERF_CHANNEL_IS_TX(ch) ? tx_pa : rx_lna);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ return 0;
+}
+
+static int bladerf1_get_rf_ports(struct bladerf *dev,
+ bladerf_channel ch,
+ const char **ports,
+ unsigned int count)
+{
+ const struct bladerf_lms_port_name_map *port_map;
+ unsigned int port_map_len;
+ size_t i;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ /* Return a null list instead of bladerf1_tx_port_map */
+ port_map = NULL;
+ port_map_len = 0;
+ } else {
+ port_map = bladerf1_rx_port_map;
+ port_map_len = ARRAY_SIZE(bladerf1_rx_port_map);
+ }
+
+ if (ports != NULL) {
+ count = (port_map_len < count) ? port_map_len : count;
+
+ for (i = 0; i < count; i++) {
+ ports[i] = port_map[i].name;
+ }
+ }
+
+ return port_map_len;
+}
+
+/******************************************************************************/
+/* Scheduled Tuning */
+/******************************************************************************/
+
+static int bladerf1_get_quick_tune(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_quick_tune *quick_tune)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ return lms_get_quick_tune(dev, ch, quick_tune);
+}
+
+static int bladerf1_schedule_retune(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_timestamp timestamp,
+ bladerf_frequency frequency,
+ struct bladerf_quick_tune *quick_tune)
+
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ int status;
+ struct lms_freq f;
+
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_SCHEDULED_RETUNE)) {
+ log_debug("This FPGA version (%u.%u.%u) does not support "
+ "scheduled retunes.\n",
+ board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch);
+
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ if (quick_tune == NULL) {
+ if (dev->xb == BLADERF_XB_200) {
+ log_info("Consider supplying the quick_tune parameter to"
+ " bladerf_schedule_retune() when the XB-200 is enabled.\n");
+ }
+ status = lms_calculate_tuning_params((uint32_t)frequency, &f);
+ if (status != 0) {
+ return status;
+ }
+ } else {
+ f.freqsel = quick_tune->freqsel;
+ f.vcocap = quick_tune->vcocap;
+ f.nint = quick_tune->nint;
+ f.nfrac = quick_tune->nfrac;
+ f.flags = quick_tune->flags;
+ f.xb_gpio = quick_tune->xb_gpio;
+ f.x = 0;
+ f.vcocap_result = 0;
+ }
+
+ return dev->backend->retune(dev, ch, timestamp, f.nint, f.nfrac, f.freqsel,
+ f.vcocap,
+ (f.flags & LMS_FREQ_FLAGS_LOW_BAND) != 0,
+ f.xb_gpio,
+ (f.flags & LMS_FREQ_FLAGS_FORCE_VCOCAP) != 0);
+}
+
+static int bladerf1_cancel_scheduled_retunes(struct bladerf *dev,
+ bladerf_channel ch)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ int status;
+
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ if (have_cap(board_data->capabilities, BLADERF_CAP_SCHEDULED_RETUNE)) {
+ status = dev->backend->retune(dev, ch, NIOS_PKT_RETUNE_CLEAR_QUEUE, 0,
+ 0, 0, 0, false, 0, false);
+ } else {
+ log_debug("This FPGA version (%u.%u.%u) does not support "
+ "scheduled retunes.\n",
+ board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch);
+
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ return status;
+}
+
+/******************************************************************************/
+/* DC/Phase/Gain Correction */
+/******************************************************************************/
+
+static int bladerf1_get_correction(struct bladerf *dev, bladerf_channel ch,
+ bladerf_correction corr, int16_t *value)
+{
+ int status;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ switch (corr) {
+ case BLADERF_CORR_PHASE:
+ status = dev->backend->get_iq_phase_correction(dev, ch, value);
+ break;
+
+ case BLADERF_CORR_GAIN:
+ status = dev->backend->get_iq_gain_correction(dev, ch, value);
+
+ /* Undo the gain control offset */
+ if (status == 0) {
+ *value -= 4096;
+ }
+ break;
+
+ case BLADERF_CORR_DCOFF_I:
+ status = lms_get_dc_offset_i(dev, ch, value);
+ break;
+
+ case BLADERF_CORR_DCOFF_Q:
+ status = lms_get_dc_offset_q(dev, ch, value);
+ break;
+
+ default:
+ status = BLADERF_ERR_INVAL;
+ log_debug("Invalid correction type: %d\n", corr);
+ break;
+ }
+
+ return status;
+}
+
+static int bladerf1_set_correction(struct bladerf *dev, bladerf_channel ch,
+ bladerf_correction corr, int16_t value)
+{
+ int status;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ switch (corr) {
+ case BLADERF_CORR_PHASE:
+ status = dev->backend->set_iq_phase_correction(dev, ch, value);
+ break;
+
+ case BLADERF_CORR_GAIN:
+ /* Gain correction requires than an offset be applied */
+ value += (int16_t) 4096;
+ status = dev->backend->set_iq_gain_correction(dev, ch, value);
+ break;
+
+ case BLADERF_CORR_DCOFF_I:
+ status = lms_set_dc_offset_i(dev, ch, value);
+ break;
+
+ case BLADERF_CORR_DCOFF_Q:
+ status = lms_set_dc_offset_q(dev, ch, value);
+ break;
+
+ default:
+ status = BLADERF_ERR_INVAL;
+ log_debug("Invalid correction type: %d\n", corr);
+ break;
+ }
+
+ return status;
+}
+
+/******************************************************************************/
+/* Trigger */
+/******************************************************************************/
+
+static int bladerf1_trigger_init(struct bladerf *dev, bladerf_channel ch, bladerf_trigger_signal signal, struct bladerf_trigger *trigger)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_TRX_SYNC_TRIG)) {
+ log_debug("FPGA v%s does not support synchronization triggers.\n",
+ board_data->fpga_version.describe);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ return fpga_trigger_init(dev, ch, signal, trigger);
+}
+
+static int bladerf1_trigger_arm(struct bladerf *dev, const struct bladerf_trigger *trigger, bool arm, uint64_t resv1, uint64_t resv2)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_TRX_SYNC_TRIG)) {
+ log_debug("FPGA v%s does not support synchronization triggers.\n",
+ board_data->fpga_version.describe);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ /* resv1 & resv2 unused - may be allocated for use as timestamp and
+ * other flags in the future */
+
+ return fpga_trigger_arm(dev, trigger, arm);
+}
+
+static int bladerf1_trigger_fire(struct bladerf *dev, const struct bladerf_trigger *trigger)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_TRX_SYNC_TRIG)) {
+ log_debug("FPGA v%s does not support synchronization triggers.\n",
+ board_data->fpga_version.describe);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ return fpga_trigger_fire(dev, trigger);
+}
+
+static int bladerf1_trigger_state(struct bladerf *dev, const struct bladerf_trigger *trigger, bool *is_armed, bool *has_fired, bool *fire_requested, uint64_t *resv1, uint64_t *resv2)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ int status;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_TRX_SYNC_TRIG)) {
+ log_debug("FPGA v%s does not support synchronization triggers.\n",
+ board_data->fpga_version.describe);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ status = fpga_trigger_state(dev, trigger, is_armed, has_fired, fire_requested);
+
+ /* Reserved for future metadata (e.g., trigger counts, timestamp) */
+ if (resv1 != NULL) {
+ *resv1 = 0;
+ }
+
+ if (resv2 != NULL) {
+ *resv2 = 0;
+ }
+
+ return status;
+}
+
+/******************************************************************************/
+/* Streaming */
+/******************************************************************************/
+
+static inline int requires_timestamps(bladerf_format format, bool *required)
+{
+ int status = 0;
+
+ switch (format) {
+ case BLADERF_FORMAT_SC16_Q11_META:
+ case BLADERF_FORMAT_PACKET_META:
+ *required = true;
+ break;
+
+ case BLADERF_FORMAT_SC16_Q11:
+ *required = false;
+ break;
+
+ default:
+ return BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
+
+/**
+ * Perform the neccessary device configuration for the specified format
+ * (e.g., enabling/disabling timestamp support), first checking that the
+ * requested format would not conflict with the other stream direction.
+ *
+ * @param dev Device handle
+ * @param[in] dir Direction that is currently being configured
+ * @param[in] format Format the channel is being configured for
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+static int perform_format_config(struct bladerf *dev, bladerf_direction dir,
+ bladerf_format format)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ int status = 0;
+ bool use_timestamps;
+ bladerf_channel other;
+ bool other_using_timestamps;
+ uint32_t gpio_val;
+
+ status = requires_timestamps(format, &use_timestamps);
+ if (status != 0) {
+ log_debug("%s: Invalid format: %d\n", __FUNCTION__, format);
+ return status;
+ }
+
+ if (use_timestamps && !have_cap(board_data->capabilities, BLADERF_CAP_TIMESTAMPS)) {
+ log_warning("Timestamp support requires FPGA v0.1.0 or later.\n");
+ return BLADERF_ERR_UPDATE_FPGA;
+ }
+
+ switch (dir) {
+ case BLADERF_RX:
+ other = BLADERF_TX;
+ break;
+
+ case BLADERF_TX:
+ other = BLADERF_RX;
+ break;
+
+ default:
+ log_debug("Invalid direction: %d\n", dir);
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = requires_timestamps(board_data->module_format[other],
+ &other_using_timestamps);
+
+ if ((status == 0) && (other_using_timestamps != use_timestamps)) {
+ log_debug("Format conflict detected: RX=%d, TX=%d\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = dev->backend->config_gpio_read(dev, &gpio_val);
+ if (status != 0) {
+ return status;
+ }
+
+ if (format == BLADERF_FORMAT_PACKET_META) {
+ gpio_val |= BLADERF_GPIO_PACKET;
+ use_timestamps = true;
+ } else {
+ gpio_val &= ~BLADERF_GPIO_PACKET;
+ }
+
+ if (use_timestamps) {
+ gpio_val |= (BLADERF_GPIO_TIMESTAMP | BLADERF_GPIO_TIMESTAMP_DIV2);
+ } else {
+ gpio_val &= ~(BLADERF_GPIO_TIMESTAMP | BLADERF_GPIO_TIMESTAMP_DIV2);
+ }
+
+ status = dev->backend->config_gpio_write(dev, gpio_val);
+ if (status == 0) {
+ board_data->module_format[dir] = format;
+ }
+
+ return status;
+}
+
+/**
+ * Deconfigure and update any state pertaining what a format that a stream
+ * direction is no longer using.
+ *
+ * @param dev Device handle
+ * @param[in] dir Direction that is currently being deconfigured
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+static int perform_format_deconfig(struct bladerf *dev, bladerf_direction dir)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ switch (dir) {
+ case BLADERF_RX:
+ case BLADERF_TX:
+ /* We'll reconfigure the HW when we call perform_format_config, so
+ * we just need to update our stored information */
+ board_data->module_format[dir] = -1;
+ break;
+
+ default:
+ log_debug("%s: Invalid direction: %d\n", __FUNCTION__, dir);
+ return BLADERF_ERR_INVAL;
+ }
+
+ return 0;
+}
+
+static int bladerf1_init_stream(struct bladerf_stream **stream, struct bladerf *dev, bladerf_stream_cb callback, void ***buffers, size_t num_buffers, bladerf_format format, size_t samples_per_buffer, size_t num_transfers, void *user_data)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ return async_init_stream(stream, dev, callback, buffers, num_buffers,
+ format, samples_per_buffer, num_transfers, user_data);
+}
+
+static int bladerf1_stream(struct bladerf_stream *stream, bladerf_channel_layout layout)
+{
+ bladerf_direction dir = layout & BLADERF_DIRECTION_MASK;
+ int stream_status, fmt_status;
+
+ if (layout != BLADERF_RX_X1 && layout != BLADERF_TX_X1) {
+ return -EINVAL;
+ }
+
+ fmt_status = perform_format_config(stream->dev, dir, stream->format);
+ if (fmt_status != 0) {
+ return fmt_status;
+ }
+
+ stream_status = async_run_stream(stream, layout);
+
+ fmt_status = perform_format_deconfig(stream->dev, dir);
+ if (fmt_status != 0) {
+ return fmt_status;
+ }
+
+ return stream_status;
+}
+
+static int bladerf1_submit_stream_buffer(struct bladerf_stream *stream, void *buffer, unsigned int timeout_ms, bool nonblock)
+{
+ size_t len;
+ len = async_stream_buf_bytes(stream);
+ return async_submit_stream_buffer(stream, buffer, &len, timeout_ms, nonblock);
+}
+
+static void bladerf1_deinit_stream(struct bladerf_stream *stream)
+{
+ async_deinit_stream(stream);
+}
+
+static int bladerf1_set_stream_timeout(struct bladerf *dev, bladerf_direction dir, unsigned int timeout)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ MUTEX_LOCK(&board_data->sync[dir].lock);
+ board_data->sync[dir].stream_config.timeout_ms = timeout;
+ MUTEX_UNLOCK(&board_data->sync[dir].lock);
+
+ return 0;
+}
+
+static int bladerf1_get_stream_timeout(struct bladerf *dev, bladerf_direction dir, unsigned int *timeout)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ MUTEX_LOCK(&board_data->sync[dir].lock);
+ *timeout = board_data->sync[dir].stream_config.timeout_ms;
+ MUTEX_UNLOCK(&board_data->sync[dir].lock);
+
+ return 0;
+}
+
+static int bladerf1_sync_config(struct bladerf *dev, bladerf_channel_layout layout, bladerf_format format, unsigned int num_buffers, unsigned int buffer_size, unsigned int num_transfers, unsigned int stream_timeout)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ bladerf_direction dir = layout & BLADERF_DIRECTION_MASK;
+ int status;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (layout != BLADERF_RX_X1 && layout != BLADERF_TX_X1) {
+ return -EINVAL;
+ }
+
+ status = perform_format_config(dev, dir, format);
+ if (status == 0) {
+ status = sync_init(&board_data->sync[dir], dev, layout,
+ format, num_buffers, buffer_size,
+ board_data->msg_size, num_transfers,
+ stream_timeout);
+ if (status != 0) {
+ perform_format_deconfig(dev, dir);
+ }
+ }
+
+ return status;
+}
+
+static int bladerf1_sync_tx(struct bladerf *dev,
+ void const *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ int status;
+
+ if (!board_data->sync[BLADERF_TX].initialized) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = sync_tx(&board_data->sync[BLADERF_TX], samples, num_samples,
+ metadata, timeout_ms);
+
+ return status;
+}
+
+static int bladerf1_sync_rx(struct bladerf *dev,
+ void *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ int status;
+
+ if (!board_data->sync[BLADERF_RX].initialized) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = sync_rx(&board_data->sync[BLADERF_RX], samples, num_samples,
+ metadata, timeout_ms);
+
+ return status;
+}
+
+static int bladerf1_get_timestamp(struct bladerf *dev,
+ bladerf_direction dir,
+ bladerf_timestamp *value)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ return dev->backend->get_timestamp(dev, dir, value);
+}
+
+/******************************************************************************/
+/* FPGA/Firmware Loading/Flashing */
+/******************************************************************************/
+
+static bool is_valid_fpga_size(struct bladerf *dev,
+ bladerf_fpga_size fpga,
+ size_t len)
+{
+ static const char env_override[] = "BLADERF_SKIP_FPGA_SIZE_CHECK";
+ bool valid;
+ size_t expected;
+ int status;
+
+ status = dev->board->get_fpga_bytes(dev, &expected);
+ if (status < 0) {
+ return status;
+ }
+
+ /* Provide a means to override this check. This is intended to allow
+ * folks who know what they're doing to work around this quickly without
+ * needing to make a code change. (e.g., someone building a custom FPGA
+ * image that enables compressoin) */
+ if (getenv(env_override)) {
+ log_info("Overriding FPGA size check per %s\n", env_override);
+ valid = true;
+ } else if (expected > 0) {
+ valid = (len == expected);
+ } else {
+ log_debug("Unknown FPGA type (%d). Using relaxed size criteria.\n",
+ fpga);
+
+ if (len < (1 * 1024 * 1024)) {
+ valid = false;
+ } else if (len >
+ (dev->flash_arch->tsize_bytes - BLADERF_FLASH_ADDR_FPGA)) {
+ valid = false;
+ } else {
+ valid = true;
+ }
+ }
+
+ if (!valid) {
+ log_warning("Detected potentially incorrect FPGA file (length was %d, "
+ "expected %d).\n",
+ len, expected);
+
+ log_debug("If you are certain this file is valid, you may define\n"
+ "BLADERF_SKIP_FPGA_SIZE_CHECK in your environment to skip "
+ "this check.\n\n");
+ }
+
+ return valid;
+}
+
+static int bladerf1_load_fpga(struct bladerf *dev, const uint8_t *buf, size_t length)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ int status;
+
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ if (!is_valid_fpga_size(dev, board_data->fpga_size, length)) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ MUTEX_LOCK(&dev->lock);
+
+ status = dev->backend->load_fpga(dev, buf, length);
+ if (status != 0) {
+ MUTEX_UNLOCK(&dev->lock);
+ return status;
+ }
+
+ /* Update device state */
+ board_data->state = STATE_FPGA_LOADED;
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ status = bladerf1_initialize(dev);
+ if (status != 0) {
+ return status;
+ }
+
+ return 0;
+}
+
+static int bladerf1_flash_fpga(struct bladerf *dev, const uint8_t *buf, size_t length)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ if (!is_valid_fpga_size(dev, board_data->fpga_size, length)) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ return spi_flash_write_fpga_bitstream(dev, buf, length);
+}
+
+static int bladerf1_erase_stored_fpga(struct bladerf *dev)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ return spi_flash_erase_fpga(dev);
+}
+
+static bool is_valid_fw_size(size_t len)
+{
+ /* Simple FW applications generally are significantly larger than this */
+ if (len < (50 * 1024)) {
+ return false;
+ } else if (len > BLADERF_FLASH_BYTE_LEN_FIRMWARE) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+static int bladerf1_flash_firmware(struct bladerf *dev, const uint8_t *buf, size_t length)
+{
+ const char env_override[] = "BLADERF_SKIP_FW_SIZE_CHECK";
+
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ /* Sanity check firmware length.
+ *
+ * TODO in the future, better sanity checks can be performed when
+ * using the bladerf image format currently used to backup/restore
+ * calibration data
+ */
+ if (!getenv(env_override) && !is_valid_fw_size(length)) {
+ log_info("Detected potentially invalid firmware file.\n");
+ log_info("Define BLADERF_SKIP_FW_SIZE_CHECK in your evironment "
+ "to skip this check.\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ return spi_flash_write_fx3_fw(dev, buf, length);
+}
+
+static int bladerf1_device_reset(struct bladerf *dev)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ return dev->backend->device_reset(dev);
+}
+
+/******************************************************************************/
+/* Tuning mode */
+/******************************************************************************/
+
+static int bladerf1_set_tuning_mode(struct bladerf *dev, bladerf_tuning_mode mode)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (mode == BLADERF_TUNING_MODE_FPGA &&
+ !have_cap(board_data->capabilities, BLADERF_CAP_FPGA_TUNING)) {
+ log_debug("The loaded FPGA version (%u.%u.%u) does not support the "
+ "provided tuning mode (%d)\n",
+ board_data->fpga_version.major, board_data->fpga_version.minor,
+ board_data->fpga_version.patch, mode);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ switch (mode) {
+ case BLADERF_TUNING_MODE_HOST:
+ log_debug("Tuning mode: host\n");
+ break;
+ case BLADERF_TUNING_MODE_FPGA:
+ log_debug("Tuning mode: FPGA\n");
+ break;
+ default:
+ assert(!"Invalid tuning mode.");
+ return BLADERF_ERR_INVAL;
+ }
+
+ board_data->tuning_mode = mode;
+
+ return 0;
+}
+
+static int bladerf1_get_tuning_mode(struct bladerf *dev, bladerf_tuning_mode *mode)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ *mode = board_data->tuning_mode;
+
+ return 0;
+}
+
+/******************************************************************************/
+/* Loopback */
+/******************************************************************************/
+
+static int bladerf1_get_loopback_modes(
+ struct bladerf *dev, struct bladerf_loopback_modes const **modes)
+{
+ if (modes != NULL) {
+ *modes = bladerf1_loopback_modes;
+ }
+
+ return ARRAY_SIZE(bladerf1_loopback_modes);
+}
+
+static int bladerf1_set_loopback(struct bladerf *dev, bladerf_loopback l)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ int status;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (l == BLADERF_LB_FIRMWARE) {
+ /* Firmware loopback was fully implemented in FW v1.7.1
+ * (1.7.0 could enable it, but 1.7.1 also allowed readback,
+ * so we'll enforce 1.7.1 here. */
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_FW_LOOPBACK)) {
+ log_warning("Firmware v1.7.1 or later is required "
+ "to use firmware loopback.\n\n");
+ status = BLADERF_ERR_UPDATE_FW;
+ return status;
+ } else {
+ /* Samples won't reach the LMS when the device is in firmware
+ * loopback mode. By placing the LMS into a loopback mode, we ensure
+ * that the PAs will be disabled, and remain enabled across
+ * frequency changes.
+ */
+ status = lms_set_loopback_mode(dev, BLADERF_LB_RF_LNA3);
+ if (status != 0) {
+ return status;
+ }
+
+ status = dev->backend->set_firmware_loopback(dev, true);
+ }
+ } else {
+ /* If applicable, ensure FW loopback is disabled */
+ if (have_cap(board_data->capabilities, BLADERF_CAP_FW_LOOPBACK)) {
+ bool fw_lb_enabled = false;
+
+ /* Query first, as the implementation of setting the mode
+ * may interrupt running streams. The API don't guarantee that
+ * switching loopback modes on the fly to work, but we can at least
+ * try to avoid unnecessarily interrupting things...*/
+ status = dev->backend->get_firmware_loopback(dev, &fw_lb_enabled);
+ if (status != 0) {
+ return status;
+ }
+
+ if (fw_lb_enabled) {
+ status = dev->backend->set_firmware_loopback(dev, false);
+ if (status != 0) {
+ return status;
+ }
+ }
+ }
+
+ status = lms_set_loopback_mode(dev, l);
+ }
+
+ return status;
+}
+
+static int bladerf1_get_loopback(struct bladerf *dev, bladerf_loopback *l)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ int status;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ *l = BLADERF_LB_NONE;
+
+ if (have_cap(board_data->capabilities, BLADERF_CAP_FW_LOOPBACK)) {
+ bool fw_lb_enabled;
+ status = dev->backend->get_firmware_loopback(dev, &fw_lb_enabled);
+ if (status == 0 && fw_lb_enabled) {
+ *l = BLADERF_LB_FIRMWARE;
+ }
+ }
+
+ if (*l == BLADERF_LB_NONE) {
+ status = lms_get_loopback_mode(dev, l);
+ }
+
+ return status;
+}
+
+/******************************************************************************/
+/* Sample RX FPGA Mux */
+/******************************************************************************/
+
+static int bladerf1_set_rx_mux(struct bladerf *dev, bladerf_rx_mux mode)
+{
+ uint32_t rx_mux_val;
+ uint32_t config_gpio;
+ int status;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ /* Validate desired mux mode */
+ switch (mode) {
+ case BLADERF_RX_MUX_BASEBAND:
+ case BLADERF_RX_MUX_12BIT_COUNTER:
+ case BLADERF_RX_MUX_32BIT_COUNTER:
+ case BLADERF_RX_MUX_DIGITAL_LOOPBACK:
+ rx_mux_val = ((uint32_t) mode) << BLADERF_GPIO_RX_MUX_SHIFT;
+ break;
+
+ default:
+ log_debug("Invalid RX mux mode setting passed to %s(): %d\n",
+ mode, __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = dev->backend->config_gpio_read(dev, &config_gpio);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Clear out and assign the associated RX mux bits */
+ config_gpio &= ~BLADERF_GPIO_RX_MUX_MASK;
+ config_gpio |= rx_mux_val;
+
+ return dev->backend->config_gpio_write(dev, config_gpio);
+}
+
+static int bladerf1_get_rx_mux(struct bladerf *dev, bladerf_rx_mux *mode)
+{
+ bladerf_rx_mux val;
+ uint32_t config_gpio;
+ int status;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ status = dev->backend->config_gpio_read(dev, &config_gpio);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Extract RX mux bits */
+ config_gpio &= BLADERF_GPIO_RX_MUX_MASK;
+ config_gpio >>= BLADERF_GPIO_RX_MUX_SHIFT;
+ val = (bladerf_rx_mux) (config_gpio);
+
+ /* Enure it's a valid/supported value */
+ switch (val) {
+ case BLADERF_RX_MUX_BASEBAND:
+ case BLADERF_RX_MUX_12BIT_COUNTER:
+ case BLADERF_RX_MUX_32BIT_COUNTER:
+ case BLADERF_RX_MUX_DIGITAL_LOOPBACK:
+ *mode = val;
+ break;
+
+ default:
+ *mode = BLADERF_RX_MUX_INVALID;
+ status = BLADERF_ERR_UNEXPECTED;
+ log_debug("Invalid rx mux mode %d read from config gpio\n", val);
+ }
+
+ return status;
+}
+
+/******************************************************************************/
+/* Low-level VCTCXO Tamer Mode */
+/******************************************************************************/
+
+static int bladerf1_set_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode mode)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_VCTCXO_TAMING_MODE)) {
+ log_debug("FPGA %s does not support VCTCXO taming via an input source\n",
+ board_data->fpga_version.describe);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ return dev->backend->set_vctcxo_tamer_mode(dev, mode);
+}
+
+static int bladerf1_get_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode *mode)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_VCTCXO_TAMING_MODE)) {
+ log_debug("FPGA %s does not support VCTCXO taming via an input source\n",
+ board_data->fpga_version.describe);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ return dev->backend->get_vctcxo_tamer_mode(dev, mode);
+}
+
+/******************************************************************************/
+/* Low-level VCTCXO Trim DAC access */
+/******************************************************************************/
+
+static int bladerf1_get_vctcxo_trim(struct bladerf *dev, uint16_t *trim)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ *trim = board_data->dac_trim;
+
+ return 0;
+}
+
+static int bladerf1_trim_dac_read(struct bladerf *dev, uint16_t *trim)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_VCTCXO_TRIMDAC_READ)) {
+ log_debug("FPGA %s does not support VCTCXO trimdac readback.\n",
+ board_data->fpga_version.describe);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ return dac161s055_read(dev, trim);
+}
+
+static int bladerf1_trim_dac_write(struct bladerf *dev, uint16_t trim)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ return dac161s055_write(dev, trim);
+}
+
+/******************************************************************************/
+/* Low-level Trigger control access */
+/******************************************************************************/
+
+static int bladerf1_read_trigger(struct bladerf *dev, bladerf_channel ch,
+ bladerf_trigger_signal trigger, uint8_t *val)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ return fpga_trigger_read(dev, ch, trigger, val);
+}
+
+static int bladerf1_write_trigger(struct bladerf *dev, bladerf_channel ch,
+ bladerf_trigger_signal trigger, uint8_t val)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ return fpga_trigger_write(dev, ch, trigger, val);
+}
+
+/******************************************************************************/
+/* Low-level Wishbone Master access */
+/******************************************************************************/
+
+static int bladerf1_wishbone_master_read(struct bladerf *dev, uint32_t addr, uint32_t *data)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ return dev->backend->wishbone_master_read(dev, addr, data);
+}
+
+static int bladerf1_wishbone_master_write(struct bladerf *dev, uint32_t addr, uint32_t data)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ return dev->backend->wishbone_master_write(dev, addr, data);
+}
+
+/******************************************************************************/
+/* Low-level Configuration GPIO access */
+/******************************************************************************/
+
+static int bladerf1_config_gpio_read(struct bladerf *dev, uint32_t *val)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ return dev->backend->config_gpio_read(dev, val);
+}
+
+static int bladerf1_config_gpio_write(struct bladerf *dev, uint32_t val)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ return dev->backend->config_gpio_write(dev, val);
+}
+
+/******************************************************************************/
+/* Low-level SPI Flash access */
+/******************************************************************************/
+
+static int bladerf1_erase_flash(struct bladerf *dev, uint32_t erase_block, uint32_t count)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ return spi_flash_erase(dev, erase_block, count);
+}
+
+static int bladerf1_read_flash(struct bladerf *dev, uint8_t *buf,
+ uint32_t page, uint32_t count)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ return spi_flash_read(dev, buf, page, count);
+}
+
+static int bladerf1_write_flash(struct bladerf *dev, const uint8_t *buf,
+ uint32_t page, uint32_t count)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ return spi_flash_write(dev, buf, page, count);
+}
+
+/******************************************************************************/
+/* Expansion support */
+/******************************************************************************/
+
+static int bladerf1_expansion_attach(struct bladerf *dev, bladerf_xb xb)
+{
+ struct bladerf1_board_data *board_data = dev->board_data;
+ bladerf_xb attached;
+ int status;
+
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ status = dev->board->expansion_get_attached(dev, &attached);
+ if (status != 0) {
+ return status;
+ }
+
+ if (xb != attached && attached != BLADERF_XB_NONE) {
+ log_debug("%s: Switching XB types is not supported.\n", __FUNCTION__);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ if (xb == BLADERF_XB_100) {
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_MASKED_XBIO_WRITE)) {
+ log_debug("%s: XB100 support requires FPGA v0.4.1 or later.\n",
+ __FUNCTION__);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ log_verbose("Attaching XB100\n");
+ status = xb100_attach(dev);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose("Enabling XB100\n");
+ status = xb100_enable(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose("Initializing XB100\n");
+ status = xb100_init(dev);
+ if (status != 0) {
+ return status;
+ }
+ } else if (xb == BLADERF_XB_200) {
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_XB200)) {
+ log_debug("%s: XB200 support requires FPGA v0.0.5 or later\n",
+ __FUNCTION__);
+ return BLADERF_ERR_UPDATE_FPGA;
+ }
+
+ log_verbose("Attaching XB200\n");
+ status = xb200_attach(dev);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose("Enabling XB200\n");
+ status = xb200_enable(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose("Initializing XB200\n");
+ status = xb200_init(dev);
+ if (status != 0) {
+ return status;
+ }
+ } else if (xb == BLADERF_XB_300) {
+ log_verbose("Attaching XB300\n");
+ status = xb300_attach(dev);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose("Enabling XB300\n");
+ status = xb300_enable(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose("Initializing XB300\n");
+ status = xb300_init(dev);
+ if (status != 0) {
+ return status;
+ }
+ } else if (xb == BLADERF_XB_NONE) {
+ log_debug("%s: Disabling an attached XB is not supported.\n",
+ __FUNCTION__);
+ return BLADERF_ERR_UNSUPPORTED;
+ } else {
+ log_debug("%s: Unknown xb type: %d\n", __FUNCTION__, xb);
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Cache what we have attached in our device handle to alleviate the
+ * need to go read the device state */
+ dev->xb = xb;
+
+ return 0;
+}
+
+static int bladerf1_expansion_get_attached(struct bladerf *dev, bladerf_xb *xb)
+{
+ int status;
+
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ switch (dev->xb) {
+ case BLADERF_XB_NONE:
+ case BLADERF_XB_100:
+ case BLADERF_XB_200:
+ case BLADERF_XB_300:
+ *xb = dev->xb;
+ status = 0;
+ break;
+ default:
+ log_debug("Device handle contains invalid XB id: %d\n", dev->xb);
+ status = BLADERF_ERR_UNEXPECTED;
+ break;
+ }
+
+ return status;
+}
+
+/******************************************************************************/
+/* Board binding */
+/******************************************************************************/
+
+const struct board_fns bladerf1_board_fns = {
+ FIELD_INIT(.matches, bladerf1_matches),
+ FIELD_INIT(.open, bladerf1_open),
+ FIELD_INIT(.close, bladerf1_close),
+ FIELD_INIT(.device_speed, bladerf1_device_speed),
+ FIELD_INIT(.get_serial, bladerf1_get_serial),
+ FIELD_INIT(.get_fpga_size, bladerf1_get_fpga_size),
+ FIELD_INIT(.get_fpga_bytes, bladerf1_get_fpga_bytes),
+ FIELD_INIT(.get_flash_size, bladerf1_get_flash_size),
+ FIELD_INIT(.is_fpga_configured, bladerf1_is_fpga_configured),
+ FIELD_INIT(.get_fpga_source, bladerf1_get_fpga_source),
+ FIELD_INIT(.get_capabilities, bladerf1_get_capabilities),
+ FIELD_INIT(.get_channel_count, bladerf1_get_channel_count),
+ FIELD_INIT(.get_fpga_version, bladerf1_get_fpga_version),
+ FIELD_INIT(.get_fw_version, bladerf1_get_fw_version),
+ FIELD_INIT(.set_gain, bladerf1_set_gain),
+ FIELD_INIT(.get_gain, bladerf1_get_gain),
+ FIELD_INIT(.set_gain_mode, bladerf1_set_gain_mode),
+ FIELD_INIT(.get_gain_mode, bladerf1_get_gain_mode),
+ FIELD_INIT(.get_gain_modes, bladerf1_get_gain_modes),
+ FIELD_INIT(.get_gain_range, bladerf1_get_gain_range),
+ FIELD_INIT(.set_gain_stage, bladerf1_set_gain_stage),
+ FIELD_INIT(.get_gain_stage, bladerf1_get_gain_stage),
+ FIELD_INIT(.get_gain_stage_range, bladerf1_get_gain_stage_range),
+ FIELD_INIT(.get_gain_stages, bladerf1_get_gain_stages),
+ FIELD_INIT(.set_sample_rate, bladerf1_set_sample_rate),
+ FIELD_INIT(.set_rational_sample_rate, bladerf1_set_rational_sample_rate),
+ FIELD_INIT(.get_sample_rate, bladerf1_get_sample_rate),
+ FIELD_INIT(.get_sample_rate_range, bladerf1_get_sample_rate_range),
+ FIELD_INIT(.get_rational_sample_rate, bladerf1_get_rational_sample_rate),
+ FIELD_INIT(.set_bandwidth, bladerf1_set_bandwidth),
+ FIELD_INIT(.get_bandwidth, bladerf1_get_bandwidth),
+ FIELD_INIT(.get_bandwidth_range, bladerf1_get_bandwidth_range),
+ FIELD_INIT(.get_frequency, bladerf1_get_frequency),
+ FIELD_INIT(.set_frequency, bladerf1_set_frequency),
+ FIELD_INIT(.get_frequency_range, bladerf1_get_frequency_range),
+ FIELD_INIT(.select_band, bladerf1_select_band),
+ FIELD_INIT(.set_rf_port, bladerf1_set_rf_port),
+ FIELD_INIT(.get_rf_port, bladerf1_get_rf_port),
+ FIELD_INIT(.get_rf_ports, bladerf1_get_rf_ports),
+ FIELD_INIT(.get_quick_tune, bladerf1_get_quick_tune),
+ FIELD_INIT(.schedule_retune, bladerf1_schedule_retune),
+ FIELD_INIT(.cancel_scheduled_retunes, bladerf1_cancel_scheduled_retunes),
+ FIELD_INIT(.get_correction, bladerf1_get_correction),
+ FIELD_INIT(.set_correction, bladerf1_set_correction),
+ FIELD_INIT(.trigger_init, bladerf1_trigger_init),
+ FIELD_INIT(.trigger_arm, bladerf1_trigger_arm),
+ FIELD_INIT(.trigger_fire, bladerf1_trigger_fire),
+ FIELD_INIT(.trigger_state, bladerf1_trigger_state),
+ FIELD_INIT(.enable_module, bladerf1_enable_module),
+ FIELD_INIT(.init_stream, bladerf1_init_stream),
+ FIELD_INIT(.stream, bladerf1_stream),
+ FIELD_INIT(.submit_stream_buffer, bladerf1_submit_stream_buffer),
+ FIELD_INIT(.deinit_stream, bladerf1_deinit_stream),
+ FIELD_INIT(.set_stream_timeout, bladerf1_set_stream_timeout),
+ FIELD_INIT(.get_stream_timeout, bladerf1_get_stream_timeout),
+ FIELD_INIT(.sync_config, bladerf1_sync_config),
+ FIELD_INIT(.sync_tx, bladerf1_sync_tx),
+ FIELD_INIT(.sync_rx, bladerf1_sync_rx),
+ FIELD_INIT(.get_timestamp, bladerf1_get_timestamp),
+ FIELD_INIT(.load_fpga, bladerf1_load_fpga),
+ FIELD_INIT(.flash_fpga, bladerf1_flash_fpga),
+ FIELD_INIT(.erase_stored_fpga, bladerf1_erase_stored_fpga),
+ FIELD_INIT(.flash_firmware, bladerf1_flash_firmware),
+ FIELD_INIT(.device_reset, bladerf1_device_reset),
+ FIELD_INIT(.set_tuning_mode, bladerf1_set_tuning_mode),
+ FIELD_INIT(.get_tuning_mode, bladerf1_get_tuning_mode),
+ FIELD_INIT(.get_loopback_modes, bladerf1_get_loopback_modes),
+ FIELD_INIT(.set_loopback, bladerf1_set_loopback),
+ FIELD_INIT(.get_loopback, bladerf1_get_loopback),
+ FIELD_INIT(.get_rx_mux, bladerf1_get_rx_mux),
+ FIELD_INIT(.set_rx_mux, bladerf1_set_rx_mux),
+ FIELD_INIT(.set_vctcxo_tamer_mode, bladerf1_set_vctcxo_tamer_mode),
+ FIELD_INIT(.get_vctcxo_tamer_mode, bladerf1_get_vctcxo_tamer_mode),
+ FIELD_INIT(.get_vctcxo_trim, bladerf1_get_vctcxo_trim),
+ FIELD_INIT(.trim_dac_read, bladerf1_trim_dac_read),
+ FIELD_INIT(.trim_dac_write, bladerf1_trim_dac_write),
+ FIELD_INIT(.read_trigger, bladerf1_read_trigger),
+ FIELD_INIT(.write_trigger, bladerf1_write_trigger),
+ FIELD_INIT(.wishbone_master_read, bladerf1_wishbone_master_read),
+ FIELD_INIT(.wishbone_master_write, bladerf1_wishbone_master_write),
+ FIELD_INIT(.config_gpio_read, bladerf1_config_gpio_read),
+ FIELD_INIT(.config_gpio_write, bladerf1_config_gpio_write),
+ FIELD_INIT(.erase_flash, bladerf1_erase_flash),
+ FIELD_INIT(.read_flash, bladerf1_read_flash),
+ FIELD_INIT(.write_flash, bladerf1_write_flash),
+ FIELD_INIT(.expansion_attach, bladerf1_expansion_attach),
+ FIELD_INIT(.expansion_get_attached, bladerf1_expansion_get_attached),
+ FIELD_INIT(.name, "bladerf1"),
+};
+
+/******************************************************************************
+ ******************************************************************************
+ * bladeRF1-specific Functions *
+ ******************************************************************************
+ ******************************************************************************/
+
+/******************************************************************************/
+/* TX Gain */
+/******************************************************************************/
+
+int bladerf_set_txvga2(struct bladerf *dev, int gain)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_txvga2_set_gain(dev, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_get_txvga2(struct bladerf *dev, int *gain)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_txvga2_get_gain(dev, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_set_txvga1(struct bladerf *dev, int gain)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_txvga1_set_gain(dev, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_get_txvga1(struct bladerf *dev, int *gain)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_txvga1_get_gain(dev, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+/******************************************************************************/
+/* RX Gain */
+/******************************************************************************/
+
+int bladerf_set_lna_gain(struct bladerf *dev, bladerf_lna_gain gain)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_lna_set_gain(dev, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_get_lna_gain(struct bladerf *dev, bladerf_lna_gain *gain)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_lna_get_gain(dev, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_set_rxvga1(struct bladerf *dev, int gain)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_rxvga1_set_gain(dev, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_get_rxvga1(struct bladerf *dev, int *gain)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_rxvga1_get_gain(dev, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_set_rxvga2(struct bladerf *dev, int gain)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_rxvga2_set_gain(dev, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_get_rxvga2(struct bladerf *dev, int *gain)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_rxvga2_get_gain(dev, gain);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+/******************************************************************************/
+/* LPF Bypass */
+/******************************************************************************/
+
+int bladerf_set_lpf_mode(struct bladerf *dev, bladerf_channel ch,
+ bladerf_lpf_mode mode)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_lpf_set_mode(dev, ch, mode);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_get_lpf_mode(struct bladerf *dev, bladerf_channel ch,
+ bladerf_lpf_mode *mode)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_lpf_get_mode(dev, ch, mode);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+/******************************************************************************/
+/* Sample Internal/Direct */
+/******************************************************************************/
+
+int bladerf_get_sampling(struct bladerf *dev, bladerf_sampling *sampling)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_get_sampling(dev, sampling);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_set_sampling(struct bladerf *dev, bladerf_sampling sampling)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_select_sampling(dev, sampling);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+/******************************************************************************/
+/* SMB Clock Configuration */
+/******************************************************************************/
+
+int bladerf_get_smb_mode(struct bladerf *dev, bladerf_smb_mode *mode)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = smb_clock_get_mode(dev, mode);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_set_smb_mode(struct bladerf *dev, bladerf_smb_mode mode)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = smb_clock_set_mode(dev, mode);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_get_smb_frequency(struct bladerf *dev, unsigned int *rate)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = si5338_get_smb_freq(dev, rate);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_set_smb_frequency(struct bladerf *dev, uint32_t rate, uint32_t *actual)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = si5338_set_smb_freq(dev, rate, actual);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_get_rational_smb_frequency(struct bladerf *dev, struct bladerf_rational_rate *rate)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = si5338_get_rational_smb_freq(dev, rate);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_set_rational_smb_frequency(struct bladerf *dev, struct bladerf_rational_rate *rate, struct bladerf_rational_rate *actual)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = si5338_set_rational_smb_freq(dev, rate, actual);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+/******************************************************************************/
+/* DC Calibration */
+/******************************************************************************/
+
+int bladerf_calibrate_dc(struct bladerf *dev, bladerf_cal_module module)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_calibrate_dc(dev, module);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+/******************************************************************************/
+/* Low-level Si5338 access */
+/******************************************************************************/
+
+int bladerf_si5338_read(struct bladerf *dev, uint8_t address, uint8_t *val)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_FPGA_LOADED);
+
+ status = dev->backend->si5338_read(dev,address,val);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_si5338_write(struct bladerf *dev, uint8_t address, uint8_t val)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_FPGA_LOADED);
+
+ status = dev->backend->si5338_write(dev,address,val);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+/******************************************************************************/
+/* Low-level LMS access */
+/******************************************************************************/
+
+int bladerf_lms_read(struct bladerf *dev, uint8_t address, uint8_t *val)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_FPGA_LOADED);
+
+ status = dev->backend->lms_read(dev,address,val);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_lms_write(struct bladerf *dev, uint8_t address, uint8_t val)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_FPGA_LOADED);
+
+ status = dev->backend->lms_write(dev,address,val);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_lms_set_dc_cals(struct bladerf *dev,
+ const struct bladerf_lms_dc_cals *dc_cals)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_set_dc_cals(dev, dc_cals);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+int bladerf_lms_get_dc_cals(struct bladerf *dev,
+ struct bladerf_lms_dc_cals *dc_cals)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_INITIALIZED);
+
+ status = lms_get_dc_cals(dev, dc_cals);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
+
+/******************************************************************************/
+/* Low-level XB SPI access */
+/******************************************************************************/
+
+int bladerf_xb_spi_write(struct bladerf *dev, uint32_t val)
+{
+ int status;
+
+ if (dev->board != &bladerf1_board_fns)
+ return BLADERF_ERR_UNSUPPORTED;
+
+ MUTEX_LOCK(&dev->lock);
+
+ CHECK_BOARD_STATE_LOCKED(STATE_FPGA_LOADED);
+
+ status = dev->backend->xb_spi(dev, val);
+
+ MUTEX_UNLOCK(&dev->lock);
+
+ return status;
+}
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/calibration.c b/Radio/HW/BladeRF/src/board/bladerf1/calibration.c
new file mode 100644
index 0000000..4918fca
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf1/calibration.c
@@ -0,0 +1,518 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* The binary DC calibration data is stored as follows. All values are
+ * little-endian byte order.
+ *
+ * 0x0000 [uint16_t: Fixed value of 0x9a51]
+ * 0x0002 [uint32_t: Reserved. Set to 0x00000000]
+ * 0x0006 [uint32_t: Table format version]
+ * 0x000a [uint32_t: Number of entries]
+ * 0x000e [uint8_t: LMS LPF tuning register value]
+ * 0x000f [uint8_t: LMS TX LPF I register value]
+ * 0x0010 [uint8_t: LMS TX LPF Q register value]
+ * 0x0011 [uint8_t: LMS RX LPF I register value]
+ * 0x0012 [uint8_t: LMS RX LPF Q register value]
+ * 0x0013 [uint8_t: LMS DC REF register value]
+ * 0x0014 [uint8_t: LMS RX VGA2a I register value]
+ * 0x0015 [uint8_t: LMS RX VGA2a Q register value]
+ * 0x0016 [uint8_t: LMS RX VGA2b I register value]
+ * 0x0017 [uint8_t: LMS RX VGA2b Q register value]
+ * 0x0018 [Start of table entries]
+ *
+ * Where a table entry is:
+ * [uint32_t: Frequency]
+ * [int16_t: DC I correction value]
+ * [int16_t: DC Q correction value]
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <float.h>
+
+#include "host_config.h"
+#include "minmax.h"
+
+#include "calibration.h"
+
+#ifdef TEST_DC_CAL_TABLE
+# include <stdio.h>
+# define SHORT_SEARCH 4
+# define WARN(str) fprintf(stderr, str)
+#else
+# include "log.h"
+# define SHORT_SEARCH 10
+# define WARN(str) log_warning(str)
+#endif
+
+#define DC_CAL_TBL_MAGIC 0x1ab1
+
+#define DC_CAL_TBL_META_SIZE 0x18
+#define DC_CAL_TBL_ENTRY_SIZE (sizeof(uint32_t) + 2 * sizeof(int16_t))
+#define DC_CAL_TBL_MIN_SIZE (DC_CAL_TBL_META_SIZE + DC_CAL_TBL_ENTRY_SIZE)
+
+static inline bool entry_matches(const struct dc_cal_tbl *tbl,
+ unsigned int entry_idx, unsigned int freq)
+{
+ if (entry_idx >= (tbl->n_entries - 1)) {
+ return freq >= tbl->entries[entry_idx].freq;
+ } else {
+ return freq >= tbl->entries[entry_idx].freq &&
+ freq < tbl->entries[entry_idx + 1].freq;
+ }
+}
+
+static unsigned int find_entry(const struct dc_cal_tbl *tbl,
+ unsigned int curr_idx,
+ unsigned int min_idx, unsigned int max_idx,
+ unsigned int freq, bool *hit_limit)
+{
+ /* Converged to a single entry - this is the best we can do */
+ if ((max_idx < min_idx) || (max_idx == min_idx && max_idx == curr_idx)) {
+ *hit_limit = true;
+ return curr_idx;
+ }
+
+ if (!entry_matches(tbl, curr_idx, freq)) {
+ if (tbl->entries[curr_idx].freq > freq) {
+ if (curr_idx > 0) {
+ max_idx = (curr_idx - 1);
+ } else {
+ /* Lower limit hit - return first entry */
+ *hit_limit = true;
+ return 0;
+ }
+ } else {
+ if (curr_idx < (tbl->n_entries - 1)) {
+ min_idx = curr_idx + 1;
+ } else {
+ /* Upper limit hit - return last entry */
+ *hit_limit = true;
+ return tbl->n_entries - 1;
+ }
+ }
+
+ curr_idx = min_idx + (max_idx - min_idx) / 2;
+
+ return find_entry(tbl, curr_idx, min_idx, max_idx, freq, hit_limit);
+ } else {
+ return curr_idx;
+ }
+}
+
+unsigned int dc_cal_tbl_lookup(const struct dc_cal_tbl *tbl, unsigned int freq)
+{
+ unsigned int ret = 0;
+ bool limit = false; /* Hit a limit before finding a match */
+
+ /* First check if we're at a nearby change. This is generally the case
+ * when the frequecy change */
+ if (tbl->n_entries > SHORT_SEARCH) {
+ const unsigned int min_idx =
+ (unsigned int) i64_max(0, tbl->curr_idx - (int64_t)SHORT_SEARCH / 2);
+
+ const unsigned int max_idx =
+ (unsigned int) i64_min(tbl->n_entries - 1, tbl->curr_idx + SHORT_SEARCH / 2);
+
+ ret = find_entry(tbl, tbl->curr_idx, min_idx, max_idx, freq, &limit);
+ if (!limit) {
+ return ret;
+ }
+ }
+
+ return find_entry(tbl, tbl->curr_idx, 0, tbl->n_entries - 1, freq, &limit);
+}
+
+struct dc_cal_tbl * dc_cal_tbl_load(const uint8_t *buf, size_t buf_len)
+{
+ struct dc_cal_tbl *ret;
+ uint32_t i;
+ uint16_t magic;
+
+ if (buf_len < DC_CAL_TBL_MIN_SIZE) {
+ return NULL;
+ }
+
+ memcpy(&magic, buf, sizeof(magic));
+ if (LE16_TO_HOST(magic) != DC_CAL_TBL_MAGIC) {
+ log_debug("Invalid magic value in cal table: %d\n", magic);
+ return NULL;
+ }
+ buf += sizeof(magic);
+
+ ret = malloc(sizeof(ret[0]));
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ buf += sizeof(uint32_t); /* Skip reserved bytes */
+
+ memcpy(&ret->version, buf, sizeof(ret->version));
+ ret->version = LE32_TO_HOST(ret->version);
+ buf += sizeof(ret->version);
+
+ memcpy(&ret->n_entries, buf, sizeof(ret->n_entries));
+ ret->n_entries = LE32_TO_HOST(ret->n_entries);
+ buf += sizeof(ret->n_entries);
+
+ if (buf_len <
+ (DC_CAL_TBL_META_SIZE + DC_CAL_TBL_ENTRY_SIZE * ret->n_entries) ) {
+
+ free(ret);
+ return NULL;
+ }
+
+ ret->entries = malloc(sizeof(ret->entries[0]) * ret->n_entries);
+ if (ret->entries == NULL) {
+ free(ret);
+ return NULL;
+ }
+
+ ret->reg_vals.lpf_tuning = *buf++;
+ ret->reg_vals.tx_lpf_i = *buf++;
+ ret->reg_vals.tx_lpf_q = *buf++;
+ ret->reg_vals.rx_lpf_i = *buf++;
+ ret->reg_vals.rx_lpf_q = *buf++;
+ ret->reg_vals.dc_ref = *buf++;
+ ret->reg_vals.rxvga2a_i = *buf++;
+ ret->reg_vals.rxvga2a_q = *buf++;
+ ret->reg_vals.rxvga2b_i = *buf++;
+ ret->reg_vals.rxvga2b_q = *buf++;
+
+ ret->curr_idx = ret->n_entries / 2;
+ for (i = 0; i < ret->n_entries; i++) {
+ memcpy(&ret->entries[i].freq, buf, sizeof(uint32_t));
+ buf += sizeof(uint32_t);
+
+ memcpy(&ret->entries[i].dc_i, buf, sizeof(int16_t));
+ buf += sizeof(int16_t);
+
+ memcpy(&ret->entries[i].dc_q, buf, sizeof(int16_t));
+ buf += sizeof(int16_t);
+
+ ret->entries[i].freq = LE32_TO_HOST(ret->entries[i].freq);
+ ret->entries[i].dc_i = LE32_TO_HOST(ret->entries[i].dc_i);
+ ret->entries[i].dc_q = LE32_TO_HOST(ret->entries[i].dc_q);
+
+ if (ret->version >= 2) {
+ memcpy(&ret->entries[i].max_dc_i, buf, sizeof(int16_t));
+ buf += sizeof(int16_t);
+
+ memcpy(&ret->entries[i].max_dc_q, buf, sizeof(int16_t));
+ buf += sizeof(int16_t);
+
+ memcpy(&ret->entries[i].mid_dc_i, buf, sizeof(int16_t));
+ buf += sizeof(int16_t);
+
+ memcpy(&ret->entries[i].mid_dc_q, buf, sizeof(int16_t));
+ buf += sizeof(int16_t);
+
+ memcpy(&ret->entries[i].min_dc_i, buf, sizeof(int16_t));
+ buf += sizeof(int16_t);
+
+ memcpy(&ret->entries[i].min_dc_q, buf, sizeof(int16_t));
+ buf += sizeof(int16_t);
+
+ ret->entries[i].max_dc_i = LE32_TO_HOST(ret->entries[i].max_dc_i);
+ ret->entries[i].max_dc_q = LE32_TO_HOST(ret->entries[i].max_dc_q);
+ ret->entries[i].mid_dc_i = LE32_TO_HOST(ret->entries[i].mid_dc_i);
+ ret->entries[i].mid_dc_q = LE32_TO_HOST(ret->entries[i].mid_dc_q);
+ ret->entries[i].min_dc_i = LE32_TO_HOST(ret->entries[i].min_dc_i);
+ ret->entries[i].min_dc_q = LE32_TO_HOST(ret->entries[i].min_dc_q);
+ }
+ }
+
+ return ret;
+}
+
+int dc_cal_tbl_image_load(struct bladerf *dev,
+ struct dc_cal_tbl **tbl, const char *img_file)
+{
+ int status;
+ struct bladerf_image *img;
+
+ img = bladerf_alloc_image(dev, BLADERF_IMAGE_TYPE_INVALID, 0, 0);
+ if (img == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ status = bladerf_image_read(img, img_file);
+ if (status != 0) {
+ return status;
+ }
+
+ if (img->type == BLADERF_IMAGE_TYPE_RX_DC_CAL ||
+ img->type == BLADERF_IMAGE_TYPE_TX_DC_CAL) {
+ *tbl = dc_cal_tbl_load(img->data, img->length);
+ status = 0;
+ } else {
+ status = BLADERF_ERR_INVAL;
+ }
+
+ bladerf_free_image(img);
+
+ return status;
+}
+
+/* Interpolate a y value given two points and a desired x value
+ *
+ * y = interp( (x0, y0), (x1, y1), x )
+ *
+ * Returns
+ */
+static inline unsigned int interp(unsigned int x0, unsigned int y0,
+ unsigned int x1, unsigned int y1,
+ unsigned int x)
+{
+ const float num = (float) y1 - y0;
+ const float den = (float) x1 - x0;
+ const float m = den == 0 ? FLT_MAX : num / den;
+ const float y = (x - x0) * m + y0;
+
+ return (unsigned int) y;
+}
+
+static inline void dc_cal_interp_entry(const struct dc_cal_tbl *tbl,
+ unsigned int idx_low,
+ unsigned int idx_high,
+ unsigned int freq,
+ struct dc_cal_entry *entry)
+{
+ const unsigned int f_low = tbl->entries[idx_low].freq;
+ const unsigned int f_high = tbl->entries[idx_high].freq;
+
+#define ENTRY_VAR(x) \
+ entry->x = (int16_t) interp(f_low, tbl->entries[idx_low].x, \
+ f_high, tbl->entries[idx_low].x, \
+ freq)
+
+ ENTRY_VAR(dc_i);
+ ENTRY_VAR(dc_q);
+
+ ENTRY_VAR(max_dc_i);
+ ENTRY_VAR(max_dc_q);
+ ENTRY_VAR(mid_dc_i);
+ ENTRY_VAR(mid_dc_q);
+ ENTRY_VAR(min_dc_i);
+ ENTRY_VAR(min_dc_q);
+}
+
+void dc_cal_tbl_entry(const struct dc_cal_tbl *tbl, unsigned int freq,
+ struct dc_cal_entry *entry)
+{
+ const unsigned int idx = dc_cal_tbl_lookup(tbl, freq);
+
+ if (tbl->entries[idx].freq == freq) {
+ memcpy(entry, &tbl->entries[idx], sizeof(struct dc_cal_entry));
+ } else if (idx == (tbl->n_entries - 1)) {
+ dc_cal_interp_entry(tbl, idx - 1, idx, freq, entry);
+ } else {
+ dc_cal_interp_entry(tbl, idx, idx + 1, freq, entry);
+ }
+}
+
+void dc_cal_tbl_free(struct dc_cal_tbl **tbl)
+{
+ if (*tbl != NULL) {
+ free((*tbl)->entries);
+ free(*tbl);
+ *tbl = NULL;
+ }
+}
+
+#ifdef TEST_DC_CAL_TABLE
+
+#define ENTRY(f) { f, 0, 0 }
+
+#define TBL(entries, curr_idx) { \
+ entries != NULL ? sizeof(entries) / sizeof(entries[0]) : 0, \
+ curr_idx, entries \
+}
+
+#define TEST_CASE(exp_idx, entries, default_idx, freq) { \
+ TBL(entries, default_idx), \
+ freq, \
+ exp_idx, \
+ exp_idx > -2, \
+}
+
+
+struct dc_cal_entry unsorted_entries[] = {
+ ENTRY(300e6), ENTRY(400e6), ENTRY(320e6),
+ ENTRY(310e6), ENTRY(550e6), ENTRY(500e6)
+};
+
+struct dc_cal_entry single_entry[] = { ENTRY(2.4e9) };
+
+struct dc_cal_entry three_entries[] = {
+ ENTRY(300e6), ENTRY(1.5e9), ENTRY(2.4e9)
+};
+
+struct dc_cal_entry entries[] = {
+ ENTRY(300e6), ENTRY(400e6), ENTRY(500e6), ENTRY(600e6), ENTRY(700e6),
+ ENTRY(800e6), ENTRY(900e6), ENTRY(1.0e9), ENTRY(1.1e9), ENTRY(1.2e9),
+ ENTRY(1.3e9), ENTRY(1.4e9), ENTRY(1.5e9), ENTRY(1.6e9), ENTRY(1.7e9),
+ ENTRY(1.8e9), ENTRY(1.9e9), ENTRY(2.0e9), ENTRY(2.1e9), ENTRY(2.2e9),
+ ENTRY(2.3e9), ENTRY(2.4e9), ENTRY(2.5e9), ENTRY(2.6e9), ENTRY(2.7e9),
+ ENTRY(2.8e9), ENTRY(2.9e9), ENTRY(3.0e9), ENTRY(3.1e9), ENTRY(3.2e9),
+ ENTRY(3.3e9), ENTRY(3.4e9), ENTRY(3.5e9), ENTRY(3.6e9), ENTRY(3.7e9),
+ ENTRY(3.8e9),
+};
+
+struct test {
+ const struct dc_cal_tbl tbl;
+ unsigned int freq;
+ int expected_idx;
+ bool check_result;
+} tests[] = {
+ /* Invalid due to unsorted entries. These won't neccessarily work,
+ * but shouldn't crash */
+ TEST_CASE(-2, unsorted_entries, 0, 300e6),
+ TEST_CASE(-2, unsorted_entries, 1, 300e6),
+ TEST_CASE(-2, unsorted_entries, 2, 300e6),
+ TEST_CASE(-2, unsorted_entries, 3, 300e6),
+ TEST_CASE(-2, unsorted_entries, 4, 300e6),
+ TEST_CASE(-2, unsorted_entries, 5, 300e6),
+ TEST_CASE(-2, unsorted_entries, 0, 310e6),
+ TEST_CASE(-2, unsorted_entries, 1, 401e6),
+ TEST_CASE(-2, unsorted_entries, 2, 550e6),
+ TEST_CASE(-2, unsorted_entries, 3, 100e5),
+ TEST_CASE(-2, unsorted_entries, 4, 3.8e9),
+ TEST_CASE(-2, unsorted_entries, 5, 321e6),
+
+ /* Single entry - should just return whatever is availble */
+ TEST_CASE(0, single_entry, 0, 300e6),
+ TEST_CASE(0, single_entry, 0, 2.4e9),
+ TEST_CASE(0, single_entry, 0, 3.8e9),
+
+ /* Three entries, exact matches */
+ TEST_CASE(0, three_entries, 0, 300e6),
+ TEST_CASE(0, three_entries, 1, 300e6),
+ TEST_CASE(0, three_entries, 2, 300e6),
+ TEST_CASE(1, three_entries, 0, 1.5e9),
+ TEST_CASE(1, three_entries, 1, 1.5e9),
+ TEST_CASE(1, three_entries, 2, 1.5e9),
+ TEST_CASE(2, three_entries, 0, 2.4e9),
+ TEST_CASE(2, three_entries, 1, 2.4e9),
+ TEST_CASE(2, three_entries, 2, 2.4e9),
+
+ /* Three entries, non-exact matches */
+ TEST_CASE(0, three_entries, 0, 435e6),
+ TEST_CASE(0, three_entries, 1, 435e6),
+ TEST_CASE(0, three_entries, 2, 435e6),
+ TEST_CASE(1, three_entries, 0, 2.0e9),
+ TEST_CASE(1, three_entries, 1, 2.0e9),
+ TEST_CASE(1, three_entries, 2, 2.0e9),
+ TEST_CASE(2, three_entries, 0, 3.8e9),
+ TEST_CASE(2, three_entries, 1, 3.8e9),
+ TEST_CASE(2, three_entries, 2, 3.8e9),
+
+ /* Larger table, lower limits */
+ TEST_CASE(0, entries, 0, 0),
+ TEST_CASE(0, entries, 0, 300e6),
+ TEST_CASE(0, entries, 0, 350e6),
+ TEST_CASE(0, entries, 17, 0),
+ TEST_CASE(0, entries, 17, 300e6),
+ TEST_CASE(0, entries, 17, 350e6),
+ TEST_CASE(0, entries, 35, 0),
+ TEST_CASE(0, entries, 35, 300e6),
+ TEST_CASE(0, entries, 35, 350e6),
+
+ /* Larger table, upper limits */
+ TEST_CASE(35, entries, 0, 3.8e9),
+ TEST_CASE(35, entries, 0, 4e9),
+ TEST_CASE(35, entries, 17, 3.8e9),
+ TEST_CASE(35, entries, 17, 4e9),
+ TEST_CASE(35, entries, 35, 3.8e9),
+ TEST_CASE(35, entries, 35, 4e9),
+
+ /* Larger table, exact matches */
+ TEST_CASE(4, entries, 0, 700e6),
+ TEST_CASE(4, entries, 4, 700e6),
+ TEST_CASE(4, entries, 15, 700e6),
+ TEST_CASE(4, entries, 30, 700e6),
+ TEST_CASE(4, entries, 35, 700e6),
+
+ TEST_CASE(12, entries, 0, 1.5e9),
+ TEST_CASE(12, entries, 12, 1.5e9),
+ TEST_CASE(12, entries, 15, 1.5e9),
+ TEST_CASE(12, entries, 30, 1.5e9),
+ TEST_CASE(12, entries, 35, 1.5e9),
+
+ TEST_CASE(30, entries, 0, 3.3e9),
+ TEST_CASE(30, entries, 10, 3.3e9),
+ TEST_CASE(30, entries, 20, 3.3e9),
+ TEST_CASE(30, entries, 30, 3.3e9),
+ TEST_CASE(30, entries, 35, 3.3e9),
+
+ /* Larger table, approximate matches */
+ TEST_CASE(4, entries, 0, 701e6),
+ TEST_CASE(4, entries, 4, 701e6),
+ TEST_CASE(4, entries, 15, 701e6),
+ TEST_CASE(4, entries, 30, 701e6),
+ TEST_CASE(4, entries, 35, 701e6),
+
+ TEST_CASE(12, entries, 0, 1.59e9),
+ TEST_CASE(12, entries, 12, 1.59e9),
+ TEST_CASE(12, entries, 15, 1.59e9),
+ TEST_CASE(12, entries, 30, 1.59e9),
+ TEST_CASE(12, entries, 35, 1.59e9),
+
+ TEST_CASE(30, entries, 0, 3.35e9),
+ TEST_CASE(30, entries, 10, 3.35e9),
+ TEST_CASE(30, entries, 20, 3.35e9),
+ TEST_CASE(30, entries, 30, 3.35e9),
+ TEST_CASE(30, entries, 35, 3.35e9),
+};
+
+static inline void print_entry(const struct dc_cal_tbl *t,
+ const char *prefix, int idx)
+{
+ if (idx >= 0) {
+ fprintf(stderr, "%s: %u Hz\n", prefix, t->entries[idx].freq);
+ } else {
+ fprintf(stderr, "%s: None (%d)\n", prefix, idx);
+ }
+}
+
+int main(void)
+{
+ unsigned int i;
+ unsigned int num_failures = 0;
+
+ for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+ const int expected_idx = tests[i].expected_idx;
+ const int entry_idx = dc_cal_tbl_lookup(&tests[i].tbl, tests[i].freq);
+
+ if (tests[i].check_result && entry_idx != expected_idx) {
+ fprintf(stderr, "Test case %u: failed.\n", i);
+ print_entry(&tests[i].tbl, " Got", entry_idx);
+ print_entry(&tests[i].tbl, " Expected", expected_idx);
+ num_failures++;
+ } else {
+ printf("Test case %u: passed.\n", i);
+ }
+ }
+
+ return num_failures;
+}
+#endif
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/calibration.h b/Radio/HW/BladeRF/src/board/bladerf1/calibration.h
new file mode 100644
index 0000000..98e28a5
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf1/calibration.h
@@ -0,0 +1,107 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef BLADERF1_CALIBRATION_H_
+#define BLADERF1_CALIBRATION_H_
+
+#include <stdint.h>
+
+#include <libbladeRF.h>
+
+struct dc_cal_entry {
+ unsigned int freq; /* Frequency (Hz) associated with this entry */
+ int16_t dc_i;
+ int16_t dc_q;
+
+ int16_t max_dc_i;
+ int16_t max_dc_q;
+ int16_t mid_dc_i;
+ int16_t mid_dc_q;
+ int16_t min_dc_i;
+ int16_t min_dc_q;
+};
+
+struct dc_cal_tbl {
+ uint32_t version;
+ uint32_t n_entries;
+ struct bladerf_lms_dc_cals reg_vals;
+
+ unsigned int curr_idx;
+ struct dc_cal_entry *entries; /* Sorted (increasing) by freq */
+};
+
+extern struct dc_cal_tbl rx_cal_test;
+
+/**
+ * Get the index of an (approximate) match from the specific dc cal table
+ *
+ * @param[in] tbl Table to search
+ * @param[in] freq Desired frequency
+ *
+ * @return index into tbl->entries[].
+ */
+unsigned int dc_cal_tbl_lookup(const struct dc_cal_tbl *tbl, unsigned int freq);
+
+/**
+ * Get the DC cal values associated with the specified frequencies. If the
+ * specified frequency is not in the table, the DC calibration values will
+ * be interpolated from surrounding entries.
+ *
+ * @param[in] tbl Table to search
+ * @param[in] freq Desired frequency
+ * @param[out] entry Found or interpolated DC calibration values
+ */
+void dc_cal_tbl_entry(const struct dc_cal_tbl *tbl,
+ unsigned int freq,
+ struct dc_cal_entry *entry);
+
+/**
+ * Load a DC calibration table from the provided data
+ *
+ * @param[in] buf Packed table data
+ * @param[in] len Length of packed data, in bytes
+ *
+ * @return Loaded DC calibration table, or NULL on error
+ */
+struct dc_cal_tbl *dc_cal_tbl_load(const uint8_t *buf, size_t buf_len);
+
+/**
+ * Load a DC calibration table from an image file
+ *
+ * @param[in] dev bladeRF device handle
+ * @param[out] tbl DC calibration Table
+ * @param[in] img_file Path to image file
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int dc_cal_tbl_image_load(struct bladerf *dev,
+ struct dc_cal_tbl **tbl, const char *img_file);
+
+/**
+ * Free a DC calibration table
+ *
+ * @param[inout] tbl Pointer to table to free
+ *
+ * The table pointer will be set to NULL after freeing it.
+ */
+void dc_cal_tbl_free(struct dc_cal_tbl **tbl);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/capabilities.c b/Radio/HW/BladeRF/src/board/bladerf1/capabilities.c
new file mode 100644
index 0000000..8454f5c
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf1/capabilities.c
@@ -0,0 +1,118 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2015 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <inttypes.h>
+
+#include "log.h"
+#include "helpers/version.h"
+
+#include "capabilities.h"
+
+uint64_t bladerf1_get_fw_capabilities(const struct bladerf_version *fw_version)
+{
+ uint64_t capabilities = 0;
+
+ if (version_fields_greater_or_equal(fw_version, 1, 7, 1)) {
+ capabilities |= BLADERF_CAP_FW_LOOPBACK;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 1, 8, 0)) {
+ capabilities |= BLADERF_CAP_QUERY_DEVICE_READY;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 1, 9, 0)) {
+ capabilities |= BLADERF_CAP_READ_FW_LOG_ENTRY;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 2, 3, 0)) {
+ capabilities |= BLADERF_CAP_FW_FLASH_ID;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 2, 3, 1)) {
+ capabilities |= BLADERF_CAP_FW_FPGA_SOURCE;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 2, 4, 0)) {
+ capabilities |= BLADERF_CAP_FW_SHORT_PACKET;
+ }
+
+ return capabilities;
+}
+
+uint64_t bladerf1_get_fpga_capabilities(const struct bladerf_version *fpga_version)
+{
+ uint64_t capabilities = 0;
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 0, 4)) {
+ capabilities |= BLADERF_CAP_UPDATED_DAC_ADDR;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 0, 5)) {
+ capabilities |= BLADERF_CAP_XB200;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 1, 0)) {
+ capabilities |= BLADERF_CAP_TIMESTAMPS;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 2, 0)) {
+ capabilities |= BLADERF_CAP_FPGA_TUNING;
+ capabilities |= BLADERF_CAP_SCHEDULED_RETUNE;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 3, 0)) {
+ capabilities |= BLADERF_CAP_PKT_HANDLER_FMT;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 3, 2)) {
+ capabilities |= BLADERF_CAP_VCTCXO_TRIMDAC_READ;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 4, 0)) {
+ capabilities |= BLADERF_CAP_ATOMIC_NINT_NFRAC_WRITE;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 4, 1)) {
+ capabilities |= BLADERF_CAP_MASKED_XBIO_WRITE;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 5, 0)) {
+ capabilities |= BLADERF_CAP_VCTCXO_TAMING_MODE;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 6, 0)) {
+ capabilities |= BLADERF_CAP_TRX_SYNC_TRIG;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 7, 0)) {
+ capabilities |= BLADERF_CAP_AGC_DC_LUT;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 12, 0)) {
+ capabilities |= BLADERF_CAP_FPGA_PACKET_META;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 15, 0)) {
+ capabilities |= BLADERF_CAP_FPGA_8BIT_SAMPLES;
+ }
+
+ return capabilities;
+}
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/capabilities.h b/Radio/HW/BladeRF/src/board/bladerf1/capabilities.h
new file mode 100644
index 0000000..5b2eb43
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf1/capabilities.h
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2015-2017 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* This file defines device capabilities added across libbladeRF, FX3, and FPGA
+ * versions that we can check for */
+
+#ifndef BLADERF1_CAPABILITIES_H_
+#define BLADERF1_CAPABILITIES_H_
+
+#include <stdint.h>
+
+#include "board/board.h"
+#include "helpers/have_cap.h"
+
+/**
+ * Determine device's firmware capabilities.
+ *
+ * @param[in] fw_version Firmware version
+ *
+ * @return Capabilities bitmask
+ */
+uint64_t bladerf1_get_fw_capabilities(const struct bladerf_version *fw_version);
+
+/**
+ * Add capability bits based upon FPGA version stored in the device handle
+ *
+ * @param[in] fpga_version FPGA version
+ *
+ * @return Capabilities bitmask
+ */
+uint64_t bladerf1_get_fpga_capabilities(
+ const struct bladerf_version *fpga_version);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/compatibility.c b/Radio/HW/BladeRF/src/board/bladerf1/compatibility.c
new file mode 100644
index 0000000..6d76d3f
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf1/compatibility.c
@@ -0,0 +1,75 @@
+#include "host_config.h"
+
+#include "helpers/version.h"
+
+/* Firmware-FPGA compatibility tables
+ *
+ * This list should be kept in decending order, such that the most recent
+ * versions are first, and the last entry should contain the earliest version
+ * that libbladeRF supports.
+ */
+
+#define VERSION(major, minor, patch) { major, minor, patch, NULL }
+
+static const struct compat fw_compat[] = {
+ /* Firmware requires >= FPGA */
+ { VERSION(2, 4, 0), VERSION(0, 6, 0) },
+ { VERSION(2, 3, 2), VERSION(0, 0, 2) },
+ { VERSION(2, 3, 1), VERSION(0, 0, 2) },
+ { VERSION(2, 3, 0), VERSION(0, 0, 2) },
+ { VERSION(2, 2, 0), VERSION(0, 0, 2) },
+ { VERSION(2, 1, 1), VERSION(0, 0, 2) },
+ { VERSION(2, 1, 0), VERSION(0, 0, 2) },
+ { VERSION(2, 0, 0), VERSION(0, 0, 2) },
+ { VERSION(1, 9, 1), VERSION(0, 0, 2) },
+ { VERSION(1, 9, 0), VERSION(0, 0, 2) },
+ { VERSION(1, 8, 1), VERSION(0, 0, 2) },
+ { VERSION(1, 8, 0), VERSION(0, 0, 2) },
+ { VERSION(1, 7, 1), VERSION(0, 0, 2) },
+ { VERSION(1, 7, 0), VERSION(0, 0, 2) },
+ { VERSION(1, 6, 1), VERSION(0, 0, 2) },
+ { VERSION(1, 6, 0), VERSION(0, 0, 1) },
+};
+
+const struct version_compat_table bladerf1_fw_compat_table = {fw_compat, ARRAY_SIZE(fw_compat)};
+
+static const struct compat fpga_compat[] = {
+ /* FPGA requires >= Firmware */
+ { VERSION(0, 15, 1), VERSION(2, 4, 0) },
+ { VERSION(0, 15, 0), VERSION(2, 4, 0) },
+ { VERSION(0, 14, 0), VERSION(2, 4, 0) },
+ { VERSION(0, 12, 0), VERSION(2, 2, 0) },
+ { VERSION(0, 11, 1), VERSION(2, 1, 0) },
+ { VERSION(0, 11, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 10, 2), VERSION(1, 6, 1) },
+ { VERSION(0, 10, 1), VERSION(1, 6, 1) },
+ { VERSION(0, 10, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 9, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 8, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 7, 3), VERSION(1, 6, 1) },
+ { VERSION(0, 7, 2), VERSION(1, 6, 1) },
+ { VERSION(0, 7, 1), VERSION(1, 6, 1) },
+ { VERSION(0, 7, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 6, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 5, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 4, 1), VERSION(1, 6, 1) },
+ { VERSION(0, 4, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 3, 5), VERSION(1, 6, 1) },
+ { VERSION(0, 3, 4), VERSION(1, 6, 1) },
+ { VERSION(0, 3, 3), VERSION(1, 6, 1) },
+ { VERSION(0, 3, 2), VERSION(1, 6, 1) },
+ { VERSION(0, 3, 1), VERSION(1, 6, 1) },
+ { VERSION(0, 3, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 2, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 1, 2), VERSION(1, 6, 1) },
+ { VERSION(0, 1, 1), VERSION(1, 6, 1) },
+ { VERSION(0, 1, 0), VERSION(1, 6, 1) },
+ { VERSION(0, 0, 6), VERSION(1, 6, 1) },
+ { VERSION(0, 0, 5), VERSION(1, 6, 1) },
+ { VERSION(0, 0, 4), VERSION(1, 6, 1) },
+ { VERSION(0, 0, 3), VERSION(1, 6, 1) },
+ { VERSION(0, 0, 2), VERSION(1, 6, 1) },
+ { VERSION(0, 0, 1), VERSION(1, 6, 0) },
+};
+
+const struct version_compat_table bladerf1_fpga_compat_table = {fpga_compat, ARRAY_SIZE(fpga_compat)};
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/compatibility.h b/Radio/HW/BladeRF/src/board/bladerf1/compatibility.h
new file mode 100644
index 0000000..b91d5ef
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf1/compatibility.h
@@ -0,0 +1,9 @@
+#ifndef BLADERF1_COMPATIBILITY_H_
+#define BLADERF1_COMPATIBILITY_H_
+
+#include "helpers/version.h"
+
+extern const struct version_compat_table bladerf1_fw_compat_table;
+extern const struct version_compat_table bladerf1_fpga_compat_table;
+
+#endif
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/flash.c b/Radio/HW/BladeRF/src/board/bladerf1/flash.c
new file mode 100644
index 0000000..6850543
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf1/flash.c
@@ -0,0 +1,543 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "log.h"
+#include "minmax.h"
+#include "misc.h"
+#include "conversions.h"
+
+#include "bladeRF.h"
+#include "board/board.h"
+
+#include "driver/spi_flash.h"
+
+#include "flash.h"
+
+#define OTP_BUFFER_SIZE 256
+
+int spi_flash_write_fx3_fw(struct bladerf *dev, const uint8_t *image, size_t len)
+{
+ int status;
+ uint8_t *readback_buf;
+ uint8_t *padded_image;
+ uint32_t padded_image_len;
+
+ /* Pad firwmare data out to a page size */
+ const uint32_t page_size = dev->flash_arch->psize_bytes;
+ const uint32_t padding_len =
+ (len % page_size == 0) ? 0 : page_size - (len % page_size);
+
+ /* Flash page where FX3 firmware starts */
+ const uint32_t flash_page_fw = BLADERF_FLASH_ADDR_FIRMWARE /
+ dev->flash_arch->psize_bytes;
+
+ /* Flash erase block where FX3 firmware starts */
+ const uint32_t flash_eb_fw = BLADERF_FLASH_ADDR_FIRMWARE /
+ dev->flash_arch->ebsize_bytes;
+
+ /** Length of firmware region of flash, in erase blocks */
+ const uint32_t flash_eb_len_fw = BLADERF_FLASH_BYTE_LEN_FIRMWARE /
+ dev->flash_arch->ebsize_bytes;
+
+ if (len >= (UINT32_MAX - padding_len)) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ padded_image_len = (uint32_t) len + padding_len;
+
+ readback_buf = malloc(padded_image_len);
+ if (readback_buf == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ padded_image = malloc(padded_image_len);
+ if (padded_image == NULL) {
+ free(readback_buf);
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Copy image */
+ memcpy(padded_image, image, len);
+
+ /* Clear the padded region */
+ memset(padded_image + len, 0xFF, padded_image_len - len);
+
+ /* Erase the entire firmware region */
+ status = spi_flash_erase(dev, flash_eb_fw, flash_eb_len_fw);
+ if (status != 0) {
+ log_debug("Failed to erase firmware region: %s\n",
+ bladerf_strerror(status));
+ goto error;
+ }
+
+ /* Convert the image length to pages */
+ padded_image_len /= page_size;
+
+ /* Write the firmware image to flash */
+ status = spi_flash_write(dev, padded_image,
+ flash_page_fw, padded_image_len);
+
+ if (status < 0) {
+ log_debug("Failed to write firmware: %s\n", bladerf_strerror(status));
+ goto error;
+ }
+
+ /* Read back and double-check what we just wrote */
+ status = spi_flash_verify(dev, readback_buf, padded_image,
+ flash_page_fw, padded_image_len);
+ if (status != 0) {
+ log_debug("Flash verification failed: %s\n", bladerf_strerror(status));
+ goto error;
+ }
+
+error:
+ free(padded_image);
+ free(readback_buf);
+ return status;
+}
+
+static inline void fill_fpga_metadata_page(struct bladerf *dev,
+ uint8_t *metadata,
+ size_t actual_bitstream_len)
+{
+ char len_str[12];
+ int idx = 0;
+
+ memset(len_str, 0, sizeof(len_str));
+ memset(metadata, 0xff, dev->flash_arch->psize_bytes);
+
+ snprintf(len_str, sizeof(len_str), "%u",
+ (unsigned int)actual_bitstream_len);
+
+ binkv_encode_field((char *)metadata, dev->flash_arch->psize_bytes,
+ &idx, "LEN", len_str);
+}
+
+static inline size_t get_flash_eb_len_fpga(struct bladerf *dev)
+{
+ int status;
+ size_t fpga_bytes;
+ size_t eb_count;
+
+ status = dev->board->get_fpga_bytes(dev, &fpga_bytes);
+ if (status < 0) {
+ return status;
+ }
+
+ eb_count = fpga_bytes / dev->flash_arch->ebsize_bytes;
+
+ if ((fpga_bytes % dev->flash_arch->ebsize_bytes) > 0) {
+ // Round up to nearest full block
+ ++eb_count;
+ }
+
+ return eb_count;
+}
+
+#define METADATA_LEN 256
+
+int spi_flash_write_fpga_bitstream(struct bladerf *dev,
+ const uint8_t *bitstream,
+ size_t len)
+{
+ /* Pad data to be page-aligned */
+ const uint32_t page_size = dev->flash_arch->psize_bytes;
+ const uint32_t padding_len =
+ (len % page_size == 0) ? 0 : page_size - (len % page_size);
+
+ /** Flash page where FPGA metadata and bitstream start */
+ const uint32_t flash_page_fpga =
+ BLADERF_FLASH_ADDR_FPGA / dev->flash_arch->psize_bytes;
+
+ /** Flash erase block where FPGA metadata and bitstream start */
+ const uint32_t flash_eb_fpga =
+ BLADERF_FLASH_ADDR_FPGA / dev->flash_arch->ebsize_bytes;
+
+ /** Length of entire FPGA region, in units of erase blocks */
+ const uint32_t flash_eb_len_fpga = (uint32_t)get_flash_eb_len_fpga(dev);
+
+ assert(METADATA_LEN <= page_size);
+
+ int status;
+ uint8_t *readback_buf;
+ uint8_t *padded_bitstream;
+ uint8_t metadata[METADATA_LEN];
+ uint32_t padded_bitstream_len;
+
+ if (len >= (UINT32_MAX - padding_len)) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ padded_bitstream_len = (uint32_t)len + padding_len;
+
+ /* Fill in metadata with the *actual* FPGA bitstream length */
+ fill_fpga_metadata_page(dev, metadata, len);
+
+ readback_buf = malloc(padded_bitstream_len);
+ if (readback_buf == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ padded_bitstream = malloc(padded_bitstream_len);
+ if (padded_bitstream == NULL) {
+ free(readback_buf);
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Copy bitstream */
+ memcpy(padded_bitstream, bitstream, len);
+
+ /* Clear the padded region */
+ memset(padded_bitstream + len, 0xFF, padded_bitstream_len - len);
+
+ /* Erase FPGA metadata and bitstream region */
+ status = spi_flash_erase(dev, flash_eb_fpga, flash_eb_len_fpga);
+ if (status != 0) {
+ log_debug("Failed to erase FPGA meta & bitstream regions: %s\n",
+ bladerf_strerror(status));
+ goto error;
+ }
+
+ /* Write the metadata page */
+ status = spi_flash_write(dev, metadata, flash_page_fpga, 1);
+ if (status != 0) {
+ log_debug("Failed to write FPGA metadata page: %s\n",
+ bladerf_strerror(status));
+ goto error;
+ }
+
+ /* Convert the padded bitstream length to pages */
+ padded_bitstream_len /= page_size;
+
+ /* Write the padded bitstream */
+ status = spi_flash_write(dev, padded_bitstream, flash_page_fpga + 1,
+ padded_bitstream_len);
+ if (status != 0) {
+ log_debug("Failed to write bitstream: %s\n", bladerf_strerror(status));
+ goto error;
+ }
+
+ /* Read back and verify metadata */
+ status = spi_flash_verify(dev, readback_buf, metadata, flash_page_fpga, 1);
+ if (status != 0) {
+ log_debug("Failed to verify metadata: %s\n", bladerf_strerror(status));
+ goto error;
+ }
+
+ /* Read back and verify the bitstream data */
+ status = spi_flash_verify(dev, readback_buf, padded_bitstream,
+ flash_page_fpga + 1, padded_bitstream_len);
+ if (status != 0) {
+ log_debug("Failed to verify bitstream data: %s\n",
+ bladerf_strerror(status));
+ goto error;
+ }
+
+error:
+ free(padded_bitstream);
+ free(readback_buf);
+ return status;
+}
+
+int spi_flash_erase_fpga(struct bladerf *dev)
+{
+ int status;
+ size_t fpga_bytes;
+
+ status = dev->board->get_fpga_bytes(dev, &fpga_bytes);
+ if (status < 0) {
+ return status;
+ }
+
+ /** Flash erase block where FPGA metadata and bitstream start */
+ const uint32_t flash_eb_fpga =
+ BLADERF_FLASH_ADDR_FPGA / dev->flash_arch->ebsize_bytes;
+
+ /** Length of entire FPGA region, in units of erase blocks */
+ const uint32_t flash_eb_len_fpga = (uint32_t)get_flash_eb_len_fpga(dev);
+
+ /* Erase the entire FPGA region, including both autoload metadata and the
+ * actual bitstream data */
+ return spi_flash_erase(dev, flash_eb_fpga, flash_eb_len_fpga);
+}
+
+int spi_flash_read_otp(struct bladerf *dev, char *field,
+ char *data, size_t data_size)
+{
+ int status;
+ char otp[OTP_BUFFER_SIZE];
+
+ memset(otp, 0xff, OTP_BUFFER_SIZE);
+
+ status = dev->backend->get_otp(dev, otp);
+ if (status < 0)
+ return status;
+ else
+ return binkv_decode_field(otp, OTP_BUFFER_SIZE, field, data, data_size);
+}
+
+int spi_flash_read_cal(struct bladerf *dev, char *field,
+ char *data, size_t data_size)
+{
+ int status;
+ char cal[CAL_BUFFER_SIZE];
+
+ status = dev->backend->get_cal(dev, cal);
+ if (status < 0)
+ return status;
+ else
+ return binkv_decode_field(cal, CAL_BUFFER_SIZE, field, data, data_size);
+}
+
+int spi_flash_read_serial(struct bladerf *dev, char *serial_buf)
+{
+ int status;
+
+ status = spi_flash_read_otp(dev, "S", serial_buf, BLADERF_SERIAL_LENGTH - 1);
+
+ if (status < 0) {
+ log_info("Unable to fetch serial number. Defaulting to 0's.\n");
+ memset(dev->ident.serial, '0', BLADERF_SERIAL_LENGTH - 1);
+
+ /* Treat this as non-fatal */
+ status = 0;
+ }
+
+ serial_buf[BLADERF_SERIAL_LENGTH - 1] = '\0';
+
+ return status;
+}
+
+int spi_flash_read_vctcxo_trim(struct bladerf *dev, uint16_t *dac_trim)
+{
+ int status;
+ bool ok;
+ int16_t trim;
+ char tmp[7] = { 0 };
+
+ status = spi_flash_read_cal(dev, "DAC", tmp, sizeof(tmp) - 1);
+ if (status < 0) {
+ return status;
+ }
+
+ trim = str2uint(tmp, 0, 0xffff, &ok);
+ if (ok == false) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ *dac_trim = trim;
+
+ return 0;
+}
+
+int spi_flash_read_fpga_size(struct bladerf *dev, bladerf_fpga_size *fpga_size)
+{
+ int status;
+ char tmp[7] = { 0 };
+
+ status = spi_flash_read_cal(dev, "B", tmp, sizeof(tmp) - 1);
+ if (status < 0) {
+ return status;
+ }
+
+ if (!strcmp("40", tmp)) {
+ *fpga_size = BLADERF_FPGA_40KLE;
+ } else if(!strcmp("115", tmp)) {
+ *fpga_size = BLADERF_FPGA_115KLE;
+ } else if(!strcmp("A4", tmp)) {
+ *fpga_size = BLADERF_FPGA_A4;
+ } else if(!strcmp("A5", tmp)) {
+ *fpga_size = BLADERF_FPGA_A5;
+ } else if(!strcmp("A9", tmp)) {
+ *fpga_size = BLADERF_FPGA_A9;
+ } else {
+ *fpga_size = BLADERF_FPGA_UNKNOWN;
+ }
+
+ return status;
+}
+
+int spi_flash_read_flash_id(struct bladerf *dev, uint8_t *mid, uint8_t *did)
+{
+ int status;
+
+ status = dev->backend->get_flash_id(dev, mid, did);
+
+ return status;
+}
+
+int spi_flash_decode_flash_architecture(struct bladerf *dev,
+ bladerf_fpga_size *fpga_size)
+{
+ int status;
+ struct bladerf_flash_arch *flash_arch;
+
+ status = 0;
+ flash_arch = dev->flash_arch;
+
+ /* Fill in defaults */
+ flash_arch->tsize_bytes = 32 << 17; /* 32 Mbit */
+ flash_arch->psize_bytes = 256;
+ flash_arch->ebsize_bytes = 64 << 10; /* 64 Kbyte */
+ flash_arch->status = STATUS_ASSUMED;
+
+ /* First try to decode the MID/DID of the flash chip */
+ switch( flash_arch->manufacturer_id ) {
+ case 0xC2: /* MACRONIX */
+ log_verbose( "Found SPI flash manufacturer: MACRONIX.\n" );
+ switch( flash_arch->device_id ) {
+ case 0x36:
+ log_verbose( "Found SPI flash device: MX25U3235E (32 Mbit).\n" );
+ flash_arch->tsize_bytes = 32 << 17;
+ flash_arch->status = STATUS_SUCCESS;
+ break;
+ default:
+ log_debug( "Unknown Macronix flash device ID.\n" );
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+ break;
+
+ case 0xEF: /* WINBOND */
+ log_verbose( "Found SPI flash manufacturer: WINBOND.\n" );
+ switch( flash_arch->device_id ) {
+ case 0x15:
+ log_verbose( "Found SPI flash device: W25Q32JV (32 Mbit).\n" );
+ flash_arch->tsize_bytes = 32 << 17;
+ flash_arch->status = STATUS_SUCCESS;
+ break;
+ case 0x16:
+ log_verbose( "Found SPI flash device: W25Q64JV (64 Mbit).\n" );
+ flash_arch->tsize_bytes = 64 << 17;
+ flash_arch->status = STATUS_SUCCESS;
+ break;
+ case 0x17:
+ log_verbose( "Found SPI flash device: W25Q128JV (128 Mbit).\n" );
+ flash_arch->tsize_bytes = 128 << 17;
+ flash_arch->status = STATUS_SUCCESS;
+ break;
+ default:
+ log_debug( "Unknown Winbond flash device ID [0x%02X].\n" , flash_arch->device_id );
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+ break;
+
+ default:
+ log_debug( "Unknown flash manufacturer ID.\n" );
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* Could not decode flash MID/DID, so assume based on FPGA size */
+ if( status < 0 || flash_arch->status != STATUS_SUCCESS ) {
+ if( (fpga_size == NULL) || (*fpga_size == BLADERF_FPGA_UNKNOWN) ) {
+ log_debug( "Could not decode flash manufacturer/device ID and have "
+ "an unknown FPGA size. Assume default flash "
+ "architecture.\n" );
+ } else {
+ switch( *fpga_size ) {
+ case BLADERF_FPGA_A9:
+ flash_arch->tsize_bytes = 128 << 17;
+ break;
+ default:
+ flash_arch->tsize_bytes = 32 << 17;
+ }
+ log_debug( "Could not decode flash manufacturer/device ID, but "
+ "found a %u kLE FPGA. Setting the most probable "
+ "flash architecture.\n", *fpga_size );
+ }
+ }
+
+ flash_arch->num_pages = flash_arch->tsize_bytes / flash_arch->psize_bytes;
+ flash_arch->num_ebs = flash_arch->tsize_bytes / flash_arch->ebsize_bytes;
+
+ log_verbose("SPI flash total size = %u Mbit\n", (flash_arch->tsize_bytes >> 17));
+ log_verbose("SPI flash page size = %u bytes\n", flash_arch->psize_bytes);
+ log_verbose("SPI flash erase block size = %u bytes\n", flash_arch->ebsize_bytes);
+ log_verbose("SPI flash number of pages = %u\n", flash_arch->num_pages);
+ log_verbose("SPI flash number of erase blocks = %u pages\n", flash_arch->num_ebs);
+
+ return status;
+}
+
+
+int binkv_decode_field(char *ptr, int len, char *field,
+ char *val, size_t maxlen)
+{
+ int c;
+ unsigned char *ub, *end;
+ unsigned short a1, a2;
+ size_t flen, wlen;
+
+ flen = strlen(field);
+
+ ub = (unsigned char *)ptr;
+ end = ub + len;
+ while (ub < end) {
+ c = *ub;
+
+ if (c == 0xff) // flash and OTP are 0xff if they've never been written to
+ break;
+
+ a1 = LE16_TO_HOST(*(unsigned short *)(&ub[c+1])); // read checksum
+ a2 = zcrc(ub, c+1); // calculate checksum
+
+ if (a1 == a2) {
+ if (!strncmp((char *)ub + 1, field, flen)) {
+ wlen = min_sz(c - flen, maxlen);
+ strncpy(val, (char *)ub + 1 + flen, wlen);
+ val[wlen] = 0;
+ return 0;
+ }
+ } else {
+ log_debug( "%s: Field checksum mismatch\n", __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ }
+ ub += c + 3; //skip past `c' bytes, 2 byte CRC field, and 1 byte len field
+ }
+ return BLADERF_ERR_INVAL;
+}
+
+int binkv_encode_field(char *ptr, int len, int *idx,
+ const char *field, const char *val)
+{
+ int vlen, flen, tlen;
+ flen = (int)strlen(field);
+ vlen = (int)strlen(val);
+ tlen = flen + vlen + 1;
+
+ if (tlen >= 256 || *idx + tlen >= len)
+ return BLADERF_ERR_MEM;
+
+ ptr[*idx] = flen + vlen;
+ strcpy(&ptr[*idx + 1], field);
+ strcpy(&ptr[*idx + 1 + flen], val);
+ *(unsigned short *)(&ptr[*idx + tlen ]) = HOST_TO_LE16(zcrc((uint8_t *)&ptr[*idx ], tlen));
+ *idx += tlen + 2;
+ return 0;
+}
+
+int binkv_add_field(char *buf, int buf_len, const char *field_name, const char *val)
+{
+ int dummy_idx = 0;
+ int i = 0;
+ int rv;
+
+ /* skip to the end, ignoring crc (don't want to further corrupt partially
+ * corrupt data) */
+ while(i < buf_len) {
+ uint8_t field_len = buf[i];
+
+ if(field_len == 0xff)
+ break;
+
+ /* skip past `field_len' bytes, 2 byte CRC field, and 1 byte len
+ * field */
+ i += field_len + 3;
+ }
+
+ rv = binkv_encode_field(buf + i, buf_len - i, &dummy_idx, field_name, val);
+ if(rv < 0)
+ return rv;
+
+ return 0;
+}
+
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/flash.h b/Radio/HW/BladeRF/src/board/bladerf1/flash.h
new file mode 100644
index 0000000..be8f02d
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf1/flash.h
@@ -0,0 +1,172 @@
+#ifndef BLADERF1_FLASH_H_
+#define BLADERF1_FLASH_H_
+
+#include <libbladeRF.h>
+
+#include <stdint.h>
+
+/**
+ * Write the provided data to the FX3 Firmware region to flash.
+ *
+ * This function does no validation of the data (i.e., that it's valid FW).
+ *
+ * @param dev bladeRF handle
+ * @param[in] image Firmware image data
+ * @param[in] len Length of firmware image data
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int spi_flash_write_fx3_fw(struct bladerf *dev,
+ const uint8_t *image,
+ size_t len);
+
+/**
+ * Write the provided FPGA bitstream to flash and enable autoloading via
+ * writing the associated metadata.
+ *
+ * @param dev bladeRF handle
+ * @param[in] bitstream FPGA bitstream data
+ * @param[in] len Length of the bitstream data
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int spi_flash_write_fpga_bitstream(struct bladerf *dev,
+ const uint8_t *bitstream,
+ size_t len);
+
+/**
+ * Erase FPGA metadata and bitstream regions of flash.
+ *
+ * @param dev bladeRF handle
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int spi_flash_erase_fpga(struct bladerf *dev);
+
+/**
+ * Read data from OTP ("otp") section of flash.
+ *
+ * @param dev Device handle
+ * @param[in] field OTP field
+ * @param[out] data Populated with retrieved data
+ * @param[in] data_size Size of the data to read
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int spi_flash_read_otp(struct bladerf *dev,
+ char *field,
+ char *data,
+ size_t data_size);
+
+/**
+ * Read data from calibration ("cal") section of flash.
+ *
+ * @param dev Device handle
+ * @param[in] field Calibration field
+ * @param[out] data Populated with retrieved data
+ * @param[in] data_size Size of the data to read
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int spi_flash_read_cal(struct bladerf *dev,
+ char *field,
+ char *data,
+ size_t data_size);
+
+/**
+ * Retrieve the device serial from flash.
+ *
+ * @pre The provided buffer is BLADERF_SERIAL_LENGTH in size
+ *
+ * @param dev Device handle. On success, serial field is updated
+ * @param[out] serial_buf Populated with device serial
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int spi_flash_read_serial(struct bladerf *dev, char *serial_buf);
+
+/**
+ * Retrieve VCTCXO calibration value from flash.
+ *
+ * @param dev Device handle
+ * @param[out] dac_trim DAC trim
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int spi_flash_read_vctcxo_trim(struct bladerf *dev, uint16_t *dac_trim);
+
+/**
+ * Retrieve FPGA size variant from flash.
+ *
+ * @param dev Device handle.
+ * @param[out] fpga_size FPGA size
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int spi_flash_read_fpga_size(struct bladerf *dev, bladerf_fpga_size *fpga_size);
+
+/**
+ * Retrieve SPI flash manufacturer ID and device ID.
+ *
+ * @param dev Device handle.
+ * @param[out] mid Flash manufacturer ID
+ * @param[out] did Flash device ID
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int spi_flash_read_flash_id(struct bladerf *dev, uint8_t *mid, uint8_t *did);
+
+/**
+ * Decode SPI flash architecture given manufacturer and device IDs.
+ *
+ * @param dev Device handle.
+ * @param fpga_size FPGA size
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int spi_flash_decode_flash_architecture(struct bladerf *dev,
+ bladerf_fpga_size *fpga_size);
+
+/**
+ * Encode a binary key-value pair.
+ *
+ * @param[in] ptr Pointer to data buffer that will contain encoded
+ * data
+ * @param[in] len Length of data buffer that will contain encoded data
+ * @param[inout] idx Pointer indicating next free byte inside of data
+ * buffer that will contain encoded data
+ * @param[in] field Key of value to be stored in encoded data buffer
+ * @param[in] val Value to be stored in encoded data buffer
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int binkv_encode_field(
+ char *ptr, int len, int *idx, const char *field, const char *val);
+
+/**
+ * Decode a binary key-value pair.
+ *
+ * @param[in] ptr Pointer to data buffer containing encoded data
+ * @param[in] len Length of data buffer containing encoded data
+ * @param[in] field Key of value to be decoded in encoded data buffer
+ * @param[out] val Value to be retrieved from encoded data buffer
+ * @param[in] maxlen Maximum length of value to be retrieved
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int binkv_decode_field(
+ char *ptr, int len, char *field, char *val, size_t maxlen);
+
+/**
+ * Add a binary key-value pair to an existing binkv data buffer.
+ *
+ * @param[in] buf Buffer to add field to
+ * @param[in] len Length of `buf' in bytes
+ * @param[in] field Key of value to be stored in encoded data buffer
+ * @param[in] val Value associated with key `field'
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int binkv_add_field(char *buf, int len, const char *field, const char *val);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/image.c b/Radio/HW/BladeRF/src/board/bladerf1/image.c
new file mode 100644
index 0000000..ddea18b
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf1/image.c
@@ -0,0 +1,592 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Daniel Gröber <dxld AT darkboxed DOT org>
+ * Copyright (C) 2013 Nuand, LLC.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <limits.h>
+#include <errno.h>
+
+#include <libbladeRF.h>
+
+#include "bladeRF.h"
+#include "rel_assert.h"
+#include "host_config.h"
+#include "sha256.h"
+#include "log.h"
+#include "minmax.h"
+
+#include "board/board.h"
+#include "driver/spi_flash.h"
+#include "helpers/file.h"
+
+#include "flash.h"
+
+/* These two are used interchangeably - ensure they're the same! */
+#if SHA256_DIGEST_SIZE != BLADERF_IMAGE_CHECKSUM_LEN
+#error "Image checksum size mismatch"
+#endif
+
+#define CALC_IMAGE_SIZE(len) ((size_t) \
+ ( \
+ BLADERF_IMAGE_MAGIC_LEN + \
+ BLADERF_IMAGE_CHECKSUM_LEN + \
+ 3 * sizeof(uint16_t) + \
+ sizeof(uint64_t) + \
+ BLADERF_SERIAL_LENGTH + \
+ BLADERF_IMAGE_RESERVED_LEN + \
+ 3 * sizeof(uint32_t) + \
+ len \
+ ) \
+)
+
+static const char image_magic[] = "bladeRF";
+
+#if BLADERF_OS_WINDOWS
+#include <time.h>
+static uint64_t get_timestamp()
+{
+ __time64_t now = _time64(NULL);
+ return (uint64_t)now;
+}
+#else
+#include <sys/time.h>
+static inline uint64_t get_timestamp()
+{
+ uint64_t ret;
+ struct timeval tv;
+
+ if (gettimeofday(&tv, NULL) == 0) {
+ ret = tv.tv_sec;
+ } else {
+ log_verbose("gettimeofday failed: %s\n", strerror(errno));
+ ret = 0;
+ }
+
+ return ret;
+}
+#endif
+
+static void sha256_buffer(const char *buf, size_t len,
+ char digest[SHA256_DIGEST_SIZE])
+{
+ SHA256_CTX ctx;
+
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, buf, len);
+ SHA256_Final((uint8_t*)digest, &ctx);
+}
+
+static int verify_checksum(uint8_t *buf, size_t buf_len)
+{
+ char checksum_expected[SHA256_DIGEST_SIZE];
+ char checksum_calc[SHA256_DIGEST_SIZE];
+
+ if (buf_len <= CALC_IMAGE_SIZE(0)) {
+ log_debug("Provided buffer isn't a full image\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Backup and clear the expected checksum before we calculate the
+ * expected checksum */
+ memcpy(checksum_expected, &buf[BLADERF_IMAGE_MAGIC_LEN],
+ sizeof(checksum_expected));
+
+ memset(&buf[BLADERF_IMAGE_MAGIC_LEN], 0, SHA256_DIGEST_SIZE);
+
+ sha256_buffer((const char *)buf, buf_len, checksum_calc);
+
+ if (memcmp(checksum_expected, checksum_calc, SHA256_DIGEST_SIZE) != 0) {
+ return BLADERF_ERR_CHECKSUM;
+ } else {
+ /* Restore the buffer's checksum so the caller can still use it */
+ memcpy(&buf[BLADERF_IMAGE_MAGIC_LEN], checksum_expected,
+ sizeof(checksum_expected));
+
+ return 0;
+ }
+}
+
+static bool image_type_is_valid(bladerf_image_type type) {
+ switch (type) {
+ case BLADERF_IMAGE_TYPE_RAW:
+ case BLADERF_IMAGE_TYPE_FIRMWARE:
+ case BLADERF_IMAGE_TYPE_FPGA_40KLE:
+ case BLADERF_IMAGE_TYPE_FPGA_115KLE:
+ case BLADERF_IMAGE_TYPE_FPGA_A4:
+ case BLADERF_IMAGE_TYPE_FPGA_A5:
+ case BLADERF_IMAGE_TYPE_FPGA_A9:
+ case BLADERF_IMAGE_TYPE_CALIBRATION:
+ case BLADERF_IMAGE_TYPE_RX_DC_CAL:
+ case BLADERF_IMAGE_TYPE_TX_DC_CAL:
+ case BLADERF_IMAGE_TYPE_RX_IQ_CAL:
+ case BLADERF_IMAGE_TYPE_TX_IQ_CAL:
+ case BLADERF_IMAGE_TYPE_GAIN_CAL:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/* Serialize image contents and fill in checksum */
+static size_t pack_image(struct bladerf_image *img, uint8_t *buf)
+{
+ size_t i = 0;
+ uint16_t ver_field;
+ uint32_t type, len, addr;
+ uint64_t timestamp;
+ char checksum[BLADERF_IMAGE_CHECKSUM_LEN];
+
+ memcpy(&buf[i], img->magic, BLADERF_IMAGE_MAGIC_LEN);
+ i += BLADERF_IMAGE_MAGIC_LEN;
+
+ memset(&buf[i], 0, BLADERF_IMAGE_CHECKSUM_LEN);
+ i += BLADERF_IMAGE_CHECKSUM_LEN;
+
+ ver_field = HOST_TO_BE16(img->version.major);
+ memcpy(&buf[i], &ver_field, sizeof(ver_field));
+ i += sizeof(ver_field);
+
+ ver_field = HOST_TO_BE16(img->version.minor);
+ memcpy(&buf[i], &ver_field, sizeof(ver_field));
+ i += sizeof(ver_field);
+
+ ver_field = HOST_TO_BE16(img->version.patch);
+ memcpy(&buf[i], &ver_field, sizeof(ver_field));
+ i += sizeof(ver_field);
+
+ timestamp = HOST_TO_BE64(img->timestamp);
+ memcpy(&buf[i], &timestamp, sizeof(timestamp));
+ i += sizeof(timestamp);
+
+ memcpy(&buf[i], &img->serial, BLADERF_SERIAL_LENGTH);
+ i += BLADERF_SERIAL_LENGTH;
+
+ memset(&buf[i], 0, BLADERF_IMAGE_RESERVED_LEN);
+ i += BLADERF_IMAGE_RESERVED_LEN;
+
+ type = HOST_TO_BE32((uint32_t)img->type);
+ memcpy(&buf[i], &type, sizeof(type));
+ i += sizeof(type);
+
+ addr = HOST_TO_BE32(img->address);
+ memcpy(&buf[i], &addr, sizeof(addr));
+ i += sizeof(addr);
+
+ len = HOST_TO_BE32(img->length);
+ memcpy(&buf[i], &len, sizeof(len));
+ i += sizeof(len);
+
+ memcpy(&buf[i], img->data, img->length);
+ i += img->length;
+
+ sha256_buffer((const char *)buf, i, checksum);
+ memcpy(&buf[BLADERF_IMAGE_MAGIC_LEN], checksum, BLADERF_IMAGE_CHECKSUM_LEN);
+
+ return i;
+}
+
+/* Unpack flash image from file and validate fields */
+static int unpack_image(struct bladerf_image *img, uint8_t *buf, size_t len)
+{
+ size_t i = 0;
+ uint32_t type;
+
+ /* Ensure we have at least a full set of metadata */
+ if (len < CALC_IMAGE_SIZE(0)) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ memcpy(img->magic, &buf[i], BLADERF_IMAGE_MAGIC_LEN);
+ img->magic[BLADERF_IMAGE_MAGIC_LEN] = '\0';
+ if (strncmp(img->magic, image_magic, BLADERF_IMAGE_MAGIC_LEN)) {
+ return BLADERF_ERR_INVAL;
+ }
+ i += BLADERF_IMAGE_MAGIC_LEN;
+
+ memcpy(img->checksum, &buf[i], BLADERF_IMAGE_CHECKSUM_LEN);
+ i += BLADERF_IMAGE_CHECKSUM_LEN;
+
+ memcpy(&img->version.major, &buf[i], sizeof(img->version.major));
+ i += sizeof(img->version.major);
+ img->version.major = BE16_TO_HOST(img->version.major);
+
+ memcpy(&img->version.minor, &buf[i], sizeof(img->version.minor));
+ i += sizeof(img->version.minor);
+ img->version.minor = BE16_TO_HOST(img->version.minor);
+
+ memcpy(&img->version.patch, &buf[i], sizeof(img->version.patch));
+ i += sizeof(img->version.patch);
+ img->version.patch = BE16_TO_HOST(img->version.patch);
+
+ memcpy(&img->timestamp, &buf[i], sizeof(img->timestamp));
+ i += sizeof(img->timestamp);
+ img->timestamp = BE64_TO_HOST(img->timestamp);
+
+ memcpy(img->serial, &buf[i], BLADERF_SERIAL_LENGTH);
+ img->serial[BLADERF_SERIAL_LENGTH] = '\0';
+ i += BLADERF_SERIAL_LENGTH;
+
+ memcpy(img->reserved, &buf[i], BLADERF_IMAGE_RESERVED_LEN);
+ i += BLADERF_IMAGE_RESERVED_LEN;
+
+ memcpy(&type, &buf[i], sizeof(type));
+ i += sizeof(type);
+ type = BE32_TO_HOST(type);
+
+ if (!image_type_is_valid((bladerf_image_type)type)) {
+ log_debug("Invalid type value in image: %d\n", (int)type);
+ return BLADERF_ERR_INVAL;
+ } else {
+ img->type = (bladerf_image_type)type;
+ }
+
+ memcpy(&img->address, &buf[i], sizeof(img->address));
+ i += sizeof(img->address);
+ img->address = BE32_TO_HOST(img->address);
+
+ memcpy(&img->length, &buf[i], sizeof(img->length));
+ i += sizeof(img->length);
+ img->length = BE32_TO_HOST(img->length);
+
+ if (len != CALC_IMAGE_SIZE(img->length)) {
+ log_debug("Image contains more or less data than expected\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Just slide the data over */
+ memmove(&buf[0], &buf[i], img->length);
+ img->data = buf;
+
+ return 0;
+}
+
+int bladerf_image_print_metadata(const struct bladerf_image *image) {
+ if (!image) {
+ return BLADERF_ERR_MEM;
+ }
+
+ printf("Magic: %s\n", image->magic);
+ printf("Type: %s\n", bladerf_image_type_to_string(image->type));
+ printf("Version: %d.%d.%d\n",
+ image->version.major, image->version.minor, image->version.patch);
+ printf("Timestamp: %" PRIx64 "\n", image->timestamp);
+ printf("Serial: %s\n", image->serial);
+ printf("Address: %x\n", image->address);
+ printf("Length: %u\n", image->length);
+ fflush(stdout);
+
+ return 0;
+}
+
+const char* bladerf_image_type_to_string(bladerf_image_type type) {
+ switch (type) {
+ case BLADERF_IMAGE_TYPE_INVALID:
+ return "Invalid";
+ case BLADERF_IMAGE_TYPE_RAW:
+ return "Raw Data";
+ case BLADERF_IMAGE_TYPE_FIRMWARE:
+ return "Firmware";
+ case BLADERF_IMAGE_TYPE_FPGA_40KLE:
+ return "FPGA 40 KLE Bitstream";
+ case BLADERF_IMAGE_TYPE_FPGA_115KLE:
+ return "FPGA 115 KLE Bitstream";
+ case BLADERF_IMAGE_TYPE_FPGA_A4:
+ return "FPGA A4 Bitstream";
+ case BLADERF_IMAGE_TYPE_FPGA_A9:
+ return "FPGA A9 Bitstream";
+ case BLADERF_IMAGE_TYPE_CALIBRATION:
+ return "Board Calibration";
+ case BLADERF_IMAGE_TYPE_RX_DC_CAL:
+ return "RX DC Offset Calibration Table";
+ case BLADERF_IMAGE_TYPE_TX_DC_CAL:
+ return "TX DC Offset Calibration Table";
+ case BLADERF_IMAGE_TYPE_RX_IQ_CAL:
+ return "RX IQ Balance Calibration Table";
+ case BLADERF_IMAGE_TYPE_TX_IQ_CAL:
+ return "TX IQ Balance Calibration Table";
+ case BLADERF_IMAGE_TYPE_FPGA_A5:
+ return "FPGA A5 Bitstream";
+ case BLADERF_IMAGE_TYPE_GAIN_CAL:
+ return "Gain Calibration";
+ default:
+ return "Unknown Type";
+ }
+}
+
+int bladerf_image_write(struct bladerf *dev,
+ struct bladerf_image *img, const char *file)
+{
+ int rv;
+ FILE *f = NULL;
+ uint8_t *buf = NULL;
+ size_t buf_len;
+
+ /* Ensure the format identifier is correct */
+ if (memcmp(img->magic, image_magic, BLADERF_IMAGE_MAGIC_LEN) != 0) {
+#ifdef LOGGING_ENABLED
+ char badmagic[BLADERF_IMAGE_MAGIC_LEN + 1];
+ memset(badmagic, 0, sizeof(badmagic));
+ memcpy(&badmagic, &img->magic, BLADERF_IMAGE_MAGIC_LEN);
+ log_debug("Invalid file format magic value: %s\n", badmagic);
+#endif
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Check for a valid image type */
+ if (!image_type_is_valid(img->type)) {
+ log_debug("Invalid image type: %d\n", img->type);
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Just to be tiny bit paranoid... */
+ if (!img->data) {
+ log_debug("Image data pointer is NULL\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ buf_len = CALC_IMAGE_SIZE(img->length);
+ buf = (uint8_t *)calloc(1, buf_len);
+ if (!buf) {
+ log_verbose("calloc failed: %s\n", strerror(errno));
+ return BLADERF_ERR_MEM;
+ }
+
+ /* If the type is RAW, we should only allow erase-block aligned
+ * addresses and lengths */
+ if (img->type == BLADERF_IMAGE_TYPE_RAW && img->address != 0xffffffff) {
+ if (img->address % dev->flash_arch->ebsize_bytes != 0) {
+ log_debug("Image address must be erase block-aligned for RAW.\n");
+ rv = BLADERF_ERR_INVAL;
+ goto error;
+ } else if (img->length % dev->flash_arch->ebsize_bytes != 0) {
+ log_debug("Image length must be erase block-aligned for RAW.\n");
+ rv = BLADERF_ERR_INVAL;
+ goto error;
+ }
+ }
+
+ pack_image(img, buf);
+
+ f = fopen(file, "wb");
+ if (!f) {
+ if (errno == EACCES) {
+ rv = BLADERF_ERR_PERMISSION;
+ } else {
+ rv = BLADERF_ERR_IO;
+ }
+
+ log_debug("Failed to open \"%s\": %s\n", file, strerror(errno));
+
+ goto error;
+ }
+
+ rv = file_write(f, buf, buf_len);
+
+error:
+ if (f) {
+ fclose(f);
+ }
+ free(buf);
+ return rv;
+}
+
+int bladerf_image_read(struct bladerf_image *img, const char *file)
+{
+ int rv = -1;
+ uint8_t *buf = NULL;
+ size_t buf_len;
+
+ rv = file_read_buffer(file, &buf, &buf_len);
+ if (rv < 0) {
+ goto bladerf_image_read_out;
+ }
+
+ rv = verify_checksum(buf, buf_len);
+ if (rv < 0) {
+ goto bladerf_image_read_out;
+ }
+
+ /* Note: On success, buf->data = buf, with the data memmove'd over.
+ * Static analysis tools might indicate a false postive leak when
+ * buf goes out of scope with rv == 0 */
+ rv = unpack_image(img, buf, buf_len);
+
+bladerf_image_read_out:
+ if (rv != 0) {
+ free(buf);
+ }
+
+ return rv;
+}
+
+static inline bool is_page_aligned(struct bladerf *dev, uint32_t val)
+{
+ return val % dev->flash_arch->psize_bytes == 0;
+}
+
+static inline bool is_valid_addr_len(struct bladerf *dev,
+ uint32_t addr, uint32_t len)
+{
+ if (addr >= dev->flash_arch->tsize_bytes) {
+ return false;
+ } else if (len > dev->flash_arch->tsize_bytes) {
+ return false;
+ } else if ((addr + len) > dev->flash_arch->tsize_bytes) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+struct bladerf_image * bladerf_alloc_image(struct bladerf *dev,
+ bladerf_image_type type,
+ uint32_t address,
+ uint32_t length)
+{
+ struct bladerf_image *image;
+
+ assert(BLADERF_IMAGE_MAGIC_LEN == (sizeof(image_magic) - 1));
+
+ /* 0xffffffff is a placeholder for images that use the format but don't
+ * currently have an address in flash to live in */
+ if (address != 0xffffffff) {
+ if (!is_page_aligned(dev, address)) {
+ log_debug("Address is not page-aligned: 0x%08x\n", address);
+ return NULL;
+ } else if (!is_page_aligned(dev, length)) {
+ log_debug("Length is not page-aligned: 0x%08x\n", length);
+ return NULL;
+ } else if (!is_valid_addr_len(dev, address, length)) {
+ log_debug("Invalid address=0x%08x or length=0x%08x\n", address, length);
+ return NULL;
+ }
+ }
+
+ image = (struct bladerf_image *)calloc(1, sizeof(*image));
+
+ if (!image) {
+ return NULL;
+ }
+
+ if (length) {
+ image->data = (uint8_t *)calloc(1, length);
+ if (!image->data) {
+ free(image);
+ return NULL;
+ }
+ }
+
+ memcpy(image->magic, &image_magic, BLADERF_IMAGE_MAGIC_LEN);
+
+ image->version.major = 0;
+ image->version.minor = 1;
+ image->version.patch = 0;
+ image->timestamp = get_timestamp();
+ image->address = address;
+ image->length = length;
+ image->type = type;
+
+ return image;
+}
+
+static int make_cal_region(bladerf_fpga_size size, uint16_t vctcxo_trim,
+ uint8_t *buf, size_t len)
+{
+ int rv;
+ static const char fpga_size_40[] = "40";
+ static const char fpga_size_115[] = "115";
+ static const char fpga_size_A4[] = "A4";
+ static const char fpga_size_A5[] = "A5";
+ static const char fpga_size_A9[] = "A9";
+ const char *fpga_size;
+ char dac[7] = {0};
+
+ if (size == BLADERF_FPGA_40KLE) {
+ fpga_size = fpga_size_40;
+ } else if (size == BLADERF_FPGA_115KLE) {
+ fpga_size = fpga_size_115;
+ } else if (size == BLADERF_FPGA_A4) {
+ fpga_size = fpga_size_A4;
+ } else if (size == BLADERF_FPGA_A5) {
+ fpga_size = fpga_size_A5;
+ } else if (size == BLADERF_FPGA_A9) {
+ fpga_size = fpga_size_A9;
+ } else {
+ assert(0); /* Bug catcher */
+ return BLADERF_ERR_INVAL;
+ }
+
+ memset(buf, 0xff, len);
+
+ assert(len < INT_MAX);
+ rv = binkv_add_field((char*)buf, (int)len, "B", fpga_size);
+
+ if (rv < 0) {
+ return rv;
+ }
+
+ sprintf(dac, "%u", vctcxo_trim);
+
+ rv = binkv_add_field((char*)buf, (int)len, "DAC", dac);
+ if (rv < 0) {
+ return rv;
+ }
+
+ return 0;
+}
+
+struct bladerf_image * bladerf_alloc_cal_image(struct bladerf *dev,
+ bladerf_fpga_size fpga_size,
+ uint16_t vctcxo_trim)
+{
+ struct bladerf_image *image;
+ int status;
+
+ image = bladerf_alloc_image(dev,
+ BLADERF_IMAGE_TYPE_CALIBRATION,
+ BLADERF_FLASH_ADDR_CAL,
+ BLADERF_FLASH_BYTE_LEN_CAL);
+
+ if (!image) {
+ return NULL;
+ }
+
+ status = make_cal_region(fpga_size, vctcxo_trim,
+ image->data, image->length);
+
+ if (status != 0) {
+ bladerf_free_image(image);
+ image = NULL;
+ }
+
+ return image;
+}
+
+void bladerf_free_image(struct bladerf_image *image)
+{
+ if (image) {
+ free(image->data);
+ free(image);
+ }
+}
diff --git a/Radio/HW/BladeRF/src/board/bladerf2/bladerf2.c b/Radio/HW/BladeRF/src/board/bladerf2/bladerf2.c
new file mode 100644
index 0000000..8d56149
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf2/bladerf2.c
@@ -0,0 +1,3744 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2017-2018 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+
+#include "libbladeRF.h"
+
+#include "bladeRF.h"
+#include "host_config.h"
+
+#include "log.h"
+#define LOGGER_ID_STRING
+#include "logger_id.h"
+#include "rel_assert.h"
+
+#include "../bladerf1/flash.h"
+#include "board/board.h"
+#include "capabilities.h"
+#include "compatibility.h"
+
+#include "ad936x.h"
+#include "ad936x_helpers.h"
+
+#include "driver/fpga_trigger.h"
+#include "driver/fx3_fw.h"
+#include "driver/ina219.h"
+#include "driver/spi_flash.h"
+
+#include "backend/backend_config.h"
+#include "backend/usb/usb.h"
+
+#include "streaming/async.h"
+#include "streaming/sync.h"
+
+#include "conversions.h"
+#include "devinfo.h"
+#include "helpers/file.h"
+#include "helpers/version.h"
+#include "helpers/wallclock.h"
+#include "iterators.h"
+#include "version.h"
+
+#include "bladerf2_common.h"
+#include "common.h"
+
+
+/******************************************************************************/
+/* Forward declarations */
+/******************************************************************************/
+
+static int bladerf2_read_flash_vctcxo_trim(struct bladerf *dev, uint16_t *trim);
+
+
+/******************************************************************************/
+/* Constants */
+/******************************************************************************/
+
+// clang-format off
+
+/* REFIN frequency range */
+static struct bladerf_range const bladerf2_pll_refclk_range = {
+ FIELD_INIT(.min, 5000000),
+ FIELD_INIT(.max, 300000000),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+};
+
+/* Loopback modes */
+static struct bladerf_loopback_modes const bladerf2_loopback_modes[] = {
+ {
+ FIELD_INIT(.name, "none"),
+ FIELD_INIT(.mode, BLADERF_LB_NONE)
+ },
+ {
+ FIELD_INIT(.name, "firmware"),
+ FIELD_INIT(.mode, BLADERF_LB_FIRMWARE)
+ },
+ {
+ FIELD_INIT(.name, "rf_bist"),
+ FIELD_INIT(.mode, BLADERF_LB_RFIC_BIST)
+ },
+};
+// clang-format on
+
+
+/******************************************************************************/
+/* Low-level Initialization */
+/******************************************************************************/
+
+static int _bladerf2_initialize(struct bladerf *dev)
+{
+ struct bladerf2_board_data *board_data;
+ struct bladerf_version required_fw_version, required_fpga_version;
+ int status;
+
+ /* Test for uninitialized dev struct */
+ NULL_CHECK(dev);
+ NULL_CHECK(dev->board_data);
+
+ /* Initialize board_data struct and members */
+ board_data = dev->board_data;
+
+ /* Read FPGA version */
+ CHECK_STATUS(
+ dev->backend->get_fpga_version(dev, &board_data->fpga_version));
+
+ log_verbose("Read FPGA version: %s\n", board_data->fpga_version.describe);
+
+ /* Determine FPGA capabilities */
+ board_data->capabilities |=
+ bladerf2_get_fpga_capabilities(&board_data->fpga_version);
+
+ log_verbose("Capability mask after FPGA load: 0x%016" PRIx64 "\n",
+ board_data->capabilities);
+
+ /* If the FPGA version check fails, just warn, but don't error out.
+ *
+ * If an error code caused this function to bail out, it would prevent a
+ * user from being able to unload and reflash a bitstream being
+ * "autoloaded" from SPI flash. */
+ status =
+ version_check(&bladerf2_fw_compat_table, &bladerf2_fpga_compat_table,
+ &board_data->fw_version, &board_data->fpga_version,
+ &required_fw_version, &required_fpga_version);
+ if (status < 0) {
+#if LOGGING_ENABLED
+ if (BLADERF_ERR_UPDATE_FPGA == status) {
+ log_warning(
+ "FPGA v%u.%u.%u was detected. Firmware v%u.%u.%u "
+ "requires FPGA v%u.%u.%u or later. Please load a "
+ "different FPGA version before continuing.\n\n",
+ board_data->fpga_version.major, board_data->fpga_version.minor,
+ board_data->fpga_version.patch, board_data->fw_version.major,
+ board_data->fw_version.minor, board_data->fw_version.patch,
+ required_fpga_version.major, required_fpga_version.minor,
+ required_fpga_version.patch);
+ } else if (BLADERF_ERR_UPDATE_FW == status) {
+ log_warning(
+ "FPGA v%u.%u.%u was detected, which requires firmware "
+ "v%u.%u.%u or later. The device firmware is currently "
+ "v%u.%u.%u. Please upgrade the device firmware before "
+ "continuing.\n\n",
+ board_data->fpga_version.major, board_data->fpga_version.minor,
+ board_data->fpga_version.patch, required_fw_version.major,
+ required_fw_version.minor, required_fw_version.patch,
+ board_data->fw_version.major, board_data->fw_version.minor,
+ board_data->fw_version.patch);
+ }
+#endif
+ }
+
+ /* Set FPGA packet protocol */
+ CHECK_STATUS(
+ dev->backend->set_fpga_protocol(dev, BACKEND_FPGA_PROTOCOL_NIOSII));
+
+ /* Initialize INA219 */
+ CHECK_STATUS(ina219_init(dev, ina219_r_shunt));
+
+ /* Set tuning mode. This will trigger initialization of the RFIC.
+ *
+ * RFIC initialization consists of:
+ * - RFFE register initialization
+ * - RFIC initialization
+ * - Setting initial frequency
+ * - Setting up FIR filters
+ * - Disabling RX and TX on the RFIC
+ * - Muting the TX
+ */
+ CHECK_STATUS(dev->board->set_tuning_mode(dev, default_tuning_mode(dev)));
+
+ /* Update device state */
+ board_data->state = STATE_INITIALIZED;
+
+ /* Initialize VCTCXO trim DAC to stored value */
+ uint16_t *trimval = &(board_data->trimdac_stored_value);
+
+ CHECK_STATUS(bladerf2_read_flash_vctcxo_trim(dev, trimval));
+ CHECK_STATUS(dev->backend->ad56x1_vctcxo_trim_dac_write(dev, *trimval));
+
+ board_data->trim_source = TRIM_SOURCE_TRIM_DAC;
+
+ /* Configure PLL */
+ CHECK_STATUS(bladerf_set_pll_refclk(dev, BLADERF_REFIN_DEFAULT));
+
+ /* Reset current quick tune profile number */
+ board_data->quick_tune_rx_profile = 0;
+ board_data->quick_tune_tx_profile = 0;
+
+ log_debug("%s: complete\n", __FUNCTION__);
+
+ return 0;
+}
+
+
+/******************************************************************************
+ * Generic Board Functions *
+ ******************************************************************************/
+
+/******************************************************************************/
+/* Matches */
+/******************************************************************************/
+
+static bool bladerf2_matches(struct bladerf *dev)
+{
+ NULL_CHECK(dev);
+ NULL_CHECK(dev->backend);
+
+ uint16_t vid, pid;
+ int status;
+
+ status = dev->backend->get_vid_pid(dev, &vid, &pid);
+ if (status < 0) {
+ log_error("%s: get_vid_pid returned status %s\n", __FUNCTION__,
+ bladerf_strerror(status));
+ return false;
+ }
+
+ if (USB_NUAND_VENDOR_ID == vid && USB_NUAND_BLADERF2_PRODUCT_ID == pid) {
+ return true;
+ }
+
+ return false;
+}
+
+
+/******************************************************************************/
+/* Open/close */
+/******************************************************************************/
+
+static int bladerf2_open(struct bladerf *dev, struct bladerf_devinfo *devinfo)
+{
+ NULL_CHECK(dev);
+ NULL_CHECK(dev->backend);
+
+ struct bladerf2_board_data *board_data;
+ struct bladerf_version required_fw_version;
+ char *full_path;
+ bladerf_dev_speed usb_speed;
+ size_t i;
+ int ready, status;
+
+ size_t const MAX_RETRIES = 30;
+
+ /* Allocate board data */
+ board_data = calloc(1, sizeof(struct bladerf2_board_data));
+ if (NULL == board_data) {
+ RETURN_ERROR_STATUS("calloc board_data", BLADERF_ERR_MEM);
+ }
+ dev->board_data = board_data;
+ board_data->phy = NULL;
+ board_data->rfic_init_params = (void *)&bladerf2_rfic_init_params;
+
+ /* Allocate flash architecture */
+ dev->flash_arch = calloc(1, sizeof(struct bladerf_flash_arch));
+ if (NULL == dev->flash_arch) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Initialize board data */
+ board_data->fpga_version.describe = board_data->fpga_version_str;
+ board_data->fw_version.describe = board_data->fw_version_str;
+
+ board_data->module_format[BLADERF_RX] = -1;
+ board_data->module_format[BLADERF_TX] = -1;
+
+ dev->flash_arch->status = STATUS_FLASH_UNINITIALIZED;
+ dev->flash_arch->manufacturer_id = 0x0;
+ dev->flash_arch->device_id = 0x0;
+
+ board_data->rfic_reset_on_close = false;
+
+ /* Read firmware version */
+ CHECK_STATUS(dev->backend->get_fw_version(dev, &board_data->fw_version));
+
+ log_verbose("Read Firmware version: %s\n", board_data->fw_version.describe);
+
+ /* Determine firmware capabilities */
+ board_data->capabilities |=
+ bladerf2_get_fw_capabilities(&board_data->fw_version);
+
+ log_verbose("Capability mask before FPGA load: 0x%016" PRIx64 "\n",
+ board_data->capabilities);
+
+ /* Update device state */
+ board_data->state = STATE_FIRMWARE_LOADED;
+
+ /* Wait until firmware is ready */
+ for (i = 0; i < MAX_RETRIES; i++) {
+ ready = dev->backend->is_fw_ready(dev);
+ if (ready != 1) {
+ if (0 == i) {
+ log_info("Waiting for device to become ready...\n");
+ } else {
+ log_debug("Retry %02u/%02u.\n", i + 1, MAX_RETRIES);
+ }
+ usleep(1000000);
+ } else {
+ break;
+ }
+ }
+
+ if (ready != 1) {
+ RETURN_ERROR_STATUS("is_fw_ready", BLADERF_ERR_TIMEOUT);
+ }
+
+ /* Determine data message size */
+ CHECK_STATUS(dev->backend->get_device_speed(dev, &usb_speed));
+
+ switch (usb_speed) {
+ case BLADERF_DEVICE_SPEED_SUPER:
+ board_data->msg_size = USB_MSG_SIZE_SS;
+ break;
+ case BLADERF_DEVICE_SPEED_HIGH:
+ board_data->msg_size = USB_MSG_SIZE_HS;
+ break;
+ default:
+ log_error("%s: unsupported device speed (%d)\n", __FUNCTION__,
+ usb_speed);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ /* Verify that we have a sufficent firmware version before continuing. */
+ status = version_check_fw(&bladerf2_fw_compat_table,
+ &board_data->fw_version, &required_fw_version);
+ if (status != 0) {
+#ifdef LOGGING_ENABLED
+ if (BLADERF_ERR_UPDATE_FW == status) {
+ log_warning("Firmware v%u.%u.%u was detected. libbladeRF v%s "
+ "requires firmware v%u.%u.%u or later. An upgrade via "
+ "the bootloader is required.\n\n",
+ &board_data->fw_version.major,
+ &board_data->fw_version.minor,
+ &board_data->fw_version.patch, LIBBLADERF_VERSION,
+ required_fw_version.major, required_fw_version.minor,
+ required_fw_version.patch);
+ }
+#endif
+ return status;
+ }
+
+ /* Probe SPI flash architecture information */
+ if (have_cap(board_data->capabilities, BLADERF_CAP_FW_FLASH_ID)) {
+ status = spi_flash_read_flash_id(dev, &dev->flash_arch->manufacturer_id,
+ &dev->flash_arch->device_id);
+ if (status < 0) {
+ log_error("Failed to probe SPI flash ID information.\n");
+ }
+ } else {
+ log_debug("FX3 firmware v%u.%u.%u does not support SPI flash ID. A "
+ "firmware update is recommended in order to probe the SPI "
+ "flash ID information.\n",
+ board_data->fw_version.major, board_data->fw_version.minor,
+ board_data->fw_version.patch);
+ }
+
+ /* Decode SPI flash ID information to figure out its architecture.
+ * We need to know a little about the flash architecture before we can
+ * read anything from it, including FPGA size and other cal data.
+ * If the firmware does not have the capability to get the flash ID,
+ * sane defaults will be chosen.
+ *
+ * Not checking return code because it is irrelevant. */
+ spi_flash_decode_flash_architecture(dev, &board_data->fpga_size);
+
+ /* Get FPGA size */
+ status = spi_flash_read_fpga_size(dev, &board_data->fpga_size);
+ if (status < 0) {
+ log_warning("Failed to get FPGA size %s\n", bladerf_strerror(status));
+ }
+
+ if (getenv("BLADERF_FORCE_FPGA_A9")) {
+ log_info("BLADERF_FORCE_FPGA_A9 is set, assuming A9 FPGA\n");
+ board_data->fpga_size = BLADERF_FPGA_A9;
+ }
+
+ /* If the flash architecture could not be decoded earlier, try again now
+ * that the FPGA size is known. */
+ if (dev->flash_arch->status != STATUS_SUCCESS) {
+ status =
+ spi_flash_decode_flash_architecture(dev, &board_data->fpga_size);
+ if (status < 0) {
+ log_debug("Assumptions were made about the SPI flash architecture! "
+ "Flash commands may not function as expected.\n");
+ }
+ }
+
+ /* Skip further work if BLADERF_FORCE_NO_FPGA_PRESENT is set */
+ if (getenv("BLADERF_FORCE_NO_FPGA_PRESENT")) {
+ log_debug("Skipping FPGA configuration and initialization - "
+ "BLADERF_FORCE_NO_FPGA_PRESENT is set.\n");
+ return 0;
+ }
+
+ /* Check if FPGA is configured */
+ status = dev->backend->is_fpga_configured(dev);
+ if (status < 0) {
+ RETURN_ERROR_STATUS("is_fpga_configured", status);
+ } else if (1 == status) {
+ board_data->state = STATE_FPGA_LOADED;
+ } else if (status != 1 && BLADERF_FPGA_UNKNOWN == board_data->fpga_size) {
+ log_warning("Unknown FPGA size. Skipping FPGA configuration...\n");
+ log_warning("Skipping further initialization...\n");
+ return 0;
+ } else if (status != 1) {
+ /* Try searching for an FPGA in the config search path */
+ switch (board_data->fpga_size) {
+ case BLADERF_FPGA_A4:
+ full_path = file_find("hostedxA4.rbf");
+ break;
+
+ case BLADERF_FPGA_A5:
+ full_path = file_find("hostedxA5.rbf");
+ break;
+
+ case BLADERF_FPGA_A9:
+ full_path = file_find("hostedxA9.rbf");
+ break;
+
+ default:
+ log_error("%s: invalid FPGA size: %d\n", __FUNCTION__,
+ board_data->fpga_size);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ if (full_path != NULL) {
+ uint8_t *buf;
+ size_t buf_size;
+
+ log_debug("Loading FPGA from: %s\n", full_path);
+
+ status = file_read_buffer(full_path, &buf, &buf_size);
+ free(full_path);
+ full_path = NULL;
+
+ if (status != 0) {
+ RETURN_ERROR_STATUS("file_read_buffer", status);
+ }
+
+ CHECK_STATUS(dev->backend->load_fpga(dev, buf, buf_size));
+
+ board_data->state = STATE_FPGA_LOADED;
+ } else {
+ log_warning("FPGA bitstream file not found.\n");
+ log_warning("Skipping further initialization...\n");
+ return 0;
+ }
+ }
+
+ /* Initialize the board */
+ CHECK_STATUS(_bladerf2_initialize(dev));
+
+ if (have_cap(board_data->capabilities, BLADERF_CAP_SCHEDULED_RETUNE)) {
+ /* Cancel any pending re-tunes that may have been left over as the
+ * result of a user application crashing or forgetting to call
+ * bladerf_close() */
+
+ bladerf_direction dir;
+
+ FOR_EACH_DIRECTION(dir)
+ {
+ size_t idx;
+ bladerf_channel ch;
+
+ FOR_EACH_CHANNEL(dir, 1, idx, ch)
+ {
+ CHECK_STATUS(dev->board->cancel_scheduled_retunes(dev, ch));
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void bladerf2_close(struct bladerf *dev)
+{
+ if (dev != NULL) {
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ struct bladerf_flash_arch *flash_arch = dev->flash_arch;
+
+ if (board_data != NULL) {
+ bladerf_direction dir;
+
+ FOR_EACH_DIRECTION(dir)
+ {
+ size_t idx;
+ bladerf_channel ch;
+
+ FOR_EACH_CHANNEL(dir, 1, idx, ch)
+ {
+ sync_deinit(&board_data->sync[ch]);
+
+ /* Cancel scheduled retunes here to avoid the device
+ * retuning underneath the user should they open it again in
+ * the future.
+ *
+ * This is intended to help developers avoid a situation
+ * during debugging where they schedule "far" into the
+ * future, but hit a case where their program aborts or
+ * exits early. If we do not cancel these scheduled retunes,
+ * the device could start up and/or "unexpectedly" switch to
+ * a different frequency.
+ */
+ if (dev->backend->is_fpga_configured(dev) &&
+ have_cap(board_data->capabilities,
+ BLADERF_CAP_SCHEDULED_RETUNE)) {
+ dev->board->cancel_scheduled_retunes(dev, ch);
+ }
+ }
+ }
+
+ if (board_data->state >= STATE_INITIALIZED && rfic != NULL) {
+ if (board_data->rfic_reset_on_close) {
+ /* We need to fully de-initialize the RFIC, so it can be
+ * reset on the next open. This seems to be necessary after
+ * doing direct SPI control of the RFIC.
+ */
+ rfic->deinitialize(dev);
+ } else {
+ /* Put the RFIC into standby mode. This will shut down any
+ * current RF activity, but it will not lose the RF state.
+ */
+ rfic->standby(dev);
+ }
+ }
+
+ free(board_data);
+ board_data = NULL;
+ }
+
+ if (flash_arch != NULL) {
+ free(flash_arch);
+ flash_arch = NULL;
+ }
+ }
+}
+
+
+/******************************************************************************/
+/* Properties */
+/******************************************************************************/
+
+static bladerf_dev_speed bladerf2_device_speed(struct bladerf *dev)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ bladerf_dev_speed usb_speed;
+ int status;
+
+ status = dev->backend->get_device_speed(dev, &usb_speed);
+ if (status < 0) {
+ log_error("%s: get_device_speed failed: %s\n", __FUNCTION__,
+ bladerf_strerror(status));
+ return BLADERF_DEVICE_SPEED_UNKNOWN;
+ }
+
+ return usb_speed;
+}
+
+static int bladerf2_get_serial(struct bladerf *dev, char *serial)
+{
+ CHECK_BOARD_STATE(STATE_UNINITIALIZED);
+ NULL_CHECK(serial);
+
+ // TODO: don't use strcpy
+ strcpy(serial, dev->ident.serial);
+
+ return 0;
+}
+
+static int bladerf2_get_fpga_size(struct bladerf *dev, bladerf_fpga_size *size)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(size);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ *size = board_data->fpga_size;
+
+ return 0;
+}
+
+static int bladerf2_get_fpga_bytes(struct bladerf *dev, size_t *size)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(size);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ switch (board_data->fpga_size) {
+ case BLADERF_FPGA_A4:
+ *size = 2632660;
+ break;
+
+ case BLADERF_FPGA_A5:
+ *size = 4244820;
+ break;
+
+ case BLADERF_FPGA_A9:
+ *size = 12858972;
+ break;
+
+ default:
+ log_debug("%s: unknown fpga_size: %x\n", board_data->fpga_size);
+ return BLADERF_ERR_INVAL;
+ }
+
+ return 0;
+}
+
+static int bladerf2_get_flash_size(struct bladerf *dev,
+ uint32_t *size,
+ bool *is_guess)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(size);
+ NULL_CHECK(is_guess);
+
+ *size = dev->flash_arch->tsize_bytes;
+ *is_guess = (dev->flash_arch->status != STATUS_SUCCESS);
+
+ return 0;
+}
+
+static int bladerf2_is_fpga_configured(struct bladerf *dev)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ return dev->backend->is_fpga_configured(dev);
+}
+
+static int bladerf2_get_fpga_source(struct bladerf *dev,
+ bladerf_fpga_source *source)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(source);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_FW_FPGA_SOURCE)) {
+ log_debug("%s: not supported by firmware\n", __FUNCTION__);
+ *source = BLADERF_FPGA_SOURCE_UNKNOWN;
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ *source = dev->backend->get_fpga_source(dev);
+
+ return 0;
+}
+
+static uint64_t bladerf2_get_capabilities(struct bladerf *dev)
+{
+ CHECK_BOARD_STATE(STATE_UNINITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->capabilities;
+}
+
+static size_t bladerf2_get_channel_count(struct bladerf *dev,
+ bladerf_direction dir)
+{
+ return 2;
+}
+
+
+/******************************************************************************/
+/* Versions */
+/******************************************************************************/
+
+static int bladerf2_get_fpga_version(struct bladerf *dev,
+ struct bladerf_version *version)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(version);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ memcpy(version, &board_data->fpga_version, sizeof(*version));
+
+ return 0;
+}
+
+static int bladerf2_get_fw_version(struct bladerf *dev,
+ struct bladerf_version *version)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(version);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ memcpy(version, &board_data->fw_version, sizeof(*version));
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Enable/disable */
+/******************************************************************************/
+
+static int bladerf2_enable_module(struct bladerf *dev,
+ bladerf_channel ch,
+ bool enable)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->rfic->enable_module(dev, ch, enable);
+}
+
+
+/******************************************************************************/
+/* Gain */
+/******************************************************************************/
+
+static int bladerf2_get_gain_stage_range(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ struct bladerf_range const **range)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(range);
+
+ struct bladerf_gain_range const *ranges = NULL;
+ bladerf_frequency frequency = 0;
+ size_t i, ranges_len;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ ranges = bladerf2_tx_gain_ranges;
+ ranges_len = ARRAY_SIZE(bladerf2_tx_gain_ranges);
+ } else {
+ ranges = bladerf2_rx_gain_ranges;
+ ranges_len = ARRAY_SIZE(bladerf2_rx_gain_ranges);
+ }
+
+ CHECK_STATUS(dev->board->get_frequency(dev, ch, &frequency));
+
+ for (i = 0; i < ranges_len; ++i) {
+ struct bladerf_gain_range const *r = &(ranges[i]);
+ struct bladerf_range const *rfreq = &(r->frequency);
+
+ // if the frequency range matches, and either:
+ // both the range name and the stage name are null, or
+ // neither name is null and the strings match
+ // then we found our match
+ if (is_within_range(rfreq, frequency) &&
+ ((NULL == r->name && NULL == stage) ||
+ (r->name != NULL && stage != NULL &&
+ (strcmp(r->name, stage) == 0)))) {
+ *range = &(r->gain);
+ return 0;
+ }
+ }
+
+ return BLADERF_ERR_INVAL;
+}
+
+static int bladerf2_get_gain_range(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_range const **range)
+{
+ return dev->board->get_gain_stage_range(dev, ch, NULL, range);
+}
+
+static int bladerf2_get_gain_modes(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_gain_modes const **modes)
+{
+ struct bladerf_gain_modes const *mode_infos;
+ unsigned int mode_infos_len;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ mode_infos = NULL;
+ mode_infos_len = 0;
+ } else {
+ mode_infos = bladerf2_rx_gain_modes;
+ mode_infos_len = ARRAY_SIZE(bladerf2_rx_gain_modes);
+ }
+
+ if (modes != NULL) {
+ *modes = mode_infos;
+ }
+
+ return mode_infos_len;
+}
+
+static int bladerf2_get_gain_stages(struct bladerf *dev,
+ bladerf_channel ch,
+ char const **stages,
+ size_t count)
+{
+ NULL_CHECK(dev);
+
+ struct bladerf_gain_range const *ranges = NULL;
+ char const **names = NULL;
+ size_t stage_count = 0;
+ unsigned int ranges_len;
+ size_t i, j;
+
+ IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, {
+ *stages = NULL;
+ return 0;
+ });
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ ranges = bladerf2_tx_gain_ranges;
+ ranges_len = ARRAY_SIZE(bladerf2_tx_gain_ranges);
+ } else {
+ ranges = bladerf2_rx_gain_ranges;
+ ranges_len = ARRAY_SIZE(bladerf2_rx_gain_ranges);
+ }
+
+ names = calloc(ranges_len + 1, sizeof(char *));
+ if (NULL == names) {
+ RETURN_ERROR_STATUS("calloc names", BLADERF_ERR_MEM);
+ }
+
+ // Iterate through all the ranges...
+ for (i = 0; i < ranges_len; ++i) {
+ struct bladerf_gain_range const *range = &(ranges[i]);
+
+ if (NULL == range->name) {
+ // this is system gain, skip it
+ continue;
+ }
+
+ // loop through the output array to make sure we record this one
+ for (j = 0; j < ranges_len; ++j) {
+ if (NULL == names[j]) {
+ // Made it to the end of names without finding a match
+ names[j] = range->name;
+ ++stage_count;
+ break;
+ } else if (strcmp(range->name, names[j]) == 0) {
+ // found a match, break
+ break;
+ }
+ }
+ }
+
+ if (NULL != stages && 0 != count) {
+ count = (stage_count < count) ? stage_count : count;
+
+ for (i = 0; i < count; i++) {
+ stages[i] = names[i];
+ }
+ }
+
+ free((char **)names);
+ return (int)stage_count;
+}
+
+static int bladerf2_get_gain_mode(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode *mode)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(mode);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->rfic->get_gain_mode(dev, ch, mode);
+}
+
+static int bladerf2_set_gain_mode(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode mode)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->rfic->set_gain_mode(dev, ch, mode);
+}
+
+static int bladerf2_get_gain(struct bladerf *dev, bladerf_channel ch, int *gain)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(gain);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->rfic->get_gain(dev, ch, gain);
+}
+
+static int bladerf2_set_gain(struct bladerf *dev, bladerf_channel ch, int gain)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct bladerf_range const *range = NULL;
+
+ CHECK_STATUS(dev->board->get_gain_range(dev, ch, &range));
+
+ return board_data->rfic->set_gain(dev, ch, clamp_to_range(range, gain));
+}
+
+static int bladerf2_get_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ int *gain)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(stage);
+ NULL_CHECK(gain);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->rfic->get_gain_stage(dev, ch, stage, gain);
+}
+
+static int bladerf2_set_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ int gain)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(stage);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct bladerf_range const *range = NULL;
+
+ CHECK_STATUS(dev->board->get_gain_stage_range(dev, ch, stage, &range));
+
+ return board_data->rfic->set_gain_stage(dev, ch, stage,
+ clamp_to_range(range, gain));
+}
+
+
+/******************************************************************************/
+/* Sample Rate */
+/******************************************************************************/
+
+static int bladerf2_get_sample_rate_range(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_range const **range)
+{
+ NULL_CHECK(range);
+
+ *range = &bladerf2_sample_rate_range_base;
+
+ if (dev->feature == BLADERF_FEATURE_OVERSAMPLE) {
+ *range = &bladerf2_sample_rate_range_oversample;
+ }
+
+ return 0;
+}
+
+static int bladerf2_get_rational_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_rational_rate *rate)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(rate);
+
+ bladerf_sample_rate integer_rate;
+
+ CHECK_STATUS(dev->board->get_sample_rate(dev, ch, &integer_rate));
+
+ rate->integer = integer_rate;
+ rate->num = 0;
+ rate->den = 1;
+
+ return 0;
+}
+
+static int bladerf2_set_rational_sample_rate(
+ struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_rational_rate *rate,
+ struct bladerf_rational_rate *actual)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(rate);
+
+ bladerf_sample_rate integer_rate, actual_integer_rate;
+
+ integer_rate = (bladerf_sample_rate)(rate->integer + rate->num / rate->den);
+
+ CHECK_STATUS(dev->board->set_sample_rate(dev, ch, integer_rate,
+ &actual_integer_rate));
+
+ if (actual != NULL) {
+ CHECK_STATUS(dev->board->get_rational_sample_rate(dev, ch, actual));
+ }
+
+ return 0;
+}
+
+static int bladerf2_get_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate *rate)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(rate);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ bladerf_sample_rate double_rate;
+ CHECK_STATUS(board_data->rfic->get_sample_rate(dev, ch, rate));
+
+ /* OVERSAMPLE feature reports half of the actual
+ sample rate so we have to double it on return */
+ if (dev->feature == BLADERF_FEATURE_OVERSAMPLE) {
+ double_rate = *rate*2;
+ *rate = double_rate;
+ }
+
+ return 0;
+}
+
+static int bladerf2_set_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate rate,
+ bladerf_sample_rate *actual)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ struct bladerf_range const *range = NULL;
+ bladerf_sample_rate current;
+ bool old_low, new_low;
+ bladerf_rfic_rxfir rxfir;
+ bladerf_rfic_txfir txfir;
+
+ /* Range checking */
+ CHECK_STATUS(dev->board->get_sample_rate_range(dev, ch, &range));
+
+ if (!is_within_range(range, rate)) {
+ return BLADERF_ERR_RANGE;
+ }
+
+ /* Feature range check */
+ if (dev->feature == BLADERF_FEATURE_OVERSAMPLE &&
+ !is_within_range(&bladerf2_sample_rate_range_oversample, rate)) {
+ log_error("Sample rate outside of OVERSAMPLE feature range\n");
+ return BLADERF_ERR_RANGE;
+ } else if (dev->feature == BLADERF_FEATURE_DEFAULT &&
+ !is_within_range(&bladerf2_sample_rate_range_base, rate)) {
+ log_error("Sample rate outside of DEFAULT feature range\n");
+ return BLADERF_ERR_RANGE;
+ }
+
+ /* Get current sample rate, and check it against the low-rate range */
+ CHECK_STATUS(dev->board->get_sample_rate(dev, ch, &current));
+
+ old_low = is_within_range(&bladerf2_sample_rate_range_4x, current);
+ new_low = is_within_range(&bladerf2_sample_rate_range_4x, rate);
+
+ /* Get current filter status */
+ if (new_low || old_low) {
+ CHECK_STATUS(
+ rfic->get_filter(dev, BLADERF_CHANNEL_RX(0), &rxfir, NULL));
+ CHECK_STATUS(
+ rfic->get_filter(dev, BLADERF_CHANNEL_TX(0), NULL, &txfir));
+ }
+
+ /* If the requested sample rate is below the native range, we must implement
+ * a 4x decimation/interpolation filter on the RFIC. */
+ if (new_low) {
+ bool fir_set_failed = false;
+ int status;
+
+ if (rxfir != BLADERF_RFIC_RXFIR_DEC4 ||
+ txfir != BLADERF_RFIC_TXFIR_INT4) {
+ log_debug("%s: enabling 4x decimation/interpolation filters\n",
+ __FUNCTION__);
+
+ /* Intermidiate sample rate assignment to circumvent rfic->set_filter error */
+ if ((current > 40e6 && rate < 2083334) || (rate > 40e6 && current < 2083334)) {
+ CHECK_STATUS(rfic->set_sample_rate(dev, ch, 30e6));
+ }
+
+ status = rfic->set_filter(dev, BLADERF_CHANNEL_RX(0),
+ BLADERF_RFIC_RXFIR_DEC4, 0);
+ if (status < 0) {
+ log_error("%s: could not set RX filter mode: %s\n",
+ __FUNCTION__, bladerf_strerror(status));
+ fir_set_failed = true;
+ }
+
+ status = rfic->set_filter(dev, BLADERF_CHANNEL_TX(0), 0,
+ BLADERF_RFIC_TXFIR_INT4);
+ if (status < 0) {
+ log_error("%s: could not set TX filter mode: %s\n",
+ __FUNCTION__, bladerf_strerror(status));
+ fir_set_failed = true;
+ }
+ }
+
+ /* Try to restore default operations if there was a failure */
+ if (fir_set_failed) {
+ log_debug("%s: attempting to reset filters to default...\n",
+ __FUNCTION__);
+ CHECK_STATUS(rfic->set_filter(dev, BLADERF_CHANNEL_RX(0),
+ BLADERF_RFIC_RXFIR_DEFAULT, 0));
+ CHECK_STATUS(rfic->set_filter(dev, BLADERF_CHANNEL_TX(0), 0,
+ BLADERF_RFIC_TXFIR_DEFAULT));
+
+ return BLADERF_ERR_UNEXPECTED;
+ }
+ }
+
+ /* The AD9361 doubles the sampling rate in OVERSAMPLE mode
+ so we must halve the sampling rate prior to setting */
+ if (dev->feature == BLADERF_FEATURE_OVERSAMPLE) {
+ rate /= 2;
+ }
+
+ /* Set the sample rate */
+ CHECK_STATUS(rfic->set_sample_rate(dev, ch, rate));
+
+ /* If the previous sample rate was below the native range, but the new one
+ * isn't, switch back to the default filters. */
+ if (old_low && !new_low) {
+ if (rxfir != BLADERF_RFIC_RXFIR_DEFAULT ||
+ txfir != BLADERF_RFIC_TXFIR_DEFAULT) {
+ log_debug("%s: disabling 4x decimation/interpolation filters\n",
+ __FUNCTION__);
+
+ CHECK_STATUS(rfic->set_filter(dev, BLADERF_CHANNEL_RX(0),
+ BLADERF_RFIC_RXFIR_DEFAULT, 0));
+ CHECK_STATUS(rfic->set_filter(dev, BLADERF_CHANNEL_TX(0), 0,
+ BLADERF_RFIC_TXFIR_DEFAULT));
+ }
+ }
+
+ /* If requested, fetch the new sample rate and return it. */
+ if (actual != NULL) {
+ CHECK_STATUS(dev->board->get_sample_rate(dev, ch, actual));
+ }
+
+ /* Warn the user if this isn't achievable */
+ check_total_sample_rate(dev);
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Bandwidth */
+/******************************************************************************/
+
+static int bladerf2_get_bandwidth_range(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_range const **range)
+{
+ NULL_CHECK(range);
+
+ *range = &bladerf2_bandwidth_range;
+
+ return 0;
+}
+
+static int bladerf2_get_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth *bandwidth)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->rfic->get_bandwidth(dev, ch, bandwidth);
+}
+
+static int bladerf2_set_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth bandwidth,
+ bladerf_bandwidth *actual)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ if (dev->feature == BLADERF_FEATURE_OVERSAMPLE) {
+ log_warning("bandwidth assignements with oversample feature enabled yields unkown results\n");
+ }
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->rfic->set_bandwidth(dev, ch, bandwidth, actual);
+}
+
+
+/******************************************************************************/
+/* Frequency */
+/******************************************************************************/
+
+static int bladerf2_get_frequency_range(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range)
+{
+ NULL_CHECK(range);
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ *range = &bladerf2_tx_frequency_range;
+ } else {
+ *range = &bladerf2_rx_frequency_range;
+ }
+
+ return 0;
+}
+
+static int bladerf2_select_band(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->rfic->select_band(dev, ch, frequency);
+}
+
+static int bladerf2_get_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency *frequency)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(frequency);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->rfic->get_frequency(dev, ch, frequency);
+}
+
+static int bladerf2_set_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return board_data->rfic->set_frequency(dev, ch, frequency);
+}
+
+
+/******************************************************************************/
+/* RF ports */
+/******************************************************************************/
+
+static int bladerf2_set_rf_port(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *port)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ struct bladerf_rfic_port_name_map const *pm = NULL;
+ unsigned int pm_len = 0;
+ uint32_t port_id = UINT32_MAX;
+ size_t i;
+
+ IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, {
+ log_debug("%s: FPGA command mode not supported\n", __FUNCTION__);
+ return BLADERF_ERR_UNSUPPORTED;
+ });
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ pm = bladerf2_tx_port_map;
+ pm_len = ARRAY_SIZE(bladerf2_tx_port_map);
+ } else {
+ pm = bladerf2_rx_port_map;
+ pm_len = ARRAY_SIZE(bladerf2_rx_port_map);
+ }
+
+ for (i = 0; i < pm_len; i++) {
+ if (strcmp(pm[i].name, port) == 0) {
+ port_id = pm[i].id;
+ break;
+ }
+ }
+
+ if (UINT32_MAX == port_id) {
+ RETURN_INVAL("port", "is not valid");
+ }
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ CHECK_AD936X(ad9361_set_tx_rf_port_output(phy, port_id));
+ } else {
+ CHECK_AD936X(ad9361_set_rx_rf_port_input(phy, port_id));
+ }
+
+ return 0;
+}
+
+static int bladerf2_get_rf_port(struct bladerf *dev,
+ bladerf_channel ch,
+ char const **port)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(port);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ struct bladerf_rfic_port_name_map const *pm = NULL;
+ unsigned int pm_len = 0;
+ uint32_t port_id;
+ bool ok;
+ size_t i;
+
+ IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, {
+ log_debug("%s: FPGA command mode not supported\n", __FUNCTION__);
+ return BLADERF_ERR_UNSUPPORTED;
+ });
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ pm = bladerf2_tx_port_map;
+ pm_len = ARRAY_SIZE(bladerf2_tx_port_map);
+ CHECK_AD936X(ad9361_get_tx_rf_port_output(phy, &port_id));
+ } else {
+ pm = bladerf2_rx_port_map;
+ pm_len = ARRAY_SIZE(bladerf2_rx_port_map);
+ CHECK_AD936X(ad9361_get_rx_rf_port_input(phy, &port_id));
+ }
+
+ ok = false;
+
+ for (i = 0; i < pm_len; i++) {
+ if (port_id == pm[i].id) {
+ *port = pm[i].name;
+ ok = true;
+ break;
+ }
+ }
+
+ if (!ok) {
+ *port = "unknown";
+ log_error("%s: unexpected port_id %u\n", __FUNCTION__, port_id);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ return 0;
+}
+
+static int bladerf2_get_rf_ports(struct bladerf *dev,
+ bladerf_channel ch,
+ char const **ports,
+ unsigned int count)
+{
+ struct bladerf_rfic_port_name_map const *pm = NULL;
+ unsigned int pm_len;
+ size_t i;
+
+ IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, {
+ *ports = NULL;
+ return 0;
+ });
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ pm = bladerf2_tx_port_map;
+ pm_len = ARRAY_SIZE(bladerf2_tx_port_map);
+ } else {
+ pm = bladerf2_rx_port_map;
+ pm_len = ARRAY_SIZE(bladerf2_rx_port_map);
+ }
+
+ if (ports != NULL) {
+ count = (pm_len < count) ? pm_len : count;
+
+ for (i = 0; i < count; i++) {
+ ports[i] = pm[i].name;
+ }
+ }
+
+ return pm_len;
+}
+
+
+/******************************************************************************/
+/* Scheduled Tuning */
+/******************************************************************************/
+
+static int bladerf2_get_quick_tune(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_quick_tune *quick_tune)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(quick_tune);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ struct band_port_map const *pm = NULL;
+
+ bladerf_frequency freq;
+
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_RX(1) &&
+ ch != BLADERF_CHANNEL_TX(0) && ch != BLADERF_CHANNEL_TX(1)) {
+ RETURN_INVAL_ARG("channel", ch, "is not valid");
+ }
+
+ CHECK_STATUS(dev->board->get_frequency(dev, ch, &freq));
+
+ pm = _get_band_port_map_by_freq(ch, freq);
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ if (board_data->quick_tune_tx_profile < NUM_BBP_FASTLOCK_PROFILES) {
+ /* Assign Nios and RFFE profile numbers */
+ quick_tune->nios_profile = board_data->quick_tune_tx_profile++;
+ log_verbose("Quick tune assigned Nios TX fast lock index: %u\n",
+ quick_tune->nios_profile);
+ quick_tune->rffe_profile =
+ quick_tune->nios_profile % NUM_RFFE_FASTLOCK_PROFILES;
+ log_verbose("Quick tune assigned RFFE TX fast lock index: %u\n",
+ quick_tune->rffe_profile);
+ } else {
+ log_error("Reached maximum number of TX quick tune profiles.");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* Create a fast lock profile in the RFIC */
+ CHECK_STATUS(
+ rfic->store_fastlock_profile(dev, ch, quick_tune->rffe_profile));
+
+ /* Save a copy of the TX fast lock profile to the Nios */
+ dev->backend->rffe_fastlock_save(dev, true, quick_tune->rffe_profile,
+ quick_tune->nios_profile);
+
+ /* Set the TX band */
+ quick_tune->port = (pm->rfic_port << 6);
+
+ /* Set the TX SPDTs */
+ quick_tune->spdt = (pm->spdt << 6) | (pm->spdt << 4);
+
+ } else {
+ if (board_data->quick_tune_rx_profile < NUM_BBP_FASTLOCK_PROFILES) {
+ /* Assign Nios and RFFE profile numbers */
+ quick_tune->nios_profile = board_data->quick_tune_rx_profile++;
+ log_verbose("Quick tune assigned Nios RX fast lock index: %u\n",
+ quick_tune->nios_profile);
+ quick_tune->rffe_profile =
+ quick_tune->nios_profile % NUM_RFFE_FASTLOCK_PROFILES;
+ log_verbose("Quick tune assigned RFFE RX fast lock index: %u\n",
+ quick_tune->rffe_profile);
+ } else {
+ log_error("Reached maximum number of RX quick tune profiles.");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* Create a fast lock profile in the RFIC */
+ CHECK_STATUS(
+ rfic->store_fastlock_profile(dev, ch, quick_tune->rffe_profile));
+
+ /* Save a copy of the RX fast lock profile to the Nios */
+ dev->backend->rffe_fastlock_save(dev, false, quick_tune->rffe_profile,
+ quick_tune->nios_profile);
+
+ /* Set the RX bit */
+ quick_tune->port = NIOS_PKT_RETUNE2_PORT_IS_RX_MASK;
+
+ /* Set the RX band */
+ if (pm->rfic_port < 3) {
+ quick_tune->port |= (3 << (pm->rfic_port << 1));
+ } else {
+ quick_tune->port |= (1 << (pm->rfic_port - 3));
+ }
+
+ /* Set the RX SPDTs */
+ quick_tune->spdt = (pm->spdt << 2) | (pm->spdt);
+ }
+
+ /* Workaround: the RFIC can end up in a bad state after fastlock use, and
+ * needs to be reset and re-initialized. This is likely due to our direct
+ * SPI writes causing state incongruence. */
+ board_data->rfic_reset_on_close = true;
+
+ return 0;
+}
+
+static int bladerf2_schedule_retune(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_timestamp timestamp,
+ bladerf_frequency frequency,
+ struct bladerf_quick_tune *quick_tune)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(quick_tune);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_SCHEDULED_RETUNE)) {
+ log_debug("This FPGA version (%u.%u.%u) does not support "
+ "scheduled retunes.\n",
+ board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch);
+
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ return dev->backend->retune2(dev, ch, timestamp, quick_tune->nios_profile,
+ quick_tune->rffe_profile, quick_tune->port,
+ quick_tune->spdt);
+}
+
+static int bladerf2_cancel_scheduled_retunes(struct bladerf *dev,
+ bladerf_channel ch)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_SCHEDULED_RETUNE)) {
+ log_debug("This FPGA version (%u.%u.%u) does not support "
+ "scheduled retunes.\n",
+ board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch);
+
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ return dev->backend->retune2(dev, ch, NIOS_PKT_RETUNE2_CLEAR_QUEUE, 0, 0, 0,
+ 0);
+}
+
+
+/******************************************************************************/
+/* DC/Phase/Gain Correction */
+/******************************************************************************/
+
+// clang-format off
+static const struct {
+ struct {
+ uint16_t reg[2]; /* Low/High band */
+ unsigned int shift; /* Value scaling */
+ } corr[4];
+} ad9361_correction_reg_table[4] = {
+ [BLADERF_CHANNEL_RX(0)].corr = {
+ [BLADERF_CORR_DCOFF_I] = {
+ FIELD_INIT(.reg, {0, 0}), /* More complex look up */
+ FIELD_INIT(.shift, 0),
+ },
+ [BLADERF_CORR_DCOFF_Q] = {
+ FIELD_INIT(.reg, {0, 0}), /* More complex look up */
+ FIELD_INIT(.shift, 0),
+ },
+ [BLADERF_CORR_PHASE] = {
+ FIELD_INIT(.reg, { AD936X_REG_RX1_INPUT_A_PHASE_CORR,
+ AD936X_REG_RX1_INPUT_BC_PHASE_CORR }),
+ FIELD_INIT(.shift, 6),
+ },
+ [BLADERF_CORR_GAIN] = {
+ FIELD_INIT(.reg, { AD936X_REG_RX1_INPUT_A_GAIN_CORR,
+ AD936X_REG_RX1_INPUT_BC_PHASE_CORR }),
+ FIELD_INIT(.shift, 6),
+ }
+ },
+ [BLADERF_CHANNEL_RX(1)].corr = {
+ [BLADERF_CORR_DCOFF_I] = {
+ FIELD_INIT(.reg, {0, 0}), /* More complex look up */
+ FIELD_INIT(.shift, 0),
+ },
+ [BLADERF_CORR_DCOFF_Q] = {
+ FIELD_INIT(.reg, {0, 0}), /* More complex look up */
+ FIELD_INIT(.shift, 0),
+ },
+ [BLADERF_CORR_PHASE] = {
+ FIELD_INIT(.reg, { AD936X_REG_RX2_INPUT_A_PHASE_CORR,
+ AD936X_REG_RX2_INPUT_BC_PHASE_CORR }),
+ FIELD_INIT(.shift, 6),
+ },
+ [BLADERF_CORR_GAIN] = {
+ FIELD_INIT(.reg, { AD936X_REG_RX2_INPUT_A_GAIN_CORR,
+ AD936X_REG_RX2_INPUT_BC_PHASE_CORR }),
+ FIELD_INIT(.shift, 6),
+ }
+ },
+ [BLADERF_CHANNEL_TX(0)].corr = {
+ [BLADERF_CORR_DCOFF_I] = {
+ FIELD_INIT(.reg, { AD936X_REG_TX1_OUT_1_OFFSET_I,
+ AD936X_REG_TX1_OUT_2_OFFSET_I }),
+ FIELD_INIT(.shift, 5),
+ },
+ [BLADERF_CORR_DCOFF_Q] = {
+ FIELD_INIT(.reg, { AD936X_REG_TX1_OUT_1_OFFSET_Q,
+ AD936X_REG_TX1_OUT_2_OFFSET_Q }),
+ FIELD_INIT(.shift, 5),
+ },
+ [BLADERF_CORR_PHASE] = {
+ FIELD_INIT(.reg, { AD936X_REG_TX1_OUT_1_PHASE_CORR,
+ AD936X_REG_TX1_OUT_2_PHASE_CORR }),
+ FIELD_INIT(.shift, 6),
+ },
+ [BLADERF_CORR_GAIN] = {
+ FIELD_INIT(.reg, { AD936X_REG_TX1_OUT_1_GAIN_CORR,
+ AD936X_REG_TX1_OUT_2_GAIN_CORR }),
+ FIELD_INIT(.shift, 6),
+ }
+ },
+ [BLADERF_CHANNEL_TX(1)].corr = {
+ [BLADERF_CORR_DCOFF_I] = {
+ FIELD_INIT(.reg, { AD936X_REG_TX2_OUT_1_OFFSET_I,
+ AD936X_REG_TX2_OUT_2_OFFSET_I }),
+ FIELD_INIT(.shift, 5),
+ },
+ [BLADERF_CORR_DCOFF_Q] = {
+ FIELD_INIT(.reg, { AD936X_REG_TX2_OUT_1_OFFSET_Q,
+ AD936X_REG_TX2_OUT_2_OFFSET_Q }),
+ FIELD_INIT(.shift, 5),
+ },
+ [BLADERF_CORR_PHASE] = {
+ FIELD_INIT(.reg, { AD936X_REG_TX2_OUT_1_PHASE_CORR,
+ AD936X_REG_TX2_OUT_2_PHASE_CORR }),
+ FIELD_INIT(.shift, 6),
+ },
+ [BLADERF_CORR_GAIN] = {
+ FIELD_INIT(.reg, { AD936X_REG_TX2_OUT_1_GAIN_CORR,
+ AD936X_REG_TX2_OUT_2_GAIN_CORR }),
+ FIELD_INIT(.shift, 6),
+ }
+ },
+};
+
+static const struct {
+ uint16_t reg_top;
+ uint16_t reg_bot;
+} ad9361_correction_rx_dcoff_reg_table[4][2][2] = {
+ /* Channel 1 */
+ [BLADERF_CHANNEL_RX(0)] = {
+ /* A band */
+ {
+ /* I */
+ {AD936X_REG_INPUT_A_OFFSETS_1, AD936X_REG_RX1_INPUT_A_OFFSETS},
+ /* Q */
+ {AD936X_REG_RX1_INPUT_A_OFFSETS, AD936X_REG_RX1_INPUT_A_Q_OFFSET},
+ },
+ /* B/C band */
+ {
+ /* I */
+ {AD936X_REG_INPUT_BC_OFFSETS_1, AD936X_REG_RX1_INPUT_BC_OFFSETS},
+ /* Q */
+ {AD936X_REG_RX1_INPUT_BC_OFFSETS, AD936X_REG_RX1_INPUT_BC_Q_OFFSET},
+ },
+ },
+ /* Channel 2 */
+ [BLADERF_CHANNEL_RX(1)] = {
+ /* A band */
+ {
+ /* I */
+ {AD936X_REG_RX2_INPUT_A_I_OFFSET, AD936X_REG_RX2_INPUT_A_OFFSETS},
+ /* Q */
+ {AD936X_REG_RX2_INPUT_A_OFFSETS, AD936X_REG_INPUT_A_OFFSETS_1},
+ },
+ /* B/C band */
+ {
+ /* I */
+ {AD936X_REG_RX2_INPUT_BC_I_OFFSET, AD936X_REG_RX2_INPUT_BC_OFFSETS},
+ /* Q */
+ {AD936X_REG_RX2_INPUT_BC_OFFSETS, AD936X_REG_INPUT_BC_OFFSETS_1},
+ },
+ },
+};
+
+static const int ad9361_correction_force_bit[2][4][2] = {
+ [0] = {
+ [BLADERF_CORR_DCOFF_I] = {2, 6},
+ [BLADERF_CORR_DCOFF_Q] = {2, 6},
+ [BLADERF_CORR_PHASE] = {0, 4},
+ [BLADERF_CORR_GAIN] = {0, 4},
+ },
+ [1] = {
+ [BLADERF_CORR_DCOFF_I] = {3, 7},
+ [BLADERF_CORR_DCOFF_Q] = {3, 7},
+ [BLADERF_CORR_PHASE] = {1, 5},
+ [BLADERF_CORR_GAIN] = {1, 5},
+ },
+};
+// clang-format on
+
+static int bladerf2_get_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_correction corr,
+ int16_t *value)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(value);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ bool low_band;
+ uint16_t reg, data;
+ unsigned int shift;
+ int32_t val;
+
+ IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, {
+ log_debug("%s: FPGA command mode not supported\n", __FUNCTION__);
+ return BLADERF_ERR_UNSUPPORTED;
+ });
+
+ /* Validate channel */
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_RX(1) &&
+ ch != BLADERF_CHANNEL_TX(0) && ch != BLADERF_CHANNEL_TX(1)) {
+ RETURN_INVAL_ARG("channel", ch, "is not valid");
+ }
+
+ /* Validate correction */
+ if (corr != BLADERF_CORR_DCOFF_I && corr != BLADERF_CORR_DCOFF_Q &&
+ corr != BLADERF_CORR_PHASE && corr != BLADERF_CORR_GAIN) {
+ RETURN_ERROR_STATUS("corr", BLADERF_ERR_UNSUPPORTED);
+ }
+
+ /* Look up band */
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ uint32_t mode;
+
+ CHECK_AD936X(ad9361_get_tx_rf_port_output(phy, &mode));
+
+ low_band = (mode != AD936X_TXA);
+ } else {
+ uint32_t mode;
+
+ CHECK_AD936X(ad9361_get_rx_rf_port_input(phy, &mode));
+
+ /* Check if RX RF port mode is supported */
+ if (mode != AD936X_A_BALANCED && mode != AD936X_B_BALANCED &&
+ mode != AD936X_C_BALANCED) {
+ RETURN_ERROR_STATUS("mode", BLADERF_ERR_UNSUPPORTED);
+ }
+
+ low_band = (mode != AD936X_A_BALANCED);
+ }
+
+ if ((corr == BLADERF_CORR_DCOFF_I || corr == BLADERF_CORR_DCOFF_Q) &&
+ (ch & BLADERF_DIRECTION_MASK) == BLADERF_RX) {
+ /* RX DC offset corrections are stuffed in a super convoluted way in
+ * the register map. See AD9361 register map page 51. */
+ bool is_q = (corr == BLADERF_CORR_DCOFF_Q);
+ uint8_t data_top, data_bot;
+ uint16_t data;
+
+ /* Read top register */
+ val = ad9361_spi_read(
+ phy->spi,
+ ad9361_correction_rx_dcoff_reg_table[ch][low_band][is_q].reg_top);
+ if (val < 0) {
+ RETURN_ERROR_AD9361("ad9361_spi_read(top)", val);
+ }
+
+ data_top = val;
+
+ /* Read bottom register */
+ val = ad9361_spi_read(
+ phy->spi,
+ ad9361_correction_rx_dcoff_reg_table[ch][low_band][is_q].reg_bot);
+ if (val < 0) {
+ RETURN_ERROR_AD9361("ad9361_spi_read(bottom)", val);
+ }
+
+ data_bot = val;
+
+ if (ch == BLADERF_CHANNEL_RX(0)) {
+ if (!is_q) {
+ /* top: | x x x x 9 8 7 6 | */
+ /* bottom: | 5 4 3 2 1 0 x x | */
+ data = ((data_top & 0xf) << 6) | (data_bot >> 2);
+ } else {
+ /* top: | x x x x x x 9 8 | */
+ /* bottom: | 7 6 5 4 3 2 1 0 | */
+ data = ((data_top & 0x3) << 8) | data_bot;
+ }
+ } else {
+ if (!is_q) {
+ /* top: | 9 8 7 6 5 4 3 2 | */
+ /* bottom: | x x x x x x 1 0 | */
+ data = (data_top << 2) | (data_bot & 0x3);
+ } else {
+ /* top: | x x 9 8 7 6 5 4 | */
+ /* bottom: | 3 2 1 0 x x x x | */
+ data = (data_top << 4) | (data_bot >> 4);
+ }
+ }
+
+ /* Scale 10-bit to 13-bit */
+ data = data << 3;
+
+ /* Sign extend value */
+ *value = data | ((data & (1 << 12)) ? 0xf000 : 0x0000);
+ } else {
+ /* Look up correction register and value shift in table */
+ reg = ad9361_correction_reg_table[ch].corr[corr].reg[low_band];
+ shift = ad9361_correction_reg_table[ch].corr[corr].shift;
+
+ /* Read register and scale value */
+ val = ad9361_spi_read(phy->spi, reg);
+ if (val < 0) {
+ RETURN_ERROR_AD9361("ad9361_spi_read(reg)", val);
+ }
+
+ /* Scale 8-bit to 12-bit/13-bit */
+ data = val << shift;
+
+ /* Sign extend value */
+ if (shift == 5) {
+ *value = data | ((data & (1 << 12)) ? 0xf000 : 0x0000);
+ } else {
+ *value = data | ((data & (1 << 13)) ? 0xc000 : 0x0000);
+ }
+ }
+
+ return 0;
+}
+
+static int bladerf2_set_correction(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_correction corr,
+ int16_t value)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ bool low_band;
+ uint16_t reg, data;
+ unsigned int shift;
+ int32_t val;
+
+ IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, {
+ log_debug("%s: FPGA command mode not supported\n", __FUNCTION__);
+ return BLADERF_ERR_UNSUPPORTED;
+ });
+
+ /* Validate channel */
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_RX(1) &&
+ ch != BLADERF_CHANNEL_TX(0) && ch != BLADERF_CHANNEL_TX(1)) {
+ RETURN_INVAL_ARG("channel", ch, "is not valid");
+ }
+
+ /* Validate correction */
+ if (corr != BLADERF_CORR_DCOFF_I && corr != BLADERF_CORR_DCOFF_Q &&
+ corr != BLADERF_CORR_PHASE && corr != BLADERF_CORR_GAIN) {
+ RETURN_ERROR_STATUS("corr", BLADERF_ERR_UNSUPPORTED);
+ }
+
+ /* Look up band */
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ uint32_t mode;
+
+ CHECK_AD936X(ad9361_get_tx_rf_port_output(phy, &mode));
+
+ low_band = (mode != AD936X_TXA);
+ } else {
+ uint32_t mode;
+
+ CHECK_AD936X(ad9361_get_rx_rf_port_input(phy, &mode));
+
+ /* Check if RX RF port mode is supported */
+ if (mode != AD936X_A_BALANCED && mode != AD936X_B_BALANCED &&
+ mode != AD936X_C_BALANCED) {
+ RETURN_ERROR_STATUS("mode", BLADERF_ERR_UNSUPPORTED);
+ }
+
+ low_band = (mode != AD936X_A_BALANCED);
+ }
+
+ if ((corr == BLADERF_CORR_DCOFF_I || corr == BLADERF_CORR_DCOFF_Q) &&
+ (ch & BLADERF_DIRECTION_MASK) == BLADERF_RX) {
+ /* RX DC offset corrections are stuffed in a super convoluted way in
+ * the register map. See AD9361 register map page 51. */
+ bool is_q = (corr == BLADERF_CORR_DCOFF_Q);
+ uint8_t data_top, data_bot;
+
+ /* Scale 13-bit to 10-bit */
+ data = value >> 3;
+
+ /* Read top register */
+ val = ad9361_spi_read(
+ phy->spi,
+ ad9361_correction_rx_dcoff_reg_table[ch][low_band][is_q].reg_top);
+ if (val < 0) {
+ RETURN_ERROR_AD9361("ad9361_spi_read(top)", val);
+ }
+
+ data_top = val;
+
+ /* Read bottom register */
+ val = ad9361_spi_read(
+ phy->spi,
+ ad9361_correction_rx_dcoff_reg_table[ch][low_band][is_q].reg_bot);
+ if (val < 0) {
+ RETURN_ERROR_AD9361("ad9361_spi_read(bottom)", val);
+ }
+
+ data_bot = val;
+
+ /* Modify registers */
+ if (ch == BLADERF_CHANNEL_RX(0)) {
+ if (!is_q) {
+ /* top: | x x x x 9 8 7 6 | */
+ /* bottom: | 5 4 3 2 1 0 x x | */
+ data_top = (data_top & 0xf0) | ((data >> 6) & 0x0f);
+ data_bot = (data_bot & 0x03) | ((data & 0x3f) << 2);
+ } else {
+ /* top: | x x x x x x 9 8 | */
+ /* bottom: | 7 6 5 4 3 2 1 0 | */
+ data_top = (data_top & 0xfc) | ((data >> 8) & 0x03);
+ data_bot = data & 0xff;
+ }
+ } else {
+ if (!is_q) {
+ /* top: | 9 8 7 6 5 4 3 2 | */
+ /* bottom: | x x x x x x 1 0 | */
+ data_top = (data >> 2) & 0xff;
+ data_bot = (data_bot & 0xfc) | (data & 0x03);
+ } else {
+ /* top: | x x 9 8 7 6 5 4 | */
+ /* bottom: | 3 2 1 0 x x x x | */
+ data_top = (data & 0xc0) | ((data >> 4) & 0x3f);
+ data_bot = (data & 0x0f) | ((data & 0x0f) << 4);
+ }
+ }
+
+ /* Write top register */
+ CHECK_AD936X(ad9361_spi_write(
+ phy->spi,
+ ad9361_correction_rx_dcoff_reg_table[ch][low_band][is_q].reg_top,
+ data_top));
+
+ /* Write bottom register */
+ CHECK_AD936X(ad9361_spi_write(
+ phy->spi,
+ ad9361_correction_rx_dcoff_reg_table[ch][low_band][is_q].reg_bot,
+ data_bot));
+ } else {
+ /* Look up correction register and value shift in table */
+ reg = ad9361_correction_reg_table[ch].corr[corr].reg[low_band];
+ shift = ad9361_correction_reg_table[ch].corr[corr].shift;
+
+ /* Scale 12-bit/13-bit to 8-bit */
+ data = (value >> shift) & 0xff;
+
+ /* Write register */
+ CHECK_AD936X(ad9361_spi_write(phy->spi, reg, data & 0xff));
+ }
+
+ reg = (BLADERF_CHANNEL_IS_TX(ch)) ? AD936X_REG_TX_FORCE_BITS
+ : AD936X_REG_FORCE_BITS;
+
+ /* Read force bit register */
+ val = ad9361_spi_read(phy->spi, reg);
+ if (val < 0) {
+ RETURN_ERROR_AD9361("ad9361_spi_read(force)", val);
+ }
+
+ /* Modify register */
+ data = val | (1 << ad9361_correction_force_bit[ch >> 1][corr][low_band]);
+
+ /* Write force bit register */
+ CHECK_AD936X(ad9361_spi_write(phy->spi, reg, data));
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Trigger */
+/******************************************************************************/
+
+static int bladerf2_trigger_init(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal signal,
+ struct bladerf_trigger *trigger)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(trigger);
+
+ return fpga_trigger_init(dev, ch, signal, trigger);
+}
+
+static int bladerf2_trigger_arm(struct bladerf *dev,
+ struct bladerf_trigger const *trigger,
+ bool arm,
+ uint64_t resv1,
+ uint64_t resv2)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(trigger);
+
+ return fpga_trigger_arm(dev, trigger, arm);
+}
+
+static int bladerf2_trigger_fire(struct bladerf *dev,
+ struct bladerf_trigger const *trigger)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(trigger);
+
+ return fpga_trigger_fire(dev, trigger);
+}
+
+static int bladerf2_trigger_state(struct bladerf *dev,
+ struct bladerf_trigger const *trigger,
+ bool *is_armed,
+ bool *has_fired,
+ bool *fire_requested,
+ uint64_t *resv1,
+ uint64_t *resv2)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(trigger);
+ NULL_CHECK(is_armed);
+ NULL_CHECK(has_fired);
+ NULL_CHECK(fire_requested);
+
+ /* Reserved for future metadata (e.g., trigger counts, timestamp) */
+ if (resv1 != NULL) {
+ *resv1 = 0;
+ }
+
+ if (resv2 != NULL) {
+ *resv2 = 0;
+ }
+
+ return fpga_trigger_state(dev, trigger, is_armed, has_fired,
+ fire_requested);
+}
+
+
+/******************************************************************************/
+/* Streaming */
+/******************************************************************************/
+
+static int bladerf2_init_stream(struct bladerf_stream **stream,
+ struct bladerf *dev,
+ bladerf_stream_cb callback,
+ void ***buffers,
+ size_t num_buffers,
+ bladerf_format format,
+ size_t samples_per_buffer,
+ size_t num_transfers,
+ void *user_data)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ return async_init_stream(stream, dev, callback, buffers, num_buffers,
+ format, samples_per_buffer, num_transfers,
+ user_data);
+}
+
+static int bladerf2_stream(struct bladerf_stream *stream,
+ bladerf_channel_layout layout)
+{
+ bladerf_direction dir = layout & BLADERF_DIRECTION_MASK;
+ int rv;
+
+ switch (layout) {
+ case BLADERF_RX_X1:
+ case BLADERF_RX_X2:
+ case BLADERF_TX_X1:
+ case BLADERF_TX_X2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ WITH_MUTEX(&stream->dev->lock, {
+ CHECK_STATUS_LOCKED(
+ perform_format_config(stream->dev, dir, stream->format));
+ });
+
+ rv = async_run_stream(stream, layout);
+
+ WITH_MUTEX(&stream->dev->lock, {
+ CHECK_STATUS_LOCKED(
+ perform_format_deconfig(stream->dev, dir));
+ });
+
+ return rv;
+}
+
+static int bladerf2_submit_stream_buffer(struct bladerf_stream *stream,
+ void *buffer,
+ unsigned int timeout_ms,
+ bool nonblock)
+{
+ size_t len;
+ len = async_stream_buf_bytes(stream);
+ return async_submit_stream_buffer(stream, buffer, &len, timeout_ms, nonblock);
+}
+
+static void bladerf2_deinit_stream(struct bladerf_stream *stream)
+{
+ async_deinit_stream(stream);
+}
+
+static int bladerf2_set_stream_timeout(struct bladerf *dev,
+ bladerf_direction dir,
+ unsigned int timeout)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ WITH_MUTEX(&board_data->sync[dir].lock,
+ { board_data->sync[dir].stream_config.timeout_ms = timeout; });
+
+ return 0;
+}
+
+static int bladerf2_get_stream_timeout(struct bladerf *dev,
+ bladerf_direction dir,
+ unsigned int *timeout)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(timeout);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ WITH_MUTEX(&board_data->sync[dir].lock,
+ { *timeout = board_data->sync[dir].stream_config.timeout_ms; });
+
+ return 0;
+}
+
+static int bladerf2_sync_config(struct bladerf *dev,
+ bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int num_buffers,
+ unsigned int buffer_size,
+ unsigned int num_transfers,
+ unsigned int stream_timeout)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ bladerf_direction dir = layout & BLADERF_DIRECTION_MASK;
+ int status;
+
+ if (dev->feature == BLADERF_FEATURE_OVERSAMPLE
+ && (format == BLADERF_FORMAT_SC16_Q11 || format == BLADERF_FORMAT_SC16_Q11_META)) {
+ log_error("16bit format unsupported with OVERSAMPLE feature enabled\n");
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ switch (layout) {
+ case BLADERF_RX_X1:
+ case BLADERF_RX_X2:
+ case BLADERF_TX_X1:
+ case BLADERF_TX_X2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ status = perform_format_config(dev, dir, format);
+ if (0 == status) {
+ status = sync_init(&board_data->sync[dir], dev, layout, format,
+ num_buffers, buffer_size, board_data->msg_size,
+ num_transfers, stream_timeout);
+ if (status != 0) {
+ perform_format_deconfig(dev, dir);
+ }
+ }
+
+ return status;
+}
+
+static int bladerf2_sync_tx(struct bladerf *dev,
+ void const *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ if (!board_data->sync[BLADERF_TX].initialized) {
+ RETURN_INVAL("sync tx", "not initialized");
+ }
+
+ return sync_tx(&board_data->sync[BLADERF_TX], samples, num_samples,
+ metadata, timeout_ms);
+}
+
+static int bladerf2_sync_rx(struct bladerf *dev,
+ void *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ if (!board_data->sync[BLADERF_RX].initialized) {
+ RETURN_INVAL("sync rx", "not initialized");
+ }
+
+ return sync_rx(&board_data->sync[BLADERF_RX], samples, num_samples,
+ metadata, timeout_ms);
+}
+
+static int bladerf2_get_timestamp(struct bladerf *dev,
+ bladerf_direction dir,
+ bladerf_timestamp *value)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(value);
+
+ return dev->backend->get_timestamp(dev, dir, value);
+}
+
+
+/******************************************************************************/
+/* FPGA/Firmware Loading/Flashing */
+/******************************************************************************/
+
+static int bladerf2_load_fpga(struct bladerf *dev,
+ uint8_t const *buf,
+ size_t length)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(buf);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ if (!is_valid_fpga_size(dev, board_data->fpga_size, length)) {
+ RETURN_INVAL("fpga file", "incorrect file size");
+ }
+
+ CHECK_STATUS(dev->backend->load_fpga(dev, buf, length));
+
+ /* Update device state */
+ board_data->state = STATE_FPGA_LOADED;
+
+ CHECK_STATUS(_bladerf2_initialize(dev));
+
+ return 0;
+}
+
+static int bladerf2_flash_fpga(struct bladerf *dev,
+ uint8_t const *buf,
+ size_t length)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(buf);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ if (!is_valid_fpga_size(dev, board_data->fpga_size, length)) {
+ RETURN_INVAL("fpga file", "incorrect file size");
+ }
+
+ return spi_flash_write_fpga_bitstream(dev, buf, length);
+}
+
+static int bladerf2_erase_stored_fpga(struct bladerf *dev)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ return spi_flash_erase_fpga(dev);
+}
+
+static int bladerf2_flash_firmware(struct bladerf *dev,
+ uint8_t const *buf,
+ size_t length)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(buf);
+
+ char const env_override[] = "BLADERF_SKIP_FW_SIZE_CHECK";
+
+ /* Sanity check firmware length.
+ *
+ * TODO in the future, better sanity checks can be performed when
+ * using the bladerf image format currently used to backup/restore
+ * calibration data
+ */
+ if (!getenv(env_override) && !is_valid_fw_size(length)) {
+ log_info("Detected potentially invalid firmware file.\n");
+ log_info("Define BLADERF_SKIP_FW_SIZE_CHECK in your environment "
+ "to skip this check.\n");
+ RETURN_INVAL_ARG("firmware size", length, "is not valid");
+ }
+
+ return spi_flash_write_fx3_fw(dev, buf, length);
+}
+
+static int bladerf2_device_reset(struct bladerf *dev)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ return dev->backend->device_reset(dev);
+}
+
+
+/******************************************************************************/
+/* Tuning mode */
+/******************************************************************************/
+
+static inline bool _supports_fpga_tuning(struct bladerf *dev)
+{
+ extern struct controller_fns const rfic_fpga_control;
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return (have_cap(board_data->capabilities, BLADERF_CAP_FPGA_TUNING) &&
+ rfic_fpga_control.is_present(dev));
+}
+
+static int bladerf2_set_tuning_mode(struct bladerf *dev,
+ bladerf_tuning_mode mode)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ extern struct controller_fns const rfic_host_control;
+ extern struct controller_fns const rfic_fpga_control;
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic_new = NULL;
+ struct controller_fns const *rfic_other = NULL;
+
+ bladerf_tuning_mode mode_other;
+ bladerf_rfic_init_state init_state;
+
+ log_debug("%s: New tuning mode: %s\n", __FUNCTION__, tuningmode2str(mode));
+
+ switch (mode) {
+ case BLADERF_TUNING_MODE_HOST:
+ rfic_new = &rfic_host_control;
+ rfic_other = _supports_fpga_tuning(dev) ? &rfic_fpga_control : NULL;
+ mode_other = BLADERF_TUNING_MODE_FPGA;
+ break;
+
+ case BLADERF_TUNING_MODE_FPGA:
+ /* Test capability */
+ if (!_supports_fpga_tuning(dev)) {
+ log_debug("%s: The loaded FPGA version (%u.%u.%u) does not "
+ "support FPGA RFIC control\n",
+ __FUNCTION__, board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ rfic_new = &rfic_fpga_control;
+ rfic_other = &rfic_host_control;
+ mode_other = BLADERF_TUNING_MODE_HOST;
+ break;
+
+ default:
+ log_error("%s: invalid tuning mode (%d)\n", mode);
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* De-initialize RFIC if it's initialized by another tuning mode */
+ if (NULL != rfic_other) {
+ CHECK_STATUS(rfic_other->get_init_state(dev, &init_state));
+
+ if (init_state != BLADERF_RFIC_INIT_STATE_OFF) {
+ log_debug("%s: %s %s RFIC control\n", __FUNCTION__, "Releasing",
+ tuningmode2str(mode_other));
+ CHECK_STATUS(rfic_other->deinitialize(dev));
+ }
+ }
+
+ /* Set board data */
+ board_data->rfic = rfic_new;
+ board_data->tuning_mode = mode;
+
+ /* Bring RFIC to initialized state */
+ CHECK_STATUS(rfic_new->get_init_state(dev, &init_state));
+
+ switch (init_state) {
+ case BLADERF_RFIC_INIT_STATE_OFF:
+ log_debug("%s: %s %s RFIC control\n", __FUNCTION__, "Initializing",
+ tuningmode2str(mode));
+ return rfic_new->initialize(dev);
+
+ case BLADERF_RFIC_INIT_STATE_STANDBY:
+ log_debug("%s: %s %s RFIC control\n", __FUNCTION__, "Restoring",
+ tuningmode2str(mode));
+ return rfic_new->initialize(dev);
+
+ case BLADERF_RFIC_INIT_STATE_ON:
+ log_debug("%s: %s %s RFIC control\n", __FUNCTION__, "Maintaining",
+ tuningmode2str(mode));
+ return 0;
+
+ default:
+ log_error("%s: invalid RFIC initialization state (%d)\n",
+ init_state);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+}
+
+static int bladerf2_get_tuning_mode(struct bladerf *dev,
+ bladerf_tuning_mode *mode)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(mode);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ *mode = board_data->tuning_mode;
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Loopback */
+/******************************************************************************/
+
+static int bladerf2_get_loopback_modes(
+ struct bladerf *dev, struct bladerf_loopback_modes const **modes)
+{
+ if (modes != NULL) {
+ *modes = bladerf2_loopback_modes;
+ }
+
+ return ARRAY_SIZE(bladerf2_loopback_modes);
+}
+
+static int bladerf2_set_loopback(struct bladerf *dev, bladerf_loopback mode)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ bool firmware_loopback = false;
+ int32_t bist_loopback = 0;
+
+
+ IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, {
+ if (BLADERF_LB_RFIC_BIST == mode) {
+ log_debug(
+ "%s: BLADERF_LB_RFIC_BIST not supported in FPGA command mode\n",
+ __FUNCTION__);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+ });
+
+ switch (mode) {
+ case BLADERF_LB_NONE:
+ break;
+ case BLADERF_LB_FIRMWARE:
+ firmware_loopback = true;
+ break;
+ case BLADERF_LB_RFIC_BIST:
+ bist_loopback = 1;
+ break;
+ default:
+ log_error("%s: unknown loopback mode (%d)\n", __FUNCTION__, mode);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ IF_COMMAND_MODE(dev, RFIC_COMMAND_HOST, {
+ /* Set digital loopback state */
+ CHECK_AD936X(ad9361_bist_loopback(phy, bist_loopback));
+ });
+
+ /* Set firmware loopback state */
+ CHECK_STATUS(dev->backend->set_firmware_loopback(dev, firmware_loopback));
+
+ return 0;
+}
+
+static int bladerf2_get_loopback(struct bladerf *dev, bladerf_loopback *mode)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(mode);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ int32_t ad9361_loopback;
+ bool fw_loopback;
+
+ /* Read firwmare loopback */
+ CHECK_STATUS(dev->backend->get_firmware_loopback(dev, &fw_loopback));
+
+ if (fw_loopback) {
+ *mode = BLADERF_LB_FIRMWARE;
+ return 0;
+ }
+
+ IF_COMMAND_MODE(dev, RFIC_COMMAND_HOST, {
+ /* Read AD9361 bist loopback */
+ /* Note: this returns void */
+ ad9361_get_bist_loopback(phy, &ad9361_loopback);
+
+ if (ad9361_loopback == 1) {
+ *mode = BLADERF_LB_RFIC_BIST;
+ return 0;
+ }
+ });
+
+ *mode = BLADERF_LB_NONE;
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Sample RX FPGA Mux */
+/******************************************************************************/
+
+static int bladerf2_set_rx_mux(struct bladerf *dev, bladerf_rx_mux mode)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ uint32_t rx_mux_val, config_gpio;
+
+ /* Validate desired mux mode */
+ switch (mode) {
+ case BLADERF_RX_MUX_BASEBAND:
+ case BLADERF_RX_MUX_12BIT_COUNTER:
+ case BLADERF_RX_MUX_32BIT_COUNTER:
+ case BLADERF_RX_MUX_DIGITAL_LOOPBACK:
+ rx_mux_val = ((uint32_t)mode) << BLADERF_GPIO_RX_MUX_SHIFT;
+ break;
+
+ default:
+ log_debug("Invalid RX mux mode setting passed to %s(): %d\n", mode,
+ __FUNCTION__);
+ RETURN_INVAL_ARG("bladerf_rx_mux", mode, "is invalid");
+ }
+
+ CHECK_STATUS(dev->backend->config_gpio_read(dev, &config_gpio));
+
+ /* Clear out and assign the associated RX mux bits */
+ config_gpio &= ~BLADERF_GPIO_RX_MUX_MASK;
+ config_gpio |= rx_mux_val;
+
+ CHECK_STATUS(dev->backend->config_gpio_write(dev, config_gpio));
+
+ return 0;
+}
+
+static int bladerf2_get_rx_mux(struct bladerf *dev, bladerf_rx_mux *mode)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(mode);
+
+ bladerf_rx_mux val;
+ uint32_t config_gpio;
+
+ CHECK_STATUS(dev->backend->config_gpio_read(dev, &config_gpio));
+
+ /* Extract RX mux bits */
+ config_gpio &= BLADERF_GPIO_RX_MUX_MASK;
+ config_gpio >>= BLADERF_GPIO_RX_MUX_SHIFT;
+ val = config_gpio;
+
+ /* Ensure it's a valid/supported value */
+ switch (val) {
+ case BLADERF_RX_MUX_BASEBAND:
+ case BLADERF_RX_MUX_12BIT_COUNTER:
+ case BLADERF_RX_MUX_32BIT_COUNTER:
+ case BLADERF_RX_MUX_DIGITAL_LOOPBACK:
+ *mode = val;
+ break;
+
+ default:
+ *mode = BLADERF_RX_MUX_INVALID;
+ log_debug("Invalid rx mux mode %d read from config gpio\n", val);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Low-level VCTCXO Tamer Mode */
+/******************************************************************************/
+
+static int bladerf2_set_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode mode)
+{
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int bladerf2_get_vctcxo_tamer_mode(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode *mode)
+{
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+
+/******************************************************************************/
+/* Low-level VCTCXO Trim DAC access */
+/******************************************************************************/
+
+static int _bladerf2_get_trim_dac_enable(struct bladerf *dev, bool *enable)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(enable);
+
+ uint16_t trim;
+
+ // Read current trim DAC setting
+ CHECK_STATUS(dev->backend->ad56x1_vctcxo_trim_dac_read(dev, &trim));
+
+ // Determine if it's enabled...
+ *enable = (TRIMDAC_EN_ACTIVE == (trim >> TRIMDAC_EN));
+
+ log_debug("trim DAC is %s\n", (*enable ? "enabled" : "disabled"));
+
+ if ((trim >> TRIMDAC_EN) != TRIMDAC_EN_ACTIVE &&
+ (trim >> TRIMDAC_EN) != TRIMDAC_EN_HIGHZ) {
+ log_warning("unknown trim DAC state: 0x%x\n", (trim >> TRIMDAC_EN));
+ }
+
+ return 0;
+}
+
+static int _bladerf2_set_trim_dac_enable(struct bladerf *dev, bool enable)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ uint16_t trim;
+ bool current_state;
+
+ // See if we have anything to do
+ CHECK_STATUS(_bladerf2_get_trim_dac_enable(dev, &current_state));
+
+ if (enable == current_state) {
+ log_debug("trim DAC already %s, nothing to do\n",
+ enable ? "enabled" : "disabled");
+ return 0;
+ }
+
+ // Read current trim DAC setting
+ CHECK_STATUS(dev->backend->ad56x1_vctcxo_trim_dac_read(dev, &trim));
+
+ // Set the trim DAC to high z if applicable
+ if (!enable && trim != (TRIMDAC_EN_HIGHZ << TRIMDAC_EN)) {
+ board_data->trimdac_last_value = trim;
+ log_debug("saving current trim DAC value: 0x%04x\n", trim);
+ trim = TRIMDAC_EN_HIGHZ << TRIMDAC_EN;
+ } else if (enable && trim == (TRIMDAC_EN_HIGHZ << TRIMDAC_EN)) {
+ trim = board_data->trimdac_last_value;
+ log_debug("restoring old trim DAC value: 0x%04x\n", trim);
+ }
+
+ // Write back the trim DAC setting
+ CHECK_STATUS(dev->backend->ad56x1_vctcxo_trim_dac_write(dev, trim));
+
+ // Update our state flag
+ board_data->trim_source = enable ? TRIM_SOURCE_TRIM_DAC : TRIM_SOURCE_NONE;
+
+ return 0;
+}
+
+/**
+ * @brief Read the VCTCXO trim value from the SPI flash
+ *
+ * Retrieves the factory VCTCXO value from the SPI flash. This function
+ * should not be used while sample streaming is in progress.
+ *
+ * @param dev Device handle
+ * @param trim Pointer to populate with the trim value
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+static int bladerf2_read_flash_vctcxo_trim(struct bladerf *dev, uint16_t *trim)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(trim);
+
+ int status;
+
+ status = spi_flash_read_vctcxo_trim(dev, trim);
+ if (status < 0) {
+ log_warning("Failed to get VCTCXO trim value: %s\n",
+ bladerf_strerror(status));
+ log_debug("Defaulting DAC trim to 0x1ffc.\n");
+ *trim = 0x1ffc;
+ return 0;
+ }
+
+ return status;
+}
+
+static int bladerf2_get_vctcxo_trim(struct bladerf *dev, uint16_t *trim)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(trim);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ *trim = board_data->trimdac_stored_value;
+
+ return 0;
+}
+
+static int bladerf2_trim_dac_read(struct bladerf *dev, uint16_t *trim)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(trim);
+
+ return dev->backend->ad56x1_vctcxo_trim_dac_read(dev, trim);
+}
+
+static int bladerf2_trim_dac_write(struct bladerf *dev, uint16_t trim)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ uint16_t trim_control = (trim >> TRIMDAC_EN) & TRIMDAC_EN_MASK;
+ uint16_t trim_value = trim & TRIMDAC_MASK;
+ bool enable;
+
+ log_debug("requested trim 0x%04x (control 0x%01x value 0x%04x)\n", trim,
+ trim_control, trim_value);
+
+ // Is the trimdac enabled?
+ CHECK_STATUS(_bladerf2_get_trim_dac_enable(dev, &enable));
+
+ // If the trimdac is not enabled, save this value for later but don't
+ // apply it.
+ if (!enable && trim_control != TRIMDAC_EN_HIGHZ) {
+ log_warning("trim DAC is disabled. New value will be saved until "
+ "trim DAC is enabled\n");
+ board_data->trimdac_last_value = trim_value;
+ return 0;
+ }
+
+ return dev->backend->ad56x1_vctcxo_trim_dac_write(dev, trim);
+}
+
+
+/******************************************************************************/
+/* Low-level Trigger control access */
+/******************************************************************************/
+
+static int bladerf2_read_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t *val)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(val);
+
+ return fpga_trigger_read(dev, ch, trigger, val);
+}
+
+static int bladerf2_write_trigger(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t val)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ return fpga_trigger_write(dev, ch, trigger, val);
+}
+
+/******************************************************************************/
+/* Low-level Wishbone Master access */
+/******************************************************************************/
+
+static int bladerf2_wishbone_master_read(struct bladerf *dev, uint32_t addr, uint32_t *data)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(data);
+
+ return dev->backend->wishbone_master_read(dev, addr, data);
+}
+
+static int bladerf2_wishbone_master_write(struct bladerf *dev, uint32_t addr, uint32_t data)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ return dev->backend->wishbone_master_write(dev, addr, data);
+}
+
+
+/******************************************************************************/
+/* Low-level Configuration GPIO access */
+/******************************************************************************/
+
+static int bladerf2_config_gpio_read(struct bladerf *dev, uint32_t *val)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(val);
+
+ return dev->backend->config_gpio_read(dev, val);
+}
+
+static int bladerf2_config_gpio_write(struct bladerf *dev, uint32_t val)
+{
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ return dev->backend->config_gpio_write(dev, val);
+}
+
+
+/******************************************************************************/
+/* Low-level SPI Flash access */
+/******************************************************************************/
+
+static int bladerf2_erase_flash(struct bladerf *dev,
+ uint32_t erase_block,
+ uint32_t count)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+
+ return spi_flash_erase(dev, erase_block, count);
+}
+
+static int bladerf2_read_flash(struct bladerf *dev,
+ uint8_t *buf,
+ uint32_t page,
+ uint32_t count)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(buf);
+
+ return spi_flash_read(dev, buf, page, count);
+}
+
+static int bladerf2_write_flash(struct bladerf *dev,
+ uint8_t const *buf,
+ uint32_t page,
+ uint32_t count)
+{
+ CHECK_BOARD_STATE(STATE_FIRMWARE_LOADED);
+ NULL_CHECK(buf);
+
+ return spi_flash_write(dev, buf, page, count);
+}
+
+
+/******************************************************************************/
+/* Expansion support */
+/******************************************************************************/
+
+static int bladerf2_expansion_attach(struct bladerf *dev, bladerf_xb xb)
+{
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int bladerf2_expansion_get_attached(struct bladerf *dev, bladerf_xb *xb)
+{
+ NULL_CHECK(xb);
+
+ *xb = BLADERF_XB_NONE;
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Board binding */
+/******************************************************************************/
+
+struct board_fns const bladerf2_board_fns = {
+ FIELD_INIT(.matches, bladerf2_matches),
+ FIELD_INIT(.open, bladerf2_open),
+ FIELD_INIT(.close, bladerf2_close),
+ FIELD_INIT(.device_speed, bladerf2_device_speed),
+ FIELD_INIT(.get_serial, bladerf2_get_serial),
+ FIELD_INIT(.get_fpga_size, bladerf2_get_fpga_size),
+ FIELD_INIT(.get_fpga_bytes, bladerf2_get_fpga_bytes),
+ FIELD_INIT(.get_flash_size, bladerf2_get_flash_size),
+ FIELD_INIT(.is_fpga_configured, bladerf2_is_fpga_configured),
+ FIELD_INIT(.get_fpga_source, bladerf2_get_fpga_source),
+ FIELD_INIT(.get_capabilities, bladerf2_get_capabilities),
+ FIELD_INIT(.get_channel_count, bladerf2_get_channel_count),
+ FIELD_INIT(.get_fpga_version, bladerf2_get_fpga_version),
+ FIELD_INIT(.get_fw_version, bladerf2_get_fw_version),
+ FIELD_INIT(.set_gain, bladerf2_set_gain),
+ FIELD_INIT(.get_gain, bladerf2_get_gain),
+ FIELD_INIT(.set_gain_mode, bladerf2_set_gain_mode),
+ FIELD_INIT(.get_gain_mode, bladerf2_get_gain_mode),
+ FIELD_INIT(.get_gain_modes, bladerf2_get_gain_modes),
+ FIELD_INIT(.get_gain_range, bladerf2_get_gain_range),
+ FIELD_INIT(.set_gain_stage, bladerf2_set_gain_stage),
+ FIELD_INIT(.get_gain_stage, bladerf2_get_gain_stage),
+ FIELD_INIT(.get_gain_stage_range, bladerf2_get_gain_stage_range),
+ FIELD_INIT(.get_gain_stages, bladerf2_get_gain_stages),
+ FIELD_INIT(.set_sample_rate, bladerf2_set_sample_rate),
+ FIELD_INIT(.set_rational_sample_rate, bladerf2_set_rational_sample_rate),
+ FIELD_INIT(.get_sample_rate, bladerf2_get_sample_rate),
+ FIELD_INIT(.get_sample_rate_range, bladerf2_get_sample_rate_range),
+ FIELD_INIT(.get_rational_sample_rate, bladerf2_get_rational_sample_rate),
+ FIELD_INIT(.set_bandwidth, bladerf2_set_bandwidth),
+ FIELD_INIT(.get_bandwidth, bladerf2_get_bandwidth),
+ FIELD_INIT(.get_bandwidth_range, bladerf2_get_bandwidth_range),
+ FIELD_INIT(.get_frequency, bladerf2_get_frequency),
+ FIELD_INIT(.set_frequency, bladerf2_set_frequency),
+ FIELD_INIT(.get_frequency_range, bladerf2_get_frequency_range),
+ FIELD_INIT(.select_band, bladerf2_select_band),
+ FIELD_INIT(.set_rf_port, bladerf2_set_rf_port),
+ FIELD_INIT(.get_rf_port, bladerf2_get_rf_port),
+ FIELD_INIT(.get_rf_ports, bladerf2_get_rf_ports),
+ FIELD_INIT(.get_quick_tune, bladerf2_get_quick_tune),
+ FIELD_INIT(.schedule_retune, bladerf2_schedule_retune),
+ FIELD_INIT(.cancel_scheduled_retunes, bladerf2_cancel_scheduled_retunes),
+ FIELD_INIT(.get_correction, bladerf2_get_correction),
+ FIELD_INIT(.set_correction, bladerf2_set_correction),
+ FIELD_INIT(.trigger_init, bladerf2_trigger_init),
+ FIELD_INIT(.trigger_arm, bladerf2_trigger_arm),
+ FIELD_INIT(.trigger_fire, bladerf2_trigger_fire),
+ FIELD_INIT(.trigger_state, bladerf2_trigger_state),
+ FIELD_INIT(.enable_module, bladerf2_enable_module),
+ FIELD_INIT(.init_stream, bladerf2_init_stream),
+ FIELD_INIT(.stream, bladerf2_stream),
+ FIELD_INIT(.submit_stream_buffer, bladerf2_submit_stream_buffer),
+ FIELD_INIT(.deinit_stream, bladerf2_deinit_stream),
+ FIELD_INIT(.set_stream_timeout, bladerf2_set_stream_timeout),
+ FIELD_INIT(.get_stream_timeout, bladerf2_get_stream_timeout),
+ FIELD_INIT(.sync_config, bladerf2_sync_config),
+ FIELD_INIT(.sync_tx, bladerf2_sync_tx),
+ FIELD_INIT(.sync_rx, bladerf2_sync_rx),
+ FIELD_INIT(.get_timestamp, bladerf2_get_timestamp),
+ FIELD_INIT(.load_fpga, bladerf2_load_fpga),
+ FIELD_INIT(.flash_fpga, bladerf2_flash_fpga),
+ FIELD_INIT(.erase_stored_fpga, bladerf2_erase_stored_fpga),
+ FIELD_INIT(.flash_firmware, bladerf2_flash_firmware),
+ FIELD_INIT(.device_reset, bladerf2_device_reset),
+ FIELD_INIT(.set_tuning_mode, bladerf2_set_tuning_mode),
+ FIELD_INIT(.get_tuning_mode, bladerf2_get_tuning_mode),
+ FIELD_INIT(.get_loopback_modes, bladerf2_get_loopback_modes),
+ FIELD_INIT(.set_loopback, bladerf2_set_loopback),
+ FIELD_INIT(.get_loopback, bladerf2_get_loopback),
+ FIELD_INIT(.get_rx_mux, bladerf2_get_rx_mux),
+ FIELD_INIT(.set_rx_mux, bladerf2_set_rx_mux),
+ FIELD_INIT(.set_vctcxo_tamer_mode, bladerf2_set_vctcxo_tamer_mode),
+ FIELD_INIT(.get_vctcxo_tamer_mode, bladerf2_get_vctcxo_tamer_mode),
+ FIELD_INIT(.get_vctcxo_trim, bladerf2_get_vctcxo_trim),
+ FIELD_INIT(.trim_dac_read, bladerf2_trim_dac_read),
+ FIELD_INIT(.trim_dac_write, bladerf2_trim_dac_write),
+ FIELD_INIT(.read_trigger, bladerf2_read_trigger),
+ FIELD_INIT(.write_trigger, bladerf2_write_trigger),
+ FIELD_INIT(.wishbone_master_read, bladerf2_wishbone_master_read),
+ FIELD_INIT(.wishbone_master_write, bladerf2_wishbone_master_write),
+ FIELD_INIT(.config_gpio_read, bladerf2_config_gpio_read),
+ FIELD_INIT(.config_gpio_write, bladerf2_config_gpio_write),
+ FIELD_INIT(.erase_flash, bladerf2_erase_flash),
+ FIELD_INIT(.read_flash, bladerf2_read_flash),
+ FIELD_INIT(.write_flash, bladerf2_write_flash),
+ FIELD_INIT(.expansion_attach, bladerf2_expansion_attach),
+ FIELD_INIT(.expansion_get_attached, bladerf2_expansion_get_attached),
+ FIELD_INIT(.name, "bladerf2"),
+};
+
+
+/******************************************************************************
+ ******************************************************************************
+ * bladeRF2-specific Functions *
+ ******************************************************************************
+ ******************************************************************************/
+
+/******************************************************************************/
+/* Bias Tee Control */
+/******************************************************************************/
+
+int bladerf_get_bias_tee(struct bladerf *dev, bladerf_channel ch, bool *enable)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(enable);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t reg;
+ uint32_t shift;
+
+ shift = BLADERF_CHANNEL_IS_TX(ch) ? RFFE_CONTROL_TX_BIAS_EN
+ : RFFE_CONTROL_RX_BIAS_EN;
+
+ /* Read RFFE control register */
+ CHECK_STATUS_LOCKED(dev->backend->rffe_control_read(dev, &reg));
+
+ /* Check register value */
+ *enable = (reg >> shift) & 0x1;
+ });
+
+ return 0;
+}
+
+int bladerf_set_bias_tee(struct bladerf *dev, bladerf_channel ch, bool enable)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t reg;
+ uint32_t shift;
+
+ shift = BLADERF_CHANNEL_IS_TX(ch) ? RFFE_CONTROL_TX_BIAS_EN
+ : RFFE_CONTROL_RX_BIAS_EN;
+
+ /* Read RFFE control register */
+ CHECK_STATUS_LOCKED(dev->backend->rffe_control_read(dev, &reg));
+
+ /* Clear register value */
+ reg &= ~(1 << shift);
+
+ /* Set register value */
+ if (enable) {
+ reg |= (1 << shift);
+ }
+
+ /* Write RFFE control register */
+ log_debug("%s: rffe_control_write %08x\n", __FUNCTION__, reg);
+ CHECK_STATUS_LOCKED(dev->backend->rffe_control_write(dev, reg));
+ });
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Low level RFIC Accessors */
+/******************************************************************************/
+
+int bladerf_get_rfic_register(struct bladerf *dev,
+ uint16_t address,
+ uint8_t *val)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(val);
+
+ WITH_MUTEX(&dev->lock, {
+ uint64_t data;
+
+ address |= (AD936X_READ | AD936X_CNT(1));
+
+ CHECK_AD936X_LOCKED(dev->backend->ad9361_spi_read(dev, address, &data));
+
+ *val = (data >> 56) & 0xff;
+ });
+
+ return 0;
+}
+
+int bladerf_set_rfic_register(struct bladerf *dev,
+ uint16_t address,
+ uint8_t val)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ WITH_MUTEX(&dev->lock, {
+ uint64_t data = (((uint64_t)val) << 56);
+
+ address |= (AD936X_WRITE | AD936X_CNT(1));
+
+ CHECK_AD936X_LOCKED(dev->backend->ad9361_spi_write(dev, address, data));
+ });
+
+ return 0;
+}
+
+int bladerf_get_rfic_temperature(struct bladerf *dev, float *val)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(val);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+
+ IF_COMMAND_MODE(dev, RFIC_COMMAND_FPGA, {
+ log_debug("%s: FPGA command mode not supported\n", __FUNCTION__);
+ return BLADERF_ERR_UNSUPPORTED;
+ });
+
+ WITH_MUTEX(&dev->lock, { *val = ad9361_get_temp(phy) / 1000.0F; });
+
+ return 0;
+}
+
+int bladerf_get_rfic_rssi(struct bladerf *dev,
+ bladerf_channel ch,
+ int *pre_rssi,
+ int *sym_rssi)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(pre_rssi);
+ NULL_CHECK(sym_rssi);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+
+ WITH_MUTEX(&dev->lock, {
+ CHECK_STATUS_LOCKED(rfic->get_rssi(dev, ch, pre_rssi, sym_rssi));
+ });
+
+ return 0;
+}
+
+int bladerf_get_rfic_ctrl_out(struct bladerf *dev, uint8_t *ctrl_out)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(ctrl_out);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t reg;
+
+ /* Read RFFE control register */
+ CHECK_STATUS_LOCKED(dev->backend->rffe_control_read(dev, &reg));
+
+ *ctrl_out = (uint8_t)((reg >> RFFE_CONTROL_CTRL_OUT) & 0xFF);
+ });
+
+ return 0;
+}
+
+int bladerf_get_rfic_rx_fir(struct bladerf *dev, bladerf_rfic_rxfir *rxfir)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(rxfir);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ bladerf_channel const ch = BLADERF_CHANNEL_RX(0);
+
+ WITH_MUTEX(&dev->lock, {
+ CHECK_STATUS_LOCKED(rfic->get_filter(dev, ch, rxfir, NULL));
+ });
+
+ return 0;
+}
+
+int bladerf_set_rfic_rx_fir(struct bladerf *dev, bladerf_rfic_rxfir rxfir)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ struct bladerf_range const sr_range = bladerf2_sample_rate_range_4x;
+ bladerf_channel const ch = BLADERF_CHANNEL_RX(0);
+
+ WITH_MUTEX(&dev->lock, {
+ /* Verify that sample rate is not too low */
+ if (rxfir != BLADERF_RFIC_RXFIR_DEC4) {
+ bladerf_sample_rate sr;
+
+ CHECK_STATUS_LOCKED(dev->board->get_sample_rate(dev, ch, &sr));
+
+ if (is_within_range(&sr_range, sr)) {
+ log_error("%s: sample rate too low for filter (%d < %d)\n",
+ __FUNCTION__, sr, sr_range.min);
+ MUTEX_UNLOCK(&dev->lock);
+ return BLADERF_ERR_INVAL;
+ }
+ }
+
+ CHECK_STATUS_LOCKED(rfic->set_filter(dev, ch, rxfir, 0));
+ });
+
+ return 0;
+}
+
+int bladerf_get_rfic_tx_fir(struct bladerf *dev, bladerf_rfic_txfir *txfir)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(txfir);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ bladerf_channel const ch = BLADERF_CHANNEL_TX(0);
+
+ WITH_MUTEX(&dev->lock, {
+ CHECK_STATUS_LOCKED(rfic->get_filter(dev, ch, NULL, txfir));
+ });
+
+ return 0;
+}
+
+int bladerf_set_rfic_tx_fir(struct bladerf *dev, bladerf_rfic_txfir txfir)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ struct bladerf_range const sr_range = bladerf2_sample_rate_range_4x;
+ bladerf_channel const ch = BLADERF_CHANNEL_TX(0);
+
+ WITH_MUTEX(&dev->lock, {
+ /* Verify that sample rate is not too low */
+ if (txfir != BLADERF_RFIC_TXFIR_INT4) {
+ bladerf_sample_rate sr;
+
+ CHECK_STATUS_LOCKED(dev->board->get_sample_rate(dev, ch, &sr));
+
+ if (is_within_range(&sr_range, sr)) {
+ log_error("%s: sample rate too low for filter (%d < %d)\n",
+ __FUNCTION__, sr, sr_range.min);
+ MUTEX_UNLOCK(&dev->lock);
+ return BLADERF_ERR_INVAL;
+ }
+ }
+
+ CHECK_STATUS_LOCKED(rfic->set_filter(dev, ch, 0, txfir));
+ });
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Low level PLL Accessors */
+/******************************************************************************/
+
+static int bladerf_pll_configure(struct bladerf *dev, uint16_t R, uint16_t N)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ uint32_t init_array[3];
+ bool is_enabled;
+ uint8_t i;
+
+ if (R < 1 || R > 16383) {
+ RETURN_INVAL("R", "outside range [1,16383]");
+ }
+
+ if (N < 1 || N > 8191) {
+ RETURN_INVAL("N", "outside range [1,8191]");
+ }
+
+ /* Get the present state... */
+ CHECK_STATUS(bladerf_get_pll_enable(dev, &is_enabled));
+
+ /* Enable the chip if applicable */
+ if (!is_enabled) {
+ CHECK_STATUS(bladerf_set_pll_enable(dev, true));
+ }
+
+ /* Register 0: Reference Counter Latch */
+ init_array[0] = 0;
+ /* R Counter: */
+ init_array[0] |= (R & ((1 << 14) - 1)) << 2;
+ /* Hardcoded values: */
+ /* Anti-backlash: 00 (2.9 ns) */
+ /* Lock detect precision: 0 (three cycles) */
+
+ /* Register 1: N Counter Latch */
+ init_array[1] = 1;
+ /* N Counter: */
+ init_array[1] |= (N & ((1 << 13) - 1)) << 8;
+ /* Hardcoded values: */
+ /* CP Gain: 0 (Setting 1) */
+
+ /* Register 2: Function Latch */
+ init_array[2] = 2;
+ /* Hardcoded values: */
+ /* Counter operation: 0 (Normal) */
+ /* Power down control: 00 (Normal) */
+ /* Muxout control: 0x1 (digital lock detect) */
+ init_array[2] |= (1 & ((1 << 3) - 1)) << 4;
+ /* PD Polarity: 1 (positive) */
+ init_array[2] |= 1 << 7;
+ /* CP three-state: 0 (normal) */
+ /* Fastlock Mode: 00 (disabled) */
+ /* Timer Counter Control: 0000 (3 PFD cycles) */
+ /* Current Setting 1: 111 (5 mA) */
+ init_array[2] |= 0x7 << 15;
+ /* Current Setting 2: 111 (5 mA) */
+ init_array[2] |= 0x7 << 18;
+
+ /* Write the values to the chip */
+ for (i = 0; i < ARRAY_SIZE(init_array); ++i) {
+ CHECK_STATUS(bladerf_set_pll_register(dev, i, init_array[i]));
+ }
+
+ /* Re-disable the chip if applicable */
+ if (!is_enabled) {
+ CHECK_STATUS(bladerf_set_pll_enable(dev, false));
+ }
+
+ return 0;
+}
+
+static int bladerf_pll_calculate_ratio(bladerf_frequency ref_freq,
+ bladerf_frequency clock_freq,
+ uint16_t *R,
+ uint16_t *N)
+{
+ NULL_CHECK(R);
+ NULL_CHECK(N);
+
+ size_t const Rmax = 16383;
+ double const tol = 0.00001;
+ double target = (double)clock_freq / (double)ref_freq;
+ uint16_t R_try, N_try;
+
+ struct bladerf_range const clock_frequency_range = {
+ FIELD_INIT(.min, 5000000),
+ FIELD_INIT(.max, 400000000),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ };
+
+ if (!is_within_range(&bladerf2_pll_refclk_range, ref_freq)) {
+ return BLADERF_ERR_RANGE;
+ }
+
+ if (!is_within_range(&clock_frequency_range, clock_freq)) {
+ return BLADERF_ERR_RANGE;
+ }
+
+ for (R_try = 1; R_try < Rmax; ++R_try) {
+ double ratio, delta;
+
+ N_try = (uint16_t)(target * R_try + 0.5);
+
+ if (N_try > 8191) {
+ continue;
+ }
+
+ ratio = (double)N_try / (double)R_try;
+ delta = (ratio > target) ? (ratio - target) : (target - ratio);
+
+ if (delta < tol) {
+ *R = R_try;
+ *N = N_try;
+
+ return 0;
+ }
+ }
+
+ RETURN_INVAL("requested ratio", "not achievable");
+}
+
+int bladerf_get_pll_lock_state(struct bladerf *dev, bool *locked)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(locked);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t reg;
+
+ /* Read RFFE control register */
+ CHECK_STATUS_LOCKED(dev->backend->rffe_control_read(dev, &reg));
+
+ *locked = (reg >> RFFE_CONTROL_ADF_MUXOUT) & 0x1;
+ });
+
+ return 0;
+}
+
+int bladerf_get_pll_enable(struct bladerf *dev, bool *enabled)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(enabled);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t data;
+
+ CHECK_STATUS_LOCKED(dev->backend->config_gpio_read(dev, &data));
+
+ *enabled = (data >> CFG_GPIO_PLL_EN) & 0x01;
+ });
+
+ return 0;
+}
+
+int bladerf_set_pll_enable(struct bladerf *dev, bool enable)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ WITH_MUTEX(&dev->lock, {
+ struct bladerf2_board_data *board_data = dev->board_data;
+ uint32_t data;
+
+ // Disable the trim DAC when we're using the PLL
+ if (enable) {
+ CHECK_STATUS_LOCKED(_bladerf2_set_trim_dac_enable(dev, false));
+ }
+
+ // Read current config GPIO value
+ CHECK_STATUS_LOCKED(dev->backend->config_gpio_read(dev, &data));
+
+ // Set the PLL enable bit accordingly
+ data &= ~(1 << CFG_GPIO_PLL_EN);
+ data |= ((enable ? 1 : 0) << CFG_GPIO_PLL_EN);
+
+ // Write back the config GPIO
+ CHECK_STATUS_LOCKED(dev->backend->config_gpio_write(dev, data));
+
+ // Update our state flag
+ board_data->trim_source = enable ? TRIM_SOURCE_PLL : TRIM_SOURCE_NONE;
+
+ // Enable the trim DAC if we're done with the
+ // PLL
+ if (!enable) {
+ CHECK_STATUS_LOCKED(_bladerf2_set_trim_dac_enable(dev, true));
+ }
+ });
+
+ return 0;
+}
+
+int bladerf_get_pll_refclk_range(struct bladerf *dev,
+ const struct bladerf_range **range)
+{
+ NULL_CHECK(range);
+
+ *range = &bladerf2_pll_refclk_range;
+
+ return 0;
+}
+
+int bladerf_get_pll_refclk(struct bladerf *dev, bladerf_frequency *frequency)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(frequency);
+
+ uint8_t const R_LATCH_REG = 0;
+ size_t const R_LATCH_SHIFT = 2;
+ uint32_t const R_LATCH_MASK = 0x3fff;
+ uint8_t const N_LATCH_REG = 1;
+ size_t const N_LATCH_SHIFT = 8;
+ uint32_t const N_LATCH_MASK = 0x1fff;
+ uint32_t reg;
+ uint16_t R, N;
+
+ // Get the current R value (latch 0, bits 2-15)
+ CHECK_STATUS(bladerf_get_pll_register(dev, R_LATCH_REG, &reg));
+ R = (reg >> R_LATCH_SHIFT) & R_LATCH_MASK;
+
+ // Get the current N value (latch 1, bits 8-20)
+ CHECK_STATUS(bladerf_get_pll_register(dev, N_LATCH_REG, &reg));
+ N = (reg >> N_LATCH_SHIFT) & N_LATCH_MASK;
+
+ // We assume the system clock frequency is
+ // BLADERF_VCTCXO_FREQUENCY. If it isn't, do your
+ // own math
+ *frequency = R * BLADERF_VCTCXO_FREQUENCY / N;
+
+ return 0;
+}
+
+int bladerf_set_pll_refclk(struct bladerf *dev, bladerf_frequency frequency)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ uint16_t R, N;
+
+ // We assume the system clock frequency is
+ // BLADERF_VCTCXO_FREQUENCY. If it isn't, do your
+ // own math
+ CHECK_STATUS(bladerf_pll_calculate_ratio(frequency,
+ BLADERF_VCTCXO_FREQUENCY, &R, &N));
+
+ CHECK_STATUS(bladerf_pll_configure(dev, R, N));
+
+ return 0;
+}
+
+int bladerf_get_pll_register(struct bladerf *dev,
+ uint8_t address,
+ uint32_t *val)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(val);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t data;
+
+ address &= 0x03;
+
+ CHECK_STATUS_LOCKED(dev->backend->adf400x_read(dev, address, &data));
+
+ *val = data;
+ });
+
+ return 0;
+}
+
+int bladerf_set_pll_register(struct bladerf *dev, uint8_t address, uint32_t val)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t data;
+
+ address &= 0x03;
+
+ data = val;
+
+ CHECK_STATUS_LOCKED(dev->backend->adf400x_write(dev, address, data));
+ });
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Low level Power Source Accessors */
+/******************************************************************************/
+
+int bladerf_get_power_source(struct bladerf *dev, bladerf_power_sources *src)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(src);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t data;
+
+ CHECK_STATUS_LOCKED(dev->backend->config_gpio_read(dev, &data));
+
+ if ((data >> CFG_GPIO_POWERSOURCE) & 0x01) {
+ *src = BLADERF_PS_USB_VBUS;
+ } else {
+ *src = BLADERF_PS_DC;
+ }
+ });
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Low level clock source selection accessors */
+/******************************************************************************/
+
+int bladerf_get_clock_select(struct bladerf *dev, bladerf_clock_select *sel)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(sel);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t gpio;
+
+ CHECK_STATUS_LOCKED(dev->backend->config_gpio_read(dev, &gpio));
+
+ if ((gpio & (1 << CFG_GPIO_CLOCK_SELECT)) == 0x0) {
+ *sel = CLOCK_SELECT_ONBOARD;
+ } else {
+ *sel = CLOCK_SELECT_EXTERNAL;
+ }
+ });
+
+ return 0;
+}
+
+int bladerf_set_clock_select(struct bladerf *dev, bladerf_clock_select sel)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ if (bladerf_device_speed(dev) == BLADERF_DEVICE_SPEED_HIGH) {
+ log_warning("USB 3.0 recommended for reliable clock select assignment.\n");
+ }
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t gpio;
+
+ CHECK_STATUS_LOCKED(dev->backend->config_gpio_read(dev, &gpio));
+
+ // Set the clock select bit(s) accordingly
+ switch (sel) {
+ case CLOCK_SELECT_ONBOARD:
+ gpio &= ~(1 << CFG_GPIO_CLOCK_SELECT);
+ break;
+ case CLOCK_SELECT_EXTERNAL:
+ gpio |= (1 << CFG_GPIO_CLOCK_SELECT);
+ break;
+ default:
+ break;
+ }
+
+ // Write back the config GPIO
+ CHECK_STATUS_LOCKED(dev->backend->config_gpio_write(dev, gpio));
+ });
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Low level clock buffer output accessors */
+/******************************************************************************/
+
+int bladerf_get_clock_output(struct bladerf *dev, bool *state)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(state);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t gpio;
+
+ CHECK_STATUS_LOCKED(dev->backend->config_gpio_read(dev, &gpio));
+
+ *state = ((gpio & (1 << CFG_GPIO_CLOCK_OUTPUT)) != 0x0);
+ });
+
+ return 0;
+}
+
+int bladerf_set_clock_output(struct bladerf *dev, bool enable)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+
+ WITH_MUTEX(&dev->lock, {
+ uint32_t gpio;
+
+ CHECK_STATUS_LOCKED(dev->backend->config_gpio_read(dev, &gpio));
+
+ // Set or clear the clock output enable bit
+ gpio &= ~(1 << CFG_GPIO_CLOCK_OUTPUT);
+ gpio |= ((enable ? 1 : 0) << CFG_GPIO_CLOCK_OUTPUT);
+
+ // Write back the config GPIO
+ CHECK_STATUS_LOCKED(dev->backend->config_gpio_write(dev, gpio));
+ });
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Low level INA219 Accessors */
+/******************************************************************************/
+
+int bladerf_get_pmic_register(struct bladerf *dev,
+ bladerf_pmic_register reg,
+ void *val)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_FPGA_LOADED);
+ NULL_CHECK(val);
+
+ int rv;
+
+ WITH_MUTEX(&dev->lock, {
+ switch (reg) {
+ case BLADERF_PMIC_CONFIGURATION:
+ case BLADERF_PMIC_CALIBRATION:
+ default:
+ rv = BLADERF_ERR_UNSUPPORTED;
+ break;
+
+ case BLADERF_PMIC_VOLTAGE_SHUNT:
+ rv = ina219_read_shunt_voltage(dev, (float *)val);
+ break;
+
+ case BLADERF_PMIC_VOLTAGE_BUS:
+ rv = ina219_read_bus_voltage(dev, (float *)val);
+ break;
+
+ case BLADERF_PMIC_POWER:
+ rv = ina219_read_power(dev, (float *)val);
+ break;
+
+ case BLADERF_PMIC_CURRENT:
+ rv = ina219_read_current(dev, (float *)val);
+ break;
+ }
+ });
+
+ return rv;
+}
+
+
+/******************************************************************************/
+/* Low level RF Switch Accessors */
+/******************************************************************************/
+
+int bladerf_get_rf_switch_config(struct bladerf *dev,
+ bladerf_rf_switch_config *config)
+{
+ CHECK_BOARD_IS_BLADERF2(dev);
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(config);
+
+ WITH_MUTEX(&dev->lock, {
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ struct controller_fns const *rfic = board_data->rfic;
+ uint32_t val;
+ uint32_t reg;
+
+ /* Get AD9361 status */
+ if (RFIC_COMMAND_HOST == rfic->command_mode) {
+ CHECK_AD936X_LOCKED(ad9361_get_tx_rf_port_output(phy, &val));
+ } else {
+ val = 0xFF;
+ }
+
+ config->tx1_rfic_port = val;
+ config->tx2_rfic_port = val;
+
+ if (RFIC_COMMAND_HOST == rfic->command_mode) {
+ CHECK_AD936X_LOCKED(ad9361_get_rx_rf_port_input(phy, &val));
+ } else {
+ val = 0xFF;
+ }
+
+ config->rx1_rfic_port = val;
+ config->rx2_rfic_port = val;
+
+ /* Read RFFE control register */
+ CHECK_STATUS_LOCKED(dev->backend->rffe_control_read(dev, &reg));
+
+ config->rx1_spdt_port =
+ (reg >> RFFE_CONTROL_RX_SPDT_1) & RFFE_CONTROL_SPDT_MASK;
+ config->rx2_spdt_port =
+ (reg >> RFFE_CONTROL_RX_SPDT_2) & RFFE_CONTROL_SPDT_MASK;
+ config->tx1_spdt_port =
+ (reg >> RFFE_CONTROL_TX_SPDT_1) & RFFE_CONTROL_SPDT_MASK;
+ config->tx2_spdt_port =
+ (reg >> RFFE_CONTROL_TX_SPDT_2) & RFFE_CONTROL_SPDT_MASK;
+ });
+
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/src/board/bladerf2/capabilities.c b/Radio/HW/BladeRF/src/board/bladerf2/capabilities.c
new file mode 100644
index 0000000..1373a0b
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf2/capabilities.c
@@ -0,0 +1,106 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2015 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <inttypes.h>
+
+#include "helpers/version.h"
+#include "log.h"
+
+#include "capabilities.h"
+
+uint64_t bladerf2_get_fw_capabilities(const struct bladerf_version *fw_version)
+{
+ uint64_t capabilities = 0;
+
+ if (version_fields_greater_or_equal(fw_version, 1, 7, 1)) {
+ capabilities |= BLADERF_CAP_FW_LOOPBACK;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 1, 8, 0)) {
+ capabilities |= BLADERF_CAP_QUERY_DEVICE_READY;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 1, 9, 0)) {
+ capabilities |= BLADERF_CAP_READ_FW_LOG_ENTRY;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 2, 1, 0)) {
+ capabilities |= BLADERF_CAP_FW_SUPPORTS_BLADERF2;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 2, 3, 0)) {
+ capabilities |= BLADERF_CAP_FW_FLASH_ID;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 2, 3, 1)) {
+ capabilities |= BLADERF_CAP_FW_FPGA_SOURCE;
+ }
+
+ if (version_fields_greater_or_equal(fw_version, 2, 4, 0)) {
+ capabilities |= BLADERF_CAP_FW_SHORT_PACKET;
+ }
+
+ return capabilities;
+}
+
+uint64_t bladerf2_get_fpga_capabilities(
+ const struct bladerf_version *fpga_version)
+{
+ uint64_t capabilities = 0;
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 1, 0)) {
+ capabilities |= BLADERF_CAP_TIMESTAMPS;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 3, 0)) {
+ capabilities |= BLADERF_CAP_PKT_HANDLER_FMT;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 3, 2)) {
+ capabilities |= BLADERF_CAP_VCTCXO_TRIMDAC_READ;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 4, 1)) {
+ capabilities |= BLADERF_CAP_MASKED_XBIO_WRITE;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 6, 0)) {
+ capabilities |= BLADERF_CAP_TRX_SYNC_TRIG;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 10, 0)) {
+ capabilities |= BLADERF_CAP_SCHEDULED_RETUNE;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 10, 1)) {
+ capabilities |= BLADERF_CAP_FPGA_TUNING;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 12, 0)) {
+ capabilities |= BLADERF_CAP_FPGA_PACKET_META;
+ }
+
+ if (version_fields_greater_or_equal(fpga_version, 0, 15, 0)) {
+ capabilities |= BLADERF_CAP_FPGA_8BIT_SAMPLES;
+ }
+
+ return capabilities;
+}
diff --git a/Radio/HW/BladeRF/src/board/bladerf2/capabilities.h b/Radio/HW/BladeRF/src/board/bladerf2/capabilities.h
new file mode 100644
index 0000000..43f3bde
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf2/capabilities.h
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2015-2017 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* This file defines device capabilities added across libbladeRF, FX3, and FPGA
+ * versions that we can check for */
+
+#ifndef BLADERF2_CAPABILITIES_H_
+#define BLADERF2_CAPABILITIES_H_
+
+#include <stdint.h>
+
+#include "board/board.h"
+#include "helpers/have_cap.h"
+
+/**
+ * Determine device's firmware capabilities.
+ *
+ * @param[in] fw_version Firmware version
+ *
+ * @return Capabilities bitmask
+ */
+uint64_t bladerf2_get_fw_capabilities(const struct bladerf_version *fw_version);
+
+/**
+ * Add capability bits based upon FPGA version stored in the device handle
+ *
+ * @param[in] fpga_version FPGA version
+ *
+ * @return Capabilities bitmask
+ */
+uint64_t bladerf2_get_fpga_capabilities(
+ const struct bladerf_version *fpga_version);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/board/bladerf2/common.c b/Radio/HW/BladeRF/src/board/bladerf2/common.c
new file mode 100644
index 0000000..15202f8
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf2/common.c
@@ -0,0 +1,415 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2018 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+
+#include "board/board.h"
+#include "capabilities.h"
+#include "common.h"
+
+
+/******************************************************************************/
+/* Constants */
+/******************************************************************************/
+
+/* Board state to string map */
+char const *bladerf2_state_to_string[] = {
+ [STATE_UNINITIALIZED] = "Uninitialized",
+ [STATE_FIRMWARE_LOADED] = "Firmware Loaded",
+ [STATE_FPGA_LOADED] = "FPGA Loaded",
+ [STATE_INITIALIZED] = "Initialized",
+};
+
+
+/******************************************************************************/
+/* Sample format control */
+/******************************************************************************/
+
+static inline int requires_timestamps(bladerf_format format, bool *required)
+{
+ switch (format) {
+ case BLADERF_FORMAT_SC8_Q7_META:
+ case BLADERF_FORMAT_SC16_Q11_META:
+ case BLADERF_FORMAT_PACKET_META:
+ *required = true;
+ break;
+
+ case BLADERF_FORMAT_SC8_Q7:
+ case BLADERF_FORMAT_SC16_Q11:
+ *required = false;
+ break;
+
+ default:
+ return BLADERF_ERR_INVAL;
+ }
+
+ return 0;
+}
+
+int perform_format_config(struct bladerf *dev,
+ bladerf_direction dir,
+ bladerf_format format)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+ bool use_timestamps, other_using_timestamps;
+ bladerf_channel other;
+ uint32_t gpio_val;
+ int status;
+
+ status = requires_timestamps(format, &use_timestamps);
+ if (status != 0) {
+ log_debug("%s: Invalid format: %d\n", __FUNCTION__, format);
+ return status;
+ }
+
+ switch (dir) {
+ case BLADERF_RX:
+ other = BLADERF_TX;
+ break;
+
+ case BLADERF_TX:
+ other = BLADERF_RX;
+ break;
+
+ default:
+ log_debug("Invalid direction: %d\n", dir);
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = requires_timestamps(board_data->module_format[other],
+ &other_using_timestamps);
+ if ((status == 0) && (other_using_timestamps != use_timestamps)) {
+ log_debug("Format conflict detected: RX=%d, TX=%d\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ CHECK_STATUS(dev->backend->config_gpio_read(dev, &gpio_val));
+
+ if (use_timestamps) {
+ gpio_val |= BLADERF_GPIO_TIMESTAMP;
+ } else {
+ gpio_val &= ~BLADERF_GPIO_TIMESTAMP;
+ }
+
+ if (format == BLADERF_FORMAT_PACKET_META) {
+ gpio_val |= BLADERF_GPIO_PACKET | BLADERF_GPIO_TIMESTAMP;
+ } else {
+ gpio_val &= ~BLADERF_GPIO_PACKET;
+ }
+
+ if (format == BLADERF_FORMAT_SC8_Q7 || format == BLADERF_FORMAT_SC8_Q7_META) {
+ gpio_val |= BLADERF_GPIO_8BIT_MODE;
+ } else {
+ gpio_val &= ~BLADERF_GPIO_8BIT_MODE;
+ }
+
+ CHECK_STATUS(dev->backend->config_gpio_write(dev, gpio_val));
+
+ board_data->module_format[dir] = format;
+
+ return 0;
+}
+
+int perform_format_deconfig(struct bladerf *dev, bladerf_direction dir)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ switch (dir) {
+ case BLADERF_RX:
+ case BLADERF_TX:
+ /* We'll reconfigure the HW when we call perform_format_config,
+ * so we just need to update our stored information */
+ board_data->module_format[dir] = -1;
+ break;
+
+ default:
+ log_debug("%s: Invalid direction: %d\n", __FUNCTION__, dir);
+ return BLADERF_ERR_INVAL;
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Size checks */
+/******************************************************************************/
+
+bool is_valid_fpga_size(struct bladerf *dev, bladerf_fpga_size fpga, size_t len)
+{
+ /* We do not build FPGAs with compression enabled. Therfore, they
+ * will always have a fixed file size.
+ */
+ char const env_override[] = "BLADERF_SKIP_FPGA_SIZE_CHECK";
+
+ bool valid;
+ size_t expected;
+ int status;
+
+ status = dev->board->get_fpga_bytes(dev, &expected);
+ if (status < 0) {
+ log_error(
+ "Error %d querying FPGA size.\n",
+ status);
+ return false;
+ }
+
+ /* Provide a means to override this check. This is intended to allow
+ * folks who know what they're doing to work around this quickly without
+ * needing to make a code change. (e.g., someone building a custom FPGA
+ * image that enables compressoin) */
+ if (getenv(env_override)) {
+ log_info("Overriding FPGA size check per %s\n", env_override);
+ valid = true;
+ } else if (expected > 0) {
+ valid = (len == expected);
+ } else {
+ log_debug("Unknown FPGA type (%d). Using relaxed size criteria.\n",
+ fpga);
+
+ if (len < (1 * 1024 * 1024)) {
+ valid = false;
+ } else if (len >
+ (dev->flash_arch->tsize_bytes - BLADERF_FLASH_ADDR_FPGA)) {
+ valid = false;
+ } else {
+ valid = true;
+ }
+ }
+
+ if (!valid) {
+ log_warning("Detected potentially incorrect FPGA file (length was %d, "
+ "expected %d).\n",
+ len, expected);
+
+ log_debug("If you are certain this file is valid, you may define\n"
+ "BLADERF_SKIP_FPGA_SIZE_CHECK in your environment to skip "
+ "this check.\n\n");
+ }
+
+ return valid;
+}
+
+bool is_valid_fw_size(size_t len)
+{
+ /* Simple FW applications generally are significantly larger than this
+ */
+ if (len < (50 * 1024)) {
+ return false;
+ } else if (len > BLADERF_FLASH_BYTE_LEN_FIRMWARE) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+bladerf_tuning_mode default_tuning_mode(struct bladerf *dev)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ bladerf_tuning_mode mode;
+ char const *env_var;
+ extern struct controller_fns const rfic_fpga_control;
+
+ mode = BLADERF_TUNING_MODE_HOST;
+
+ /* Detect TX FPGA bug and report warning */
+ if (BLADERF_TUNING_MODE_FPGA == mode && rfic_fpga_control.is_present(dev) &&
+ version_fields_less_than(&board_data->fpga_version, 0, 10, 2)) {
+ log_warning("FPGA v%u.%u.%u has errata related to FPGA-based tuning; "
+ "defaulting to host-based tuning. To use FPGA-based "
+ "tuning, update to FPGA v%u.%u.%u, or set the "
+ "BLADERF_DEFAULT_TUNING_MODE enviroment variable to "
+ "'fpga'.\n",
+ board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch, 0, 10, 2);
+ mode = BLADERF_TUNING_MODE_HOST;
+ }
+
+ env_var = getenv("BLADERF_DEFAULT_TUNING_MODE");
+
+ if (env_var != NULL) {
+ if (!strcasecmp("host", env_var)) {
+ mode = BLADERF_TUNING_MODE_HOST;
+ } else if (!strcasecmp("fpga", env_var)) {
+ /* Capability check */
+ if (!have_cap(board_data->capabilities, BLADERF_CAP_FPGA_TUNING)) {
+ log_error("The loaded FPGA version (%u.%u.%u) does not support "
+ "the tuning mode being used to override the default. "
+ "Ignoring BLADERF_DEFAULT_TUNING_MODE.\n",
+ board_data->fpga_version.major,
+ board_data->fpga_version.minor,
+ board_data->fpga_version.patch);
+ } else {
+ mode = BLADERF_TUNING_MODE_FPGA;
+ }
+ } else {
+ log_debug("Invalid tuning mode override: %s\n", env_var);
+ }
+ }
+
+ /* Check if the FPGA actually has RFIC control built in */
+ if (BLADERF_TUNING_MODE_FPGA == mode &&
+ !rfic_fpga_control.is_present(dev)) {
+ log_debug("FPGA does not have RFIC tuning capabilities, "
+ "defaulting to host-based control.\n");
+ mode = BLADERF_TUNING_MODE_HOST;
+ }
+
+ switch (mode) {
+ case BLADERF_TUNING_MODE_HOST:
+ log_debug("Default tuning mode: Host\n");
+ break;
+ case BLADERF_TUNING_MODE_FPGA:
+ log_debug("Default tuning mode: FPGA\n");
+ break;
+ default:
+ assert(!"Bug encountered.");
+ mode = BLADERF_TUNING_MODE_HOST;
+ }
+
+ return mode;
+}
+
+
+/******************************************************************************/
+/* Misc */
+/******************************************************************************/
+
+bool check_total_sample_rate(struct bladerf *dev)
+{
+ int status;
+ uint32_t reg;
+ size_t i;
+
+ bladerf_sample_rate rate_accum = 0;
+ size_t active_channels = 0;
+
+ const bladerf_sample_rate MAX_SAMPLE_THROUGHPUT = 80000000;
+
+ /* Read RFFE control register */
+ status = dev->backend->rffe_control_read(dev, &reg);
+ if (status < 0) {
+ return false;
+ }
+
+ /* Accumulate sample rates for all channels */
+ if (_rffe_dir_enabled(reg, BLADERF_RX)) {
+ bladerf_sample_rate rx_rate;
+
+ status =
+ dev->board->get_sample_rate(dev, BLADERF_CHANNEL_RX(0), &rx_rate);
+ if (status < 0) {
+ return false;
+ }
+
+ for (i = 0; i < dev->board->get_channel_count(dev, BLADERF_RX); ++i) {
+ if (_rffe_ch_enabled(reg, BLADERF_CHANNEL_RX(i))) {
+ rate_accum += rx_rate;
+ ++active_channels;
+ }
+ }
+ }
+
+ if (_rffe_dir_enabled(reg, BLADERF_TX)) {
+ bladerf_sample_rate tx_rate;
+
+ status =
+ dev->board->get_sample_rate(dev, BLADERF_CHANNEL_TX(0), &tx_rate);
+ if (status < 0) {
+ return false;
+ }
+
+ for (i = 0; i < dev->board->get_channel_count(dev, BLADERF_TX); ++i) {
+ if (_rffe_ch_enabled(reg, BLADERF_CHANNEL_TX(i))) {
+ rate_accum += tx_rate;
+ ++active_channels;
+ }
+ }
+ }
+
+ log_verbose("%s: active_channels=%d, rate_accum=%d, maximum=%d\n",
+ __FUNCTION__, active_channels, rate_accum,
+ MAX_SAMPLE_THROUGHPUT);
+
+ if (rate_accum > MAX_SAMPLE_THROUGHPUT) {
+ log_warning("The total sample throughput for the %d active channel%s, "
+ "%g Msps, is greater than the recommended maximum sample "
+ "throughput, %g Msps. You may experience dropped samples "
+ "unless the sample rate is reduced, or some channels are "
+ "deactivated.\n",
+ active_channels, (active_channels == 1 ? "" : "s"),
+ rate_accum / 1e6, MAX_SAMPLE_THROUGHPUT / 1e6);
+ return false;
+ }
+
+ return true;
+}
+
+bool does_rffe_dir_have_enabled_ch(uint32_t reg, bladerf_direction dir)
+{
+ switch (dir) {
+ case BLADERF_RX:
+ return _rffe_ch_enabled(reg, BLADERF_CHANNEL_RX(0)) ||
+ _rffe_ch_enabled(reg, BLADERF_CHANNEL_RX(1));
+ case BLADERF_TX:
+ return _rffe_ch_enabled(reg, BLADERF_CHANNEL_TX(0)) ||
+ _rffe_ch_enabled(reg, BLADERF_CHANNEL_TX(1));
+ }
+
+ return false;
+}
+
+int get_gain_offset(struct bladerf *dev, bladerf_channel ch, float *offset)
+{
+ CHECK_BOARD_STATE(STATE_INITIALIZED);
+ NULL_CHECK(offset);
+
+ struct bladerf_gain_range const *ranges = NULL;
+ bladerf_frequency frequency = 0;
+ size_t i, ranges_len;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ ranges = bladerf2_tx_gain_ranges;
+ ranges_len = ARRAY_SIZE(bladerf2_tx_gain_ranges);
+ } else {
+ ranges = bladerf2_rx_gain_ranges;
+ ranges_len = ARRAY_SIZE(bladerf2_rx_gain_ranges);
+ }
+
+ CHECK_STATUS(dev->board->get_frequency(dev, ch, &frequency));
+
+ for (i = 0; i < ranges_len; ++i) {
+ struct bladerf_gain_range const *r = &(ranges[i]);
+ struct bladerf_range const *rfreq = &(r->frequency);
+
+ // if the frequency range matches, and the range name is null,
+ // then we found our match
+ if (is_within_range(rfreq, frequency) && (NULL == r->name)) {
+ *offset = r->offset;
+ return 0;
+ }
+ }
+
+ return BLADERF_ERR_INVAL;
+}
diff --git a/Radio/HW/BladeRF/src/board/bladerf2/common.h b/Radio/HW/BladeRF/src/board/bladerf2/common.h
new file mode 100644
index 0000000..5a88cb4
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf2/common.h
@@ -0,0 +1,494 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2018 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef BLADERF2_COMMON_H_
+#define BLADERF2_COMMON_H_
+
+#if !defined(BLADERF_NIOS_BUILD) && !defined(BLADERF_NIOS_PC_SIMULATION)
+#include "log.h"
+#endif
+
+#include "bladerf2_common.h"
+#include "helpers/version.h"
+#include "streaming/sync.h"
+
+
+/******************************************************************************/
+/* Types */
+/******************************************************************************/
+
+enum bladerf2_vctcxo_trim_source {
+ TRIM_SOURCE_NONE,
+ TRIM_SOURCE_TRIM_DAC,
+ TRIM_SOURCE_PLL,
+ TRIM_SOURCE_AUXDAC
+};
+
+enum bladerf2_rfic_command_mode {
+ RFIC_COMMAND_HOST, /**< Host-based control */
+ RFIC_COMMAND_FPGA, /**< FPGA-based control */
+};
+
+struct controller_fns {
+ bool (*is_present)(struct bladerf *dev);
+ bool (*is_initialized)(struct bladerf *dev);
+ bool (*is_standby)(struct bladerf *dev);
+ int (*get_init_state)(struct bladerf *dev, bladerf_rfic_init_state *state);
+
+ int (*initialize)(struct bladerf *dev);
+ int (*standby)(struct bladerf *dev);
+ int (*deinitialize)(struct bladerf *dev);
+
+ int (*enable_module)(struct bladerf *dev, bladerf_channel ch, bool enable);
+
+ int (*get_sample_rate)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate *rate);
+ int (*set_sample_rate)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate rate);
+
+ int (*get_frequency)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency *frequency);
+ int (*set_frequency)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency);
+ int (*select_band)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency);
+
+ int (*get_bandwidth)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth *bandwidth);
+ int (*set_bandwidth)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth bandwidth,
+ bladerf_bandwidth *actual);
+
+ int (*get_gain_mode)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode *mode);
+ int (*set_gain_mode)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode mode);
+
+ int (*get_gain)(struct bladerf *dev, bladerf_channel ch, int *gain);
+ int (*set_gain)(struct bladerf *dev, bladerf_channel ch, int gain);
+
+ int (*get_gain_stage)(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ int *gain);
+ int (*set_gain_stage)(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ int gain);
+
+ int (*get_rssi)(struct bladerf *dev,
+ bladerf_channel ch,
+ int *pre_rssi,
+ int *sym_rssi);
+
+ int (*get_filter)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_rfic_rxfir *rxfir,
+ bladerf_rfic_txfir *txfir);
+ int (*set_filter)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_rfic_rxfir rxfir,
+ bladerf_rfic_txfir txfir);
+
+ int (*get_txmute)(struct bladerf *dev, bladerf_channel ch, bool *state);
+ int (*set_txmute)(struct bladerf *dev, bladerf_channel ch, bool state);
+
+ int (*store_fastlock_profile)(struct bladerf *dev,
+ bladerf_channel ch,
+ uint32_t profile);
+
+ int (*save_fastlock_profile)(struct bladerf *dev,
+ bladerf_channel ch,
+ uint32_t profile,
+ uint8_t *values);
+
+ enum bladerf2_rfic_command_mode const command_mode;
+};
+
+struct bladerf2_board_data {
+ /* Board state */
+ enum {
+ STATE_UNINITIALIZED,
+ STATE_FIRMWARE_LOADED,
+ STATE_FPGA_LOADED,
+ STATE_INITIALIZED,
+ } state;
+
+ /* AD9361 PHY Handle */
+ struct ad9361_rf_phy *phy;
+
+ /* RFIC configuration parameters */
+ void *rfic_init_params;
+
+ /* Bitmask of capabilities determined by version numbers */
+ uint64_t capabilities;
+
+ /* Format currently being used with a module, or -1 if module is not used */
+ bladerf_format module_format[NUM_MODULES];
+
+ /* Which mode of operation we use for tuning */
+ bladerf_tuning_mode tuning_mode;
+
+ /* Board properties */
+ bladerf_fpga_size fpga_size;
+ /* Data message size */
+ size_t msg_size;
+
+ /* Version information */
+ struct bladerf_version fpga_version;
+ struct bladerf_version fw_version;
+ char fpga_version_str[BLADERF_VERSION_STR_MAX + 1];
+ char fw_version_str[BLADERF_VERSION_STR_MAX + 1];
+
+ /* Synchronous interface handles */
+ struct bladerf_sync sync[2];
+
+ /* VCTCXO trim state */
+ enum bladerf2_vctcxo_trim_source trim_source;
+ uint16_t trimdac_last_value; /**< saved running value */
+ uint16_t trimdac_stored_value; /**< cached value read from SPI flash */
+
+ /* Quick Tune Profile Status */
+ uint16_t quick_tune_tx_profile;
+ uint16_t quick_tune_rx_profile;
+
+ /* RFIC backend command handling */
+ struct controller_fns const *rfic;
+
+ /* RFIC FIR Filter status */
+ bladerf_rfic_rxfir rxfir;
+ bladerf_rfic_txfir txfir;
+
+ /* If true, RFIC control will be fully de-initialized on close, instead of
+ * just put into a standby state. */
+ bool rfic_reset_on_close;
+};
+
+struct bladerf_rfic_status_register {
+ bool rfic_initialized;
+ size_t write_queue_length;
+};
+
+
+/******************************************************************************/
+/* Externs */
+/******************************************************************************/
+
+extern AD9361_InitParam bladerf2_rfic_init_params;
+extern AD9361_InitParam bladerf2_rfic_init_params_fastagc_burst;
+extern AD9361_RXFIRConfig bladerf2_rfic_rx_fir_config;
+extern AD9361_TXFIRConfig bladerf2_rfic_tx_fir_config;
+extern AD9361_RXFIRConfig bladerf2_rfic_rx_fir_config_dec2;
+extern AD9361_TXFIRConfig bladerf2_rfic_tx_fir_config_int2;
+extern AD9361_RXFIRConfig bladerf2_rfic_rx_fir_config_dec4;
+extern AD9361_TXFIRConfig bladerf2_rfic_tx_fir_config_int4;
+extern const float ina219_r_shunt;
+
+
+/******************************************************************************/
+/* Constants */
+/******************************************************************************/
+
+extern char const *bladerf2_state_to_string[4];
+
+
+/******************************************************************************/
+/* Macros */
+/******************************************************************************/
+
+/* Macro for logging and returning an error status. This should be used for
+ * errors defined in the \ref RETCODES list. */
+#define RETURN_ERROR_STATUS(_what, _status) \
+ do { \
+ log_error("%s: %s failed: %s\n", __FUNCTION__, _what, \
+ bladerf_strerror(_status)); \
+ return _status; \
+ } while (0)
+
+/* Macro for converting, logging, and returning libad9361 error codes. */
+#define RETURN_ERROR_AD9361(_what, _status) \
+ do { \
+ RETURN_ERROR_STATUS(_what, errno_ad9361_to_bladerf(_status)); \
+ } while (0)
+
+/* Macro for logging and returning ::BLADERF_ERR_INVAL */
+#define RETURN_INVAL_ARG(_what, _arg, _why) \
+ do { \
+ log_error("%s: %s '%s' invalid: %s\n", __FUNCTION__, _what, #_arg, \
+ _why); \
+ return BLADERF_ERR_INVAL; \
+ } while (0)
+
+#define RETURN_INVAL(_what, _why) \
+ do { \
+ log_error("%s: %s invalid: %s\n", __FUNCTION__, _what, _why); \
+ return BLADERF_ERR_INVAL; \
+ } while (0)
+
+/**
+ * @brief Null test
+ *
+ * @param _var The variable to check
+ *
+ * @return RETURN_INVAL if _var is null, continues otherwise
+ */
+#define NULL_CHECK(_var) \
+ do { \
+ if (NULL == _var) { \
+ RETURN_INVAL(#_var, "is null"); \
+ } \
+ } while (0)
+
+/**
+ * @brief Null test, with mutex unlock on failure
+ *
+ * @param _var The variable to check
+ *
+ * @return RETURN_INVAL if _var is null, continues otherwise
+ */
+#define NULL_CHECK_LOCKED(_var) \
+ do { \
+ NULL_CHECK(dev); \
+ \
+ if (NULL == _var) { \
+ MUTEX_UNLOCK(__lock); \
+ RETURN_INVAL(#_var, "is null"); \
+ } \
+ } while (0)
+
+/**
+ * @brief Board state check
+ *
+ * @param _state Minimum sufficient board state
+ *
+ * @return BLADERF_ERR_NOT_INIT if board's state is less than _state, continues
+ * otherwise
+ */
+#define CHECK_BOARD_STATE(_state) \
+ do { \
+ NULL_CHECK(dev); \
+ NULL_CHECK(dev->board); \
+ \
+ struct bladerf2_board_data *_bd = dev->board_data; \
+ \
+ if (_bd->state < _state) { \
+ log_error("%s: Board state insufficient for operation " \
+ "(current \"%s\", requires \"%s\").\n", \
+ __FUNCTION__, bladerf2_state_to_string[_bd->state], \
+ bladerf2_state_to_string[_state]); \
+ \
+ return BLADERF_ERR_NOT_INIT; \
+ } \
+ } while (0)
+
+/**
+ * @brief Test if board is a bladeRF 2
+ *
+ * @param _dev Device handle
+ *
+ * @return BLADERF_ERR_UNSUPPORTED if board is not a bladeRF 2, continues
+ * otherwise
+ */
+#define CHECK_BOARD_IS_BLADERF2(_dev) \
+ do { \
+ NULL_CHECK(_dev); \
+ NULL_CHECK(_dev->board); \
+ \
+ if (_dev->board != &bladerf2_board_fns) { \
+ log_error("%s: Board type \"%s\" not supported\n", __FUNCTION__, \
+ _dev->board->name); \
+ return BLADERF_ERR_UNSUPPORTED; \
+ } \
+ } while (0)
+
+/**
+ * @brief Call a function and return early if it fails
+ *
+ * @param _fn The function
+ *
+ * @return function return value if less than zero; continues otherwise
+ */
+#define CHECK_STATUS(_fn) \
+ do { \
+ int _s = _fn; \
+ if (_s < 0) { \
+ RETURN_ERROR_STATUS(#_fn, _s); \
+ } \
+ } while (0)
+
+/**
+ * @brief Call a function and goto error if it fails
+ *
+ * @param _fn The function
+ *
+ * @note `int status` must be declared in the including scope
+ */
+#define CHECK_STATUS_GOTO(_fn) \
+ do { \
+ status = _fn; \
+ if (status < 0) { \
+ log_error("%s: %i failed: %s\n", #_fn, status, \
+ bladerf_strerror(status)); \
+ goto error; \
+ } \
+ } while (0)
+
+/**
+ * @brief Call a function and, if it fails, unlock the mutex and return
+ *
+ * @param _fn The function
+ *
+ * @return function return value if less than zero; continues otherwise
+ */
+#define CHECK_STATUS_LOCKED(_fn) \
+ do { \
+ int _s = _fn; \
+ if (_s < 0) { \
+ MUTEX_UNLOCK(__lock); \
+ RETURN_ERROR_STATUS(#_fn, _s); \
+ } \
+ } while (0)
+
+/**
+ * @brief Call a function and return early if it fails, with error translation
+ * from AD936x to bladeRF return codes
+ *
+ * @param _fn The function
+ *
+ * @return function return value if less than zero; continues otherwise
+ */
+#define CHECK_AD936X(_fn) \
+ do { \
+ int _s = _fn; \
+ if (_s < 0) { \
+ RETURN_ERROR_AD9361(#_fn, _s); \
+ } \
+ } while (0)
+
+/**
+ * @brief Call a function and, if it fails, unlock the mutex and return, with
+ * error translation from AD936x to bladeRF return codes
+ *
+ * @param _fn The function
+ *
+ * @return function return value if less than zero; continues otherwise
+ */
+#define CHECK_AD936X_LOCKED(_fn) \
+ do { \
+ int _s = _fn; \
+ if (_s < 0) { \
+ MUTEX_UNLOCK(__lock); \
+ RETURN_ERROR_AD9361(#_fn, _s); \
+ } \
+ } while (0)
+
+/**
+ * @brief Execute a command block with a mutex lock
+ *
+ * @note Variables declared within the including scope must be declared
+ * one-per-line.
+ *
+ * @param _lock Lock to hold
+ * @param _thing Block to execute
+ */
+#define WITH_MUTEX(_lock, _thing) \
+ do { \
+ MUTEX *__lock = _lock; \
+ \
+ MUTEX_LOCK(__lock); \
+ _thing; \
+ MUTEX_UNLOCK(__lock); \
+ } while (0)
+
+/**
+ * @brief Execute command block, conditional to _mode
+ *
+ * @param _dev Device handle
+ * @param _mode Command mode
+ * @param _thing Block to do if it happens
+ */
+#define IF_COMMAND_MODE(_dev, _mode, _thing) \
+ do { \
+ NULL_CHECK(_dev); \
+ NULL_CHECK(_dev->board_data); \
+ \
+ struct bladerf2_board_data *bd = _dev->board_data; \
+ \
+ if (bd->rfic->command_mode == _mode) { \
+ _thing; \
+ }; \
+ } while (0)
+
+
+/******************************************************************************/
+/* Functions */
+/******************************************************************************/
+
+/**
+ * Perform the neccessary device configuration for the specified format
+ * (e.g., enabling/disabling timestamp support), first checking that the
+ * requested format would not conflict with the other stream direction.
+ *
+ * @param dev Device handle
+ * @param[in] dir Direction that is currently being configured
+ * @param[in] format Format the channel is being configured for
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int perform_format_config(struct bladerf *dev,
+ bladerf_direction dir,
+ bladerf_format format);
+
+/**
+ * Deconfigure and update any state pertaining what a format that a stream
+ * direction is no longer using.
+ *
+ * @param dev Device handle
+ * @param[in] dir Direction that is currently being deconfigured
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int perform_format_deconfig(struct bladerf *dev, bladerf_direction dir);
+
+bool is_valid_fpga_size(struct bladerf *dev,
+ bladerf_fpga_size fpga,
+ size_t len);
+
+bool is_valid_fw_size(size_t len);
+
+bladerf_tuning_mode default_tuning_mode(struct bladerf *dev);
+
+bool check_total_sample_rate(struct bladerf *dev);
+
+bool does_rffe_dir_have_enabled_ch(uint32_t reg, bladerf_direction dir);
+
+int get_gain_offset(struct bladerf *dev, bladerf_channel ch, float *offset);
+
+#endif // BLADERF2_COMMON_H_
diff --git a/Radio/HW/BladeRF/src/board/bladerf2/compatibility.c b/Radio/HW/BladeRF/src/board/bladerf2/compatibility.c
new file mode 100644
index 0000000..7ee0b8e
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf2/compatibility.c
@@ -0,0 +1,50 @@
+#include "host_config.h"
+
+#include "helpers/version.h"
+
+/* Firmware-FPGA compatibility tables
+ *
+ * This list should be kept in decending order, such that the most recent
+ * versions are first, and the last entry should contain the earliest version
+ * that libbladeRF supports.
+ */
+
+#define VERSION(major, minor, patch) { major, minor, patch, NULL }
+
+static const struct compat fw_compat[] = {
+ /* Firmware requires >= FPGA */
+ { VERSION(2, 4, 0), VERSION(0, 6, 0) },
+ { VERSION(2, 3, 2), VERSION(0, 6, 0) },
+ { VERSION(2, 3, 1), VERSION(0, 6, 0) },
+ { VERSION(2, 3, 0), VERSION(0, 6, 0) },
+ { VERSION(2, 2, 0), VERSION(0, 6, 0) },
+ { VERSION(2, 1, 1), VERSION(0, 6, 0) },
+ { VERSION(2, 1, 0), VERSION(0, 6, 0) },
+ { VERSION(2, 0, 0), VERSION(0, 6, 0) },
+};
+
+const struct version_compat_table bladerf2_fw_compat_table = {fw_compat, ARRAY_SIZE(fw_compat)};
+
+static const struct compat fpga_compat[] = {
+ /* FPGA requires >= Firmware */
+ { VERSION(0, 15, 3), VERSION(2, 4, 0) },
+ { VERSION(0, 15, 2), VERSION(2, 4, 0) },
+ { VERSION(0, 15, 1), VERSION(2, 4, 0) },
+ { VERSION(0, 15, 0), VERSION(2, 4, 0) },
+ { VERSION(0, 14, 0), VERSION(2, 4, 0) },
+ { VERSION(0, 12, 0), VERSION(2, 2, 0) },
+ { VERSION(0, 11, 1), VERSION(2, 1, 0) },
+ { VERSION(0, 11, 0), VERSION(2, 1, 0) },
+ { VERSION(0, 10, 2), VERSION(2, 1, 0) },
+ { VERSION(0, 10, 1), VERSION(2, 1, 0) },
+ { VERSION(0, 10, 0), VERSION(2, 1, 0) },
+ { VERSION(0, 9, 0), VERSION(2, 1, 0) },
+ { VERSION(0, 8, 0), VERSION(2, 1, 0) },
+ { VERSION(0, 7, 3), VERSION(2, 1, 0) },
+ { VERSION(0, 7, 2), VERSION(2, 1, 0) },
+ { VERSION(0, 7, 1), VERSION(2, 0, 0) },
+ { VERSION(0, 7, 0), VERSION(2, 0, 0) },
+ { VERSION(0, 6, 0), VERSION(2, 0, 0) },
+};
+
+const struct version_compat_table bladerf2_fpga_compat_table = {fpga_compat, ARRAY_SIZE(fpga_compat)};
diff --git a/Radio/HW/BladeRF/src/board/bladerf2/compatibility.h b/Radio/HW/BladeRF/src/board/bladerf2/compatibility.h
new file mode 100644
index 0000000..67447b9
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf2/compatibility.h
@@ -0,0 +1,9 @@
+#ifndef BLADERF2_COMPATIBILITY_H_
+#define BLADERF2_COMPATIBILITY_H_
+
+#include "helpers/version.h"
+
+extern const struct version_compat_table bladerf2_fw_compat_table;
+extern const struct version_compat_table bladerf2_fpga_compat_table;
+
+#endif
diff --git a/Radio/HW/BladeRF/src/board/bladerf2/rfic_fpga.c b/Radio/HW/BladeRF/src/board/bladerf2/rfic_fpga.c
new file mode 100644
index 0000000..089a8cb
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf2/rfic_fpga.c
@@ -0,0 +1,767 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2018 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <libbladeRF.h>
+
+#include "board/board.h"
+#include "common.h"
+#include "conversions.h"
+#include "iterators.h"
+#include "log.h"
+
+// #define BLADERF_HEADLESS_C_DEBUG
+
+
+/******************************************************************************/
+/* Declarations */
+/******************************************************************************/
+
+/**
+ * @brief RFIC Command Read
+ *
+ * Executes a command using the FPGA-based RFIC interface, returning the reply
+ * value in *data.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel to act upon; use ::BLADERF_CHANNEL_INVALID for
+ * non-channel-specific commands
+ * @param[in] cmd Command
+ * @param[out] data Pointer for response data
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+static int _rfic_cmd_read(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_rfic_command cmd,
+ uint64_t *data);
+
+/**
+ * @brief RFIC Command Write
+ *
+ * Executes a command using the FPGA-based RFIC interface, using the supplied
+ * data value as an argument. Waits until the command has been executed before
+ * returning.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel to act upon; use ::BLADERF_CHANNEL_INVALID for
+ * non-channel-specific commands
+ * @param[in] cmd Command
+ * @param[in] data Argument for command
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+static int _rfic_cmd_write(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_rfic_command cmd,
+ uint64_t data);
+
+
+/******************************************************************************/
+/* Helpers */
+/******************************************************************************/
+
+/* Build RFIC address from bladerf_rfic_command and bladerf_channel */
+#define RFIC_ADDRESS(cmd, ch) ((cmd & 0xFF) + ((ch & 0xF) << 8))
+
+static int _rfic_fpga_get_status(
+ struct bladerf *dev, struct bladerf_rfic_status_register *rfic_status)
+{
+ uint64_t sreg = 0;
+ int status;
+
+ status = _rfic_cmd_read(dev, BLADERF_CHANNEL_INVALID,
+ BLADERF_RFIC_COMMAND_STATUS, &sreg);
+
+ rfic_status->rfic_initialized = ((sreg >> 0) & 0x1);
+ rfic_status->write_queue_length = ((sreg >> 8) & 0xFF);
+
+ return status;
+}
+
+static int _rfic_fpga_get_status_wqlen(struct bladerf *dev)
+{
+ struct bladerf_rfic_status_register rfic_status;
+ int status;
+
+ status = _rfic_fpga_get_status(dev, &rfic_status);
+ if (status < 0) {
+ return status;
+ }
+
+#ifdef BLADERF_HEADLESS_C_DEBUG
+ if (rfic_status.write_queue_length > 0) {
+ log_verbose("%s: queue len = %d\n", __FUNCTION__,
+ rfic_status.write_queue_length);
+ }
+#endif
+
+ return (int)rfic_status.write_queue_length;
+}
+
+static int _rfic_fpga_spinwait(struct bladerf *dev)
+{
+ size_t const TRIES = 30;
+ unsigned int const DELAY = 100;
+ size_t count = 0;
+ int jobs;
+
+ /* Poll the CPU and spin until the job has been completed. */
+ do {
+ jobs = _rfic_fpga_get_status_wqlen(dev);
+ if (0 != jobs) {
+ usleep(DELAY);
+ }
+ } while ((0 != jobs) && (++count < TRIES));
+
+ /* If it's simply taking too long to dequeue the command, status will
+ * have the number of items in the queue. Bonk this down to a timeout. */
+ if (jobs > 0) {
+ jobs = BLADERF_ERR_TIMEOUT;
+ }
+
+ return jobs;
+}
+
+
+/******************************************************************************/
+/* Low level RFIC Accessors */
+/******************************************************************************/
+
+static int _rfic_cmd_read(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_rfic_command cmd,
+ uint64_t *data)
+{
+ return dev->backend->rfic_command_read(dev, RFIC_ADDRESS(cmd, ch), data);
+}
+
+static int _rfic_cmd_write(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_rfic_command cmd,
+ uint64_t data)
+{
+ /* Perform the write command. */
+ CHECK_STATUS(
+ dev->backend->rfic_command_write(dev, RFIC_ADDRESS(cmd, ch), data));
+
+ /* Block until the job has been completed. */
+ return _rfic_fpga_spinwait(dev);
+}
+
+
+/******************************************************************************/
+/* Initialization */
+/******************************************************************************/
+
+static bool _rfic_fpga_is_present(struct bladerf *dev)
+{
+ uint64_t sreg = 0;
+ int status;
+
+ status = _rfic_cmd_read(dev, BLADERF_CHANNEL_INVALID,
+ BLADERF_RFIC_COMMAND_STATUS, &sreg);
+ if (status < 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static int _rfic_fpga_get_init_state(struct bladerf *dev,
+ bladerf_rfic_init_state *state)
+{
+ uint64_t data;
+
+ CHECK_STATUS(_rfic_cmd_read(dev, BLADERF_CHANNEL_INVALID,
+ BLADERF_RFIC_COMMAND_INIT, &data));
+
+ *state = (bladerf_rfic_init_state)data;
+
+ return 0;
+}
+
+static bool _rfic_fpga_is_initialized(struct bladerf *dev)
+{
+ bladerf_rfic_init_state state;
+ int status;
+
+ status = _rfic_fpga_get_init_state(dev, &state);
+ if (status < 0) {
+ log_error("%s: failed to get RFIC initialization state: %s\n",
+ __FUNCTION__, bladerf_strerror(status));
+ return false;
+ }
+
+ return BLADERF_RFIC_INIT_STATE_ON == state;
+}
+
+static bool _rfic_fpga_is_standby(struct bladerf *dev)
+{
+ bladerf_rfic_init_state state;
+ int status;
+
+ status = _rfic_fpga_get_init_state(dev, &state);
+ if (status < 0) {
+ log_error("%s: failed to get RFIC initialization state: %s\n",
+ __FUNCTION__, bladerf_strerror(status));
+ return false;
+ }
+
+ return BLADERF_RFIC_INIT_STATE_STANDBY == state;
+}
+
+static int _rfic_fpga_initialize(struct bladerf *dev)
+{
+ log_debug("%s: initializing\n", __FUNCTION__);
+
+ return _rfic_cmd_write(dev, BLADERF_CHANNEL_INVALID,
+ BLADERF_RFIC_COMMAND_INIT,
+ BLADERF_RFIC_INIT_STATE_ON);
+}
+
+static int _rfic_fpga_standby(struct bladerf *dev)
+{
+ log_debug("%s: entering standby mode\n", __FUNCTION__);
+
+ return _rfic_cmd_write(dev, BLADERF_CHANNEL_INVALID,
+ BLADERF_RFIC_COMMAND_INIT,
+ BLADERF_RFIC_INIT_STATE_STANDBY);
+}
+
+static int _rfic_fpga_deinitialize(struct bladerf *dev)
+{
+ log_debug("%s: deinitializing\n", __FUNCTION__);
+
+ return _rfic_cmd_write(dev, BLADERF_CHANNEL_INVALID,
+ BLADERF_RFIC_COMMAND_INIT,
+ BLADERF_RFIC_INIT_STATE_OFF);
+}
+
+
+/******************************************************************************/
+/* Enable */
+/******************************************************************************/
+
+static int _rfic_fpga_enable_module(struct bladerf *dev,
+ bladerf_channel ch,
+ bool ch_enable)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ bladerf_direction dir = BLADERF_CHANNEL_IS_TX(ch) ? BLADERF_TX : BLADERF_RX;
+ uint32_t reg; /* RFFE register value */
+ bool ch_enabled; /* Channel: initial state */
+ bool ch_pending; /* Channel: target state is not initial state */
+ bool dir_enabled; /* Direction: initial state */
+ bool dir_enable; /* Direction: target state */
+ bool dir_pending; /* Direction: target state is not initial state */
+ bool be_toggle; /* Backend: toggle off/on to reset backend FIFO */
+ bool be_teardown; /* Backend: disable backend module */
+ bool be_setup; /* Backend: enable backend module */
+
+ /* Read RFFE control register */
+ CHECK_STATUS(dev->backend->rffe_control_read(dev, &reg));
+
+#ifdef BLADERF_HEADLESS_C_DEBUG
+ uint32_t reg_old = reg;
+#endif
+
+ /* Calculate initial and target states */
+ ch_enabled = _rffe_ch_enabled(reg, ch);
+ dir_enabled = _rffe_dir_enabled(reg, dir);
+ dir_enable = ch_enable || _rffe_dir_otherwise_enabled(reg, ch);
+ ch_pending = ch_enabled != ch_enable;
+ dir_pending = dir_enabled != dir_enable;
+ be_toggle = !BLADERF_CHANNEL_IS_TX(ch) && ch_enable && !dir_pending;
+ be_setup = be_toggle || (dir_pending && dir_enable);
+ be_teardown = be_toggle || (dir_pending && !dir_enable);
+
+ /* Perform Direction Teardown */
+ if (dir_pending && !dir_enable) {
+ sync_deinit(&board_data->sync[dir]);
+ perform_format_deconfig(dev, dir);
+ }
+
+ /* Perform Channel Setup/Teardown */
+ if (ch_pending) {
+ /* Set/unset TX mute */
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ CHECK_STATUS(rfic->set_txmute(dev, ch, !ch_enable));
+ }
+
+ /* Execute RFIC enable command. */
+ CHECK_STATUS(_rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_ENABLE,
+ ch_enable ? 1 : 0));
+ }
+
+ /* Perform backend teardown */
+ if (be_teardown) {
+ CHECK_STATUS(dev->backend->enable_module(dev, dir, false));
+ }
+
+ /* Perform backend setup */
+ if (be_setup) {
+ CHECK_STATUS(dev->backend->enable_module(dev, dir, true));
+ }
+
+ /* Warn the user if the sample rate isn't reasonable */
+ if (ch_pending && ch_enable) {
+ check_total_sample_rate(dev);
+ }
+
+#ifdef BLADERF_HEADLESS_C_DEBUG
+ /* Debug output... */
+ if (BLADERF_LOG_LEVEL_VERBOSE == log_get_verbosity()) {
+ CHECK_STATUS(dev->backend->rffe_control_read(dev, &reg));
+
+ log_verbose(
+ "%s: %s ch[en=%d->%d pend=%d] dir[en=%d->%d pend=%d] be[clr=%d "
+ "su=%d td=%d] reg=0x%08x->0x%08x\n",
+ __FUNCTION__, channel2str(ch), ch_enabled, ch_enable, ch_pending,
+ dir_enabled, dir_enable, dir_pending, be_toggle, be_setup,
+ be_teardown, reg_old, reg);
+ }
+#endif
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Sample rate */
+/******************************************************************************/
+
+static int _rfic_fpga_get_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate *rate)
+{
+ uint64_t readval;
+
+ CHECK_STATUS(
+ _rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_SAMPLERATE, &readval));
+
+ *rate = (uint32_t)readval;
+
+ return 0;
+}
+
+static int _rfic_fpga_set_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate rate)
+{
+ return _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_SAMPLERATE, rate);
+}
+
+
+/******************************************************************************/
+/* Frequency */
+/******************************************************************************/
+
+static int _rfic_fpga_get_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency *frequency)
+{
+ bladerf_frequency freq;
+
+ CHECK_STATUS(
+ _rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_FREQUENCY, &freq));
+
+ *frequency = freq;
+
+ return 0;
+}
+
+static int _rfic_fpga_set_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency)
+{
+ struct bladerf_range const *range = NULL;
+
+ CHECK_STATUS(dev->board->get_frequency_range(dev, ch, &range));
+
+ if (!is_within_range(range, frequency)) {
+ return BLADERF_ERR_RANGE;
+ }
+
+ return _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_FREQUENCY, frequency);
+}
+
+static int _rfic_fpga_select_band(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency)
+{
+ // This functionality is unnecessary with the headless architecture.
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Bandwidth */
+/******************************************************************************/
+
+static int _rfic_fpga_get_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth *bandwidth)
+{
+ uint64_t readval;
+
+ CHECK_STATUS(
+ _rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_BANDWIDTH, &readval));
+
+ *bandwidth = (uint32_t)readval;
+
+ return 0;
+}
+
+static int _rfic_fpga_set_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth bandwidth,
+ bladerf_bandwidth *actual)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ struct bladerf_range const *range = NULL;
+
+ CHECK_STATUS(dev->board->get_bandwidth_range(dev, ch, &range));
+
+ if (!is_within_range(range, bandwidth)) {
+ return BLADERF_ERR_RANGE;
+ }
+
+ CHECK_STATUS(
+ _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_BANDWIDTH, bandwidth));
+
+ if (actual != NULL) {
+ return rfic->get_bandwidth(dev, ch, actual);
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Gain */
+/* These functions handle offset */
+/******************************************************************************/
+
+static int _rfic_fpga_get_gain(struct bladerf *dev,
+ bladerf_channel ch,
+ int *gain)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ char const *stage = BLADERF_CHANNEL_IS_TX(ch) ? "dsa" : "full";
+ int val;
+ float offset;
+
+ CHECK_STATUS(get_gain_offset(dev, ch, &offset));
+
+ CHECK_STATUS(rfic->get_gain_stage(dev, ch, stage, &val));
+
+ *gain = __round_int(val + offset);
+
+ return 0;
+}
+
+static int _rfic_fpga_set_gain(struct bladerf *dev,
+ bladerf_channel ch,
+ int gain)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct controller_fns const *rfic = board_data->rfic;
+ char const *stage = BLADERF_CHANNEL_IS_TX(ch) ? "dsa" : "full";
+ float offset;
+
+ CHECK_STATUS(get_gain_offset(dev, ch, &offset));
+
+ return rfic->set_gain_stage(dev, ch, stage, __round_int(gain - offset));
+}
+
+
+/******************************************************************************/
+/* Gain mode */
+/******************************************************************************/
+
+static int _rfic_fpga_get_gain_mode(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode *mode)
+{
+ uint64_t readval;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ log_warning("%s: automatic gain control not valid for TX channels\n",
+ __FUNCTION__);
+ *mode = BLADERF_GAIN_DEFAULT;
+ return 0;
+ }
+
+ CHECK_STATUS(
+ _rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_GAINMODE, &readval));
+
+ *mode = (bladerf_gain_mode)readval;
+
+ return 0;
+}
+
+static int _rfic_fpga_set_gain_mode(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode mode)
+{
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ log_warning("%s: automatic gain control not valid for TX channels\n",
+ __FUNCTION__);
+ return 0;
+ }
+
+ return _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_GAINMODE, mode);
+}
+
+
+/******************************************************************************/
+/* Gain stage */
+/* These functions handle scaling */
+/******************************************************************************/
+
+static int _rfic_fpga_get_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ int *gain)
+{
+ struct bladerf_range const *range = NULL;
+ uint64_t val;
+
+ if ((BLADERF_CHANNEL_IS_TX(ch) && strcmp(stage, "dsa") != 0) ||
+ (!BLADERF_CHANNEL_IS_TX(ch) && strcmp(stage, "full") != 0)) {
+ log_error("%s: unknown gain stage '%s'\n", __FUNCTION__, stage);
+ return BLADERF_ERR_INVAL;
+ }
+
+ CHECK_STATUS(dev->board->get_gain_stage_range(dev, ch, stage, &range));
+
+ CHECK_STATUS(_rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_GAIN, &val));
+
+ *gain = __unscale_int(range, val);
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ *gain = -(*gain);
+ }
+
+ return 0;
+}
+
+static int _rfic_fpga_set_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ int gain)
+{
+ struct bladerf_range const *range = NULL;
+ int64_t val;
+
+ if ((BLADERF_CHANNEL_IS_TX(ch) && strcmp(stage, "dsa") != 0) ||
+ (!BLADERF_CHANNEL_IS_TX(ch) && strcmp(stage, "full") != 0)) {
+ log_error("%s: unknown gain stage '%s'\n", __FUNCTION__, stage);
+ return BLADERF_ERR_INVAL;
+ }
+
+ CHECK_STATUS(dev->board->get_gain_stage_range(dev, ch, stage, &range));
+
+ if (BLADERF_CHANNEL_IS_TX(ch) && gain < -89) {
+ val = -89750;
+ } else {
+ val = __scale_int64(range, clamp_to_range(range, gain));
+ }
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ val = -val;
+ }
+
+ return _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_GAIN, (uint64_t)val);
+}
+
+
+/******************************************************************************/
+/* RSSI */
+/******************************************************************************/
+
+static int _rfic_fpga_get_rssi(struct bladerf *dev,
+ bladerf_channel ch,
+ int *pre_rssi,
+ int *sym_rssi)
+{
+ uint64_t readval;
+ int16_t mult, pre, sym;
+
+ CHECK_STATUS(_rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_RSSI, &readval));
+
+ mult = (int16_t)((readval >> BLADERF_RFIC_RSSI_MULT_SHIFT) &
+ BLADERF_RFIC_RSSI_MULT_MASK);
+ pre = (int16_t)((readval >> BLADERF_RFIC_RSSI_PRE_SHIFT) &
+ BLADERF_RFIC_RSSI_PRE_MASK);
+ sym = (int16_t)((readval >> BLADERF_RFIC_RSSI_SYM_SHIFT) &
+ BLADERF_RFIC_RSSI_SYM_MASK);
+
+ *pre_rssi = __round_int(pre / (float)mult);
+ *sym_rssi = __round_int(sym / (float)mult);
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Filter */
+/******************************************************************************/
+
+static int _rfic_fpga_get_filter(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_rfic_rxfir *rxfir,
+ bladerf_rfic_txfir *txfir)
+{
+ uint64_t readval;
+
+ CHECK_STATUS(
+ _rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_FILTER, &readval));
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ if (NULL != txfir) {
+ *txfir = (bladerf_rfic_txfir)readval;
+ } else {
+ return BLADERF_ERR_INVAL;
+ }
+ } else {
+ if (NULL != rxfir) {
+ *rxfir = (bladerf_rfic_rxfir)readval;
+ } else {
+ return BLADERF_ERR_INVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int _rfic_fpga_set_filter(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_rfic_rxfir rxfir,
+ bladerf_rfic_txfir txfir)
+{
+ uint64_t data;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ data = (uint64_t)txfir;
+ } else {
+ data = (uint64_t)rxfir;
+ }
+
+ return _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_FILTER, data);
+}
+
+
+/******************************************************************************/
+/* TX Mute */
+/******************************************************************************/
+
+static int _rfic_fpga_get_txmute(struct bladerf *dev,
+ bladerf_channel ch,
+ bool *state)
+{
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ uint64_t readval;
+
+ CHECK_STATUS(
+ _rfic_cmd_read(dev, ch, BLADERF_RFIC_COMMAND_TXMUTE, &readval));
+
+ return (readval > 0);
+ }
+
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int _rfic_fpga_set_txmute(struct bladerf *dev,
+ bladerf_channel ch,
+ bool state)
+{
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ return _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_TXMUTE,
+ state ? 1 : 0);
+ }
+
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+
+/******************************************************************************/
+/* Fastlock */
+/******************************************************************************/
+
+static int _rfic_fpga_store_fastlock_profile(struct bladerf *dev,
+ bladerf_channel ch,
+ uint32_t profile)
+{
+ return _rfic_cmd_write(dev, ch, BLADERF_RFIC_COMMAND_FASTLOCK, profile);
+}
+
+
+/******************************************************************************/
+/* Function pointers */
+/******************************************************************************/
+
+struct controller_fns const rfic_fpga_control = {
+ FIELD_INIT(.is_present, _rfic_fpga_is_present),
+ FIELD_INIT(.is_standby, _rfic_fpga_is_standby),
+ FIELD_INIT(.is_initialized, _rfic_fpga_is_initialized),
+ FIELD_INIT(.get_init_state, _rfic_fpga_get_init_state),
+
+ FIELD_INIT(.initialize, _rfic_fpga_initialize),
+ FIELD_INIT(.standby, _rfic_fpga_standby),
+ FIELD_INIT(.deinitialize, _rfic_fpga_deinitialize),
+
+ FIELD_INIT(.enable_module, _rfic_fpga_enable_module),
+
+ FIELD_INIT(.get_sample_rate, _rfic_fpga_get_sample_rate),
+ FIELD_INIT(.set_sample_rate, _rfic_fpga_set_sample_rate),
+
+ FIELD_INIT(.get_frequency, _rfic_fpga_get_frequency),
+ FIELD_INIT(.set_frequency, _rfic_fpga_set_frequency),
+ FIELD_INIT(.select_band, _rfic_fpga_select_band),
+
+ FIELD_INIT(.get_bandwidth, _rfic_fpga_get_bandwidth),
+ FIELD_INIT(.set_bandwidth, _rfic_fpga_set_bandwidth),
+
+ FIELD_INIT(.get_gain_mode, _rfic_fpga_get_gain_mode),
+ FIELD_INIT(.set_gain_mode, _rfic_fpga_set_gain_mode),
+
+ FIELD_INIT(.get_gain, _rfic_fpga_get_gain),
+ FIELD_INIT(.set_gain, _rfic_fpga_set_gain),
+
+ FIELD_INIT(.get_gain_stage, _rfic_fpga_get_gain_stage),
+ FIELD_INIT(.set_gain_stage, _rfic_fpga_set_gain_stage),
+
+ FIELD_INIT(.get_rssi, _rfic_fpga_get_rssi),
+
+ FIELD_INIT(.get_filter, _rfic_fpga_get_filter),
+ FIELD_INIT(.set_filter, _rfic_fpga_set_filter),
+
+ FIELD_INIT(.get_txmute, _rfic_fpga_get_txmute),
+ FIELD_INIT(.set_txmute, _rfic_fpga_set_txmute),
+
+ FIELD_INIT(.store_fastlock_profile, _rfic_fpga_store_fastlock_profile),
+
+ FIELD_INIT(.command_mode, RFIC_COMMAND_FPGA),
+};
diff --git a/Radio/HW/BladeRF/src/board/bladerf2/rfic_host.c b/Radio/HW/BladeRF/src/board/bladerf2/rfic_host.c
new file mode 100644
index 0000000..7cccbe6
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf2/rfic_host.c
@@ -0,0 +1,1058 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2018 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+
+#include <libbladeRF.h>
+
+#include "ad936x_helpers.h"
+#include "bladerf2_common.h"
+#include "board/board.h"
+#include "common.h"
+#include "helpers/wallclock.h"
+#include "iterators.h"
+#include "log.h"
+
+// #define BLADERF_HOSTED_C_DEBUG
+
+
+/******************************************************************************/
+/* Initialization */
+/******************************************************************************/
+
+static bool _rfic_host_is_present(struct bladerf *dev)
+{
+ return true;
+}
+
+static bool _rfic_host_is_initialized(struct bladerf *dev)
+{
+ if (NULL == dev || NULL == dev->board_data) {
+ return false;
+ }
+
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ return (NULL != board_data->phy);
+}
+
+static int _rfic_host_get_init_state(struct bladerf *dev,
+ bladerf_rfic_init_state *state)
+{
+ if (_rfic_host_is_initialized(dev)) {
+ *state = BLADERF_RFIC_INIT_STATE_ON;
+ } else {
+ *state = BLADERF_RFIC_INIT_STATE_OFF;
+ }
+
+ return 0;
+}
+
+static bool _rfic_host_is_standby(struct bladerf *dev)
+{
+ return false;
+}
+
+static int _rfic_host_initialize(struct bladerf *dev)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = NULL;
+ struct controller_fns const *rfic = board_data->rfic;
+ uint32_t reg;
+ bladerf_direction dir;
+ bladerf_channel ch;
+ size_t i;
+ uint32_t config_gpio;
+
+ log_debug("%s: initializating\n", __FUNCTION__);
+
+ /* Initialize RFFE control */
+ CHECK_STATUS(dev->backend->rffe_control_write(
+ dev, (1 << RFFE_CONTROL_ENABLE) | (1 << RFFE_CONTROL_TXNRX)));
+
+
+ CHECK_STATUS(dev->backend->config_gpio_read(dev, &config_gpio));
+
+ board_data->rfic_init_params = (config_gpio & BLADERF_GPIO_PACKET_CORE_PRESENT) ?
+ (void *)&bladerf2_rfic_init_params_fastagc_burst :
+ (void *)&bladerf2_rfic_init_params;
+
+ /* Initialize AD9361 */
+ CHECK_AD936X(ad9361_init(&phy, (AD9361_InitParam *)board_data->rfic_init_params, dev));
+
+ if (NULL == phy || NULL == phy->pdata) {
+ RETURN_ERROR_STATUS("ad9361_init struct initialization",
+ BLADERF_ERR_UNEXPECTED);
+ }
+
+ log_verbose("%s: ad9361 initialized @ %p\n", __FUNCTION__, phy);
+
+ board_data->phy = phy;
+
+ /* Force AD9361 to a non-default freq. This will entice it to do a
+ * proper re-tuning when we set it back to the default freq later on. */
+ FOR_EACH_DIRECTION(dir)
+ {
+ FOR_EACH_CHANNEL(dir, 1, i, ch)
+ {
+ CHECK_STATUS(rfic->set_frequency(dev, ch, RESET_FREQUENCY));
+ }
+ }
+
+ /* Set up AD9361 FIR filters */
+ /* TODO: permit specification of filter taps, for the truly brave */
+ CHECK_STATUS(rfic->set_filter(dev, BLADERF_CHANNEL_RX(0),
+ BLADERF_RFIC_RXFIR_DEFAULT, 0));
+ CHECK_STATUS(rfic->set_filter(dev, BLADERF_CHANNEL_TX(0), 0,
+ BLADERF_RFIC_TXFIR_DEFAULT));
+
+ /* Clear RFFE control */
+ CHECK_STATUS(dev->backend->rffe_control_read(dev, &reg));
+ reg &= ~(1 << RFFE_CONTROL_TXNRX);
+ reg &= ~(1 << RFFE_CONTROL_ENABLE);
+ reg &= ~(1 << RFFE_CONTROL_RX_SPDT_1);
+ reg &= ~(1 << RFFE_CONTROL_RX_SPDT_2);
+ reg &= ~(1 << RFFE_CONTROL_TX_SPDT_1);
+ reg &= ~(1 << RFFE_CONTROL_TX_SPDT_2);
+ reg &= ~(1 << RFFE_CONTROL_MIMO_RX_EN_0);
+ reg &= ~(1 << RFFE_CONTROL_MIMO_TX_EN_0);
+ reg &= ~(1 << RFFE_CONTROL_MIMO_RX_EN_1);
+ reg &= ~(1 << RFFE_CONTROL_MIMO_TX_EN_1);
+ CHECK_STATUS(dev->backend->rffe_control_write(dev, reg));
+
+ /* Move AD9361 back to desired frequency */
+ CHECK_STATUS(rfic->set_frequency(dev, BLADERF_CHANNEL_RX(0),
+ phy->pdata->rx_synth_freq));
+ CHECK_STATUS(rfic->set_frequency(dev, BLADERF_CHANNEL_TX(0),
+ phy->pdata->tx_synth_freq));
+
+ /* Mute TX channels */
+ FOR_EACH_CHANNEL(BLADERF_TX, dev->board->get_channel_count(dev, BLADERF_TX),
+ i, ch)
+ {
+ CHECK_STATUS(rfic->set_txmute(dev, ch, true));
+ }
+
+ log_debug("%s: initialization complete\n", __FUNCTION__);
+
+ return 0;
+}
+
+static int _rfic_host_deinitialize(struct bladerf *dev)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ uint32_t reg;
+
+ log_debug("%s: deinitializing\n", __FUNCTION__);
+
+ /* Clear RFFE control */
+ CHECK_STATUS(dev->backend->rffe_control_read(dev, &reg));
+ reg &= ~(1 << RFFE_CONTROL_TXNRX);
+ reg &= ~(1 << RFFE_CONTROL_ENABLE);
+ reg &= ~(1 << RFFE_CONTROL_RX_SPDT_1);
+ reg &= ~(1 << RFFE_CONTROL_RX_SPDT_2);
+ reg &= ~(1 << RFFE_CONTROL_TX_SPDT_1);
+ reg &= ~(1 << RFFE_CONTROL_TX_SPDT_2);
+ reg &= ~(1 << RFFE_CONTROL_MIMO_RX_EN_0);
+ reg &= ~(1 << RFFE_CONTROL_MIMO_TX_EN_0);
+ reg &= ~(1 << RFFE_CONTROL_MIMO_RX_EN_1);
+ reg &= ~(1 << RFFE_CONTROL_MIMO_TX_EN_1);
+ CHECK_STATUS(dev->backend->rffe_control_write(dev, reg));
+
+ if (NULL != board_data->phy) {
+ CHECK_STATUS(ad9361_deinit(board_data->phy));
+ board_data->phy = NULL;
+ }
+
+ return 0;
+}
+
+static int _rfic_host_standby(struct bladerf *dev)
+{
+ return _rfic_host_deinitialize(dev);
+}
+
+
+/******************************************************************************/
+/* Enable */
+/******************************************************************************/
+
+static int _rfic_host_enable_module(struct bladerf *dev,
+ bladerf_channel ch,
+ bool enable)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ struct controller_fns const *rfic = board_data->rfic;
+ bladerf_direction dir = BLADERF_CHANNEL_IS_TX(ch) ? BLADERF_TX : BLADERF_RX;
+ bladerf_frequency freq = 0;
+ bladerf_channel_layout layout;
+ uint32_t reg; /* RFFE register value */
+ uint32_t reg_old; /* Original RFFE register value */
+ int ch_bit; /* RFFE MIMO channel bit */
+ int dir_bit; /* RFFE RX/TX enable bit */
+ bool ch_pending; /* Requested channel state differs */
+ bool dir_enable; /* Direction is enabled */
+ bool dir_pending; /* Requested direction state differs */
+ bool backend_clear; /* Backend requires reset */
+ bool mimo_enabled;
+
+ /* Look up the RFFE bits affecting this channel */
+ ch_bit = _get_rffe_control_bit_for_ch(ch);
+ dir_bit = _get_rffe_control_bit_for_dir(dir);
+ if (ch_bit < 0 || dir_bit < 0) {
+ RETURN_ERROR_STATUS("_get_rffe_control_bit", BLADERF_ERR_INVAL);
+ }
+
+ /* Query the current frequency if necessary */
+ if (enable) {
+ CHECK_STATUS(rfic->get_frequency(dev, ch, &freq));
+ }
+
+ /**
+ * If we are moving from 0 active channels to 1 active channel:
+ * Channel: Setup SPDT, MIMO, TX Mute
+ * Direction: Setup ENABLE/TXNRX, RFIC port
+ * Backend: Enable
+ *
+ * If we are moving from 1 active channel to 0 active channels:
+ * Channel: Teardown SPDT, MIMO, TX Mute
+ * Direction: Teardown ENABLE/TXNRX, RFIC port, Sync
+ * Backend: Disable
+ *
+ * If we are enabling an nth channel, where n > 1:
+ * Channel: Setup SPDT, MIMO, TX Mute
+ * Direction: no change
+ * Backend: Clear
+ *
+ * If we are disabling an nth channel, where n > 1:
+ * Channel: Teardown SPDT, MIMO, TX Mute
+ * Direction: no change
+ * Backend: no change
+ */
+
+ /* Read RFFE control register */
+ CHECK_STATUS(dev->backend->rffe_control_read(dev, &reg));
+ reg_old = reg;
+ ch_pending = _rffe_ch_enabled(reg, ch) != enable;
+ layout = board_data->sync->stream_config.layout;
+ mimo_enabled = layout == BLADERF_RX_X2 || layout == BLADERF_TX_X2;
+
+ if (layout == BLADERF_TX_X2) {
+ if (enable) {
+ log_debug("Enabling both TX channels. MIMO layout configured.\n");
+ reg |= (1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_TX(0)));
+ reg |= (1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_TX(1)));
+ } else {
+ log_debug("Disabling both TX channels. MIMO layout configured.\n");
+ reg &= ~(1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_TX(0)));
+ reg &= ~(1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_TX(1)));
+ }
+ }
+
+ if (layout == BLADERF_RX_X2) {
+ if (enable) {
+ log_debug("Enabling both RX channels. MIMO layout configured.\n");
+ reg |= (1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_RX(0)));
+ reg |= (1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_RX(1)));
+ } else {
+ log_debug("Disabling both RX channels. MIMO layout configured.\n");
+ reg &= ~(1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_RX(0)));
+ reg &= ~(1 << _get_rffe_control_bit_for_ch(BLADERF_CHANNEL_RX(1)));
+ }
+ }
+
+ /* Channel Setup/Teardown */
+ if (ch_pending) {
+ /* Modify SPDT bits */
+ CHECK_STATUS(_modify_spdt_bits_by_freq(&reg, ch, enable, freq));
+
+ /* Modify MIMO channel enable bits */
+ if (enable) {
+ reg |= (1 << ch_bit);
+ } else {
+ reg &= ~(1 << ch_bit);
+ }
+
+ /* Set/unset TX mute */
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ txmute_set(phy, ch, !enable);
+ }
+ }
+
+ dir_enable = enable || does_rffe_dir_have_enabled_ch(reg, dir);
+ dir_pending = _rffe_dir_enabled(reg, dir) != dir_enable;
+
+ /* Direction Setup/Teardown */
+ if (dir_pending) {
+ /* Modify ENABLE/TXNRX bits */
+ if (dir_enable) {
+ reg |= (1 << dir_bit);
+ } else {
+ reg &= ~(1 << dir_bit);
+ }
+
+ /* Select RFIC port */
+ CHECK_STATUS(set_ad9361_port_by_freq(phy, ch, dir_enable, freq));
+
+ /* Tear down sync interface if required */
+ if (!dir_enable) {
+ sync_deinit(&board_data->sync[dir]);
+ perform_format_deconfig(dev, dir);
+ }
+ }
+
+ /* Reset FIFO if we are enabling an additional RX channel */
+ backend_clear = enable && !dir_pending && BLADERF_RX == dir && !mimo_enabled;
+
+ /* Write RFFE control register */
+ if (reg_old != reg) {
+ CHECK_STATUS(dev->backend->rffe_control_write(dev, reg));
+ } else {
+ log_debug("%s: reg value unchanged? (%08x)\n", __FUNCTION__, reg);
+ }
+
+ /* Backend Setup/Teardown/Reset */
+ if (dir_pending || backend_clear) {
+ if (!dir_enable || backend_clear) {
+ CHECK_STATUS(dev->backend->enable_module(dev, dir, false));
+ }
+
+ if (dir_enable || backend_clear) {
+ CHECK_STATUS(dev->backend->enable_module(dev, dir, true));
+ }
+ }
+
+ /* Warn the user if the sample rate isn't reasonable */
+ if (ch_pending && enable) {
+ check_total_sample_rate(dev);
+ }
+
+#ifdef BLADERF_HOSTED_C_DEBUG
+ /* Debug logging */
+ static uint64_t lastrun = 0; /* nsec value at last run */
+ uint64_t nsec = wallclock_get_current_nsec();
+
+ log_debug("%s: %s%d ch_en=%d ch_pend=%d dir_en=%d dir_pend=%d be_clr=%d "
+ "reg=0x%08x->0x%08x nsec=%" PRIu64 " (delta: %" PRIu64 ")\n",
+ __FUNCTION__, BLADERF_TX == dir ? "TX" : "RX", (ch >> 1) + 1,
+ enable, ch_pending, dir_enable, dir_pending, backend_clear,
+ reg_old, reg, nsec, nsec - lastrun);
+
+ lastrun = nsec;
+#endif
+
+ /** Force rerun frequency calibration */
+ if (enable) {
+ bladerf_frequency current_frequency;
+ CHECK_STATUS(dev->board->get_frequency(dev, ch, &current_frequency));
+ CHECK_STATUS(dev->board->set_frequency(dev, ch, current_frequency));
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Sample rate */
+/******************************************************************************/
+
+static int _rfic_host_get_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate *rate)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ CHECK_AD936X(ad9361_get_tx_sampling_freq(phy, rate));
+ } else {
+ CHECK_AD936X(ad9361_get_rx_sampling_freq(phy, rate));
+ }
+
+ return 0;
+}
+
+static int _rfic_host_set_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate rate)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ CHECK_AD936X(ad9361_set_tx_sampling_freq(phy, rate));
+ } else {
+ CHECK_AD936X(ad9361_set_rx_sampling_freq(phy, rate));
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Frequency */
+/******************************************************************************/
+
+static int _rfic_host_get_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency *frequency)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ bladerf_frequency lo_frequency;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ CHECK_AD936X(ad9361_get_tx_lo_freq(phy, &lo_frequency));
+ } else {
+ CHECK_AD936X(ad9361_get_rx_lo_freq(phy, &lo_frequency));
+ }
+
+ *frequency = lo_frequency;
+
+ return 0;
+}
+
+static int _rfic_host_set_frequency(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ struct controller_fns const *rfic = board_data->rfic;
+ struct bladerf_range const *range = NULL;
+
+ CHECK_STATUS(dev->board->get_frequency_range(dev, ch, &range));
+
+ if (!is_within_range(range, frequency)) {
+ return BLADERF_ERR_RANGE;
+ }
+
+ /* Set up band selection */
+ CHECK_STATUS(rfic->select_band(dev, ch, frequency));
+
+ /* Change LO frequency */
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ CHECK_AD936X(ad9361_set_tx_lo_freq(phy, frequency));
+ } else {
+ CHECK_AD936X(ad9361_set_rx_lo_freq(phy, frequency));
+ }
+
+ return 0;
+}
+
+static int _rfic_host_select_band(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ uint32_t reg;
+ size_t i;
+
+ /* Read RFFE control register */
+ CHECK_STATUS(dev->backend->rffe_control_read(dev, &reg));
+
+ /* Modify the SPDT bits. */
+ /* We have to do this for all the channels sharing the same LO. */
+ for (i = 0; i < 2; ++i) {
+ bladerf_channel bch = BLADERF_CHANNEL_IS_TX(ch) ? BLADERF_CHANNEL_TX(i)
+ : BLADERF_CHANNEL_RX(i);
+ CHECK_STATUS(_modify_spdt_bits_by_freq(
+ &reg, bch, _rffe_ch_enabled(reg, bch), frequency));
+ }
+
+ /* Write RFFE control register */
+ CHECK_STATUS(dev->backend->rffe_control_write(dev, reg));
+
+ /* Set AD9361 port */
+ CHECK_STATUS(
+ set_ad9361_port_by_freq(phy, ch, _rffe_ch_enabled(reg, ch), frequency));
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Bandwidth */
+/******************************************************************************/
+
+static int _rfic_host_get_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth *bandwidth)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ CHECK_AD936X(ad9361_get_tx_rf_bandwidth(phy, bandwidth));
+ } else {
+ CHECK_AD936X(ad9361_get_rx_rf_bandwidth(phy, bandwidth));
+ }
+
+ return 0;
+}
+
+static int _rfic_host_set_bandwidth(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth bandwidth,
+ bladerf_bandwidth *actual)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ struct controller_fns const *rfic = board_data->rfic;
+ struct bladerf_range const *range = NULL;
+
+ CHECK_STATUS(dev->board->get_bandwidth_range(dev, ch, &range));
+
+ bandwidth = (unsigned int)clamp_to_range(range, bandwidth);
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ CHECK_AD936X(ad9361_set_tx_rf_bandwidth(phy, bandwidth));
+ } else {
+ CHECK_AD936X(ad9361_set_rx_rf_bandwidth(phy, bandwidth));
+ }
+
+ if (actual != NULL) {
+ return rfic->get_bandwidth(dev, ch, actual);
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Gain mode */
+/******************************************************************************/
+
+static int _rfic_host_get_gain_mode(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode *mode)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ uint8_t const rfic_ch = ch >> 1;
+ uint8_t gc_mode;
+ bool ok;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ log_warning("%s: automatic gain control not valid for TX channels\n",
+ __FUNCTION__);
+ *mode = BLADERF_GAIN_DEFAULT;
+ return 0;
+ }
+
+ /* Get the gain */
+ CHECK_AD936X(ad9361_get_rx_gain_control_mode(phy, rfic_ch, &gc_mode));
+
+ /* Mode conversion */
+ if (mode != NULL) {
+ *mode = gainmode_ad9361_to_bladerf(gc_mode, &ok);
+ if (!ok) {
+ RETURN_INVAL("mode", "is not valid");
+ }
+ }
+
+ return 0;
+}
+
+static int _rfic_host_set_gain_mode(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode mode)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ uint8_t const rfic_ch = ch >> 1;
+ enum rf_gain_ctrl_mode gc_mode;
+ bool ok;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ log_warning("%s: automatic gain control not valid for TX channels\n",
+ __FUNCTION__);
+ return 0;
+ }
+
+ /* Channel conversion */
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ gc_mode = ((AD9361_InitParam *)board_data->rfic_init_params)->gc_rx1_mode;
+ break;
+
+ case BLADERF_CHANNEL_RX(1):
+ gc_mode = ((AD9361_InitParam *)board_data->rfic_init_params)->gc_rx2_mode;
+ break;
+
+ default:
+ log_error("%s: unknown channel index (%d)\n", __FUNCTION__, ch);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ /* Mode conversion */
+ if (mode != BLADERF_GAIN_DEFAULT) {
+ gc_mode = gainmode_bladerf_to_ad9361(mode, &ok);
+ if (!ok) {
+ RETURN_INVAL("mode", "is not valid");
+ }
+ }
+
+ /* Set the mode! */
+ CHECK_AD936X(ad9361_set_rx_gain_control_mode(phy, rfic_ch, gc_mode));
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Gain */
+/* These functions handle offset */
+/******************************************************************************/
+
+static int _rfic_host_get_gain(struct bladerf *dev,
+ bladerf_channel ch,
+ int *gain)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ struct controller_fns const *rfic = board_data->rfic;
+ int val;
+ float offset;
+
+ CHECK_STATUS(get_gain_offset(dev, ch, &offset));
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ bool muted;
+
+ CHECK_STATUS(txmute_get(phy, ch, &muted));
+
+ if (muted) {
+ struct bladerf_range const *range = NULL;
+
+ CHECK_STATUS(
+ dev->board->get_gain_stage_range(dev, ch, "dsa", &range));
+
+ val = -__unscale_int(range, txmute_get_cached(phy, ch));
+ } else {
+ CHECK_STATUS(rfic->get_gain_stage(dev, ch, "dsa", &val));
+ }
+ } else {
+ CHECK_STATUS(rfic->get_gain_stage(dev, ch, "full", &val));
+ }
+
+ *gain = __round_int(val + offset);
+
+ return 0;
+}
+
+static int _rfic_host_set_gain(struct bladerf *dev,
+ bladerf_channel ch,
+ int gain)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ struct controller_fns const *rfic = board_data->rfic;
+ int val;
+ float offset;
+
+ CHECK_STATUS(get_gain_offset(dev, ch, &offset));
+
+ val = gain - offset;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ bool muted;
+
+ CHECK_STATUS(txmute_get(phy, ch, &muted));
+
+ if (muted) {
+ struct bladerf_range const *range = NULL;
+
+ CHECK_STATUS(
+ dev->board->get_gain_stage_range(dev, ch, "dsa", &range));
+
+ CHECK_STATUS(txmute_set_cached(phy, ch, -__scale_int(range, val)));
+ } else {
+ CHECK_STATUS(rfic->set_gain_stage(dev, ch, "dsa", val));
+ }
+ } else {
+ CHECK_STATUS(rfic->set_gain_stage(dev, ch, "full", val));
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Gain stage */
+/* These functions handle scaling */
+/******************************************************************************/
+
+static int _rfic_host_get_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ int *gain)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ struct bladerf_range const *range = NULL;
+ uint8_t const rfic_ch = ch >> 1;
+ int val;
+
+ CHECK_STATUS(dev->board->get_gain_stage_range(dev, ch, stage, &range));
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ if (strcmp(stage, "dsa") == 0) {
+ uint32_t atten;
+
+ CHECK_AD936X(ad9361_get_tx_attenuation(phy, rfic_ch, &atten));
+
+ val = -atten;
+ } else {
+ log_error("%s: gain stage '%s' invalid\n", __FUNCTION__, stage);
+ return BLADERF_ERR_INVAL;
+ }
+ } else {
+ struct rf_rx_gain rx_gain;
+ CHECK_AD936X(ad9361_get_rx_gain(phy, rfic_ch + 1, &rx_gain));
+
+ if (strcmp(stage, "full") == 0) {
+ val = rx_gain.gain_db;
+ } else if (strcmp(stage, "digital") == 0) {
+ val = rx_gain.digital_gain;
+ } else {
+ log_error("%s: gain stage '%s' invalid\n", __FUNCTION__, stage);
+ return BLADERF_ERR_INVAL;
+ }
+ }
+
+ *gain = __unscale_int(range, val);
+
+ return 0;
+}
+
+static int _rfic_host_set_gain_stage(struct bladerf *dev,
+ bladerf_channel ch,
+ char const *stage,
+ int gain)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ struct bladerf_range const *range = NULL;
+ uint8_t const rfic_ch = ch >> 1;
+ int val;
+
+ CHECK_STATUS(dev->board->get_gain_stage_range(dev, ch, stage, &range));
+
+ /* Scale/round/clamp as required */
+ if (BLADERF_CHANNEL_IS_TX(ch) && gain < -89) {
+ val = -89750;
+ } else {
+ val = __scale_int(range, clamp_to_range(range, gain));
+ }
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ if (strcmp(stage, "dsa") == 0) {
+ CHECK_AD936X(ad9361_set_tx_attenuation(phy, rfic_ch, -val));
+ } else {
+ log_warning("%s: gain stage '%s' invalid\n", __FUNCTION__, stage);
+ return BLADERF_ERR_INVAL;
+ }
+ } else {
+ if (strcmp(stage, "full") == 0) {
+ CHECK_AD936X(ad9361_set_rx_rf_gain(phy, rfic_ch, val));
+ } else {
+ log_warning("%s: gain stage '%s' invalid\n", __FUNCTION__, stage);
+ return BLADERF_ERR_INVAL;
+ }
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* RSSI */
+/******************************************************************************/
+
+static int _rfic_host_get_rssi(struct bladerf *dev,
+ bladerf_channel ch,
+ int *pre_rssi,
+ int *sym_rssi)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+ uint8_t const rfic_ch = ch >> 1;
+
+ int pre, sym;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ uint32_t rssi = 0;
+
+ CHECK_AD936X(ad9361_get_tx_rssi(phy, rfic_ch, &rssi));
+
+ pre = __round_int(rssi / 1000.0);
+ sym = __round_int(rssi / 1000.0);
+ } else {
+ struct rf_rssi rssi;
+
+ CHECK_AD936X(ad9361_get_rx_rssi(phy, rfic_ch, &rssi));
+
+ pre = __round_int(rssi.preamble / (float)rssi.multiplier);
+ sym = __round_int(rssi.symbol / (float)rssi.multiplier);
+ }
+
+ if (NULL != pre_rssi) {
+ *pre_rssi = -pre;
+ }
+
+ if (NULL != sym_rssi) {
+ *sym_rssi = -sym;
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Filter */
+/******************************************************************************/
+
+static int _rfic_host_get_filter(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_rfic_rxfir *rxfir,
+ bladerf_rfic_txfir *txfir)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+
+ if (NULL != rxfir) {
+ *rxfir = board_data->rxfir;
+ }
+
+ if (NULL != txfir) {
+ *txfir = board_data->txfir;
+ }
+
+ return 0;
+}
+
+static int _rfic_host_set_filter(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_rfic_rxfir rxfir,
+ bladerf_rfic_txfir txfir)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ AD9361_TXFIRConfig *fir_config = NULL;
+ uint8_t enable;
+
+ if (BLADERF_RFIC_TXFIR_CUSTOM == txfir) {
+ log_warning("custom FIR not implemented, assuming default\n");
+ txfir = BLADERF_RFIC_TXFIR_DEFAULT;
+ }
+
+ switch (txfir) {
+ case BLADERF_RFIC_TXFIR_BYPASS:
+ fir_config = &bladerf2_rfic_tx_fir_config;
+ enable = 0;
+ break;
+ case BLADERF_RFIC_TXFIR_INT1:
+ fir_config = &bladerf2_rfic_tx_fir_config;
+ enable = 1;
+ break;
+ case BLADERF_RFIC_TXFIR_INT2:
+ fir_config = &bladerf2_rfic_tx_fir_config_int2;
+ enable = 1;
+ break;
+ case BLADERF_RFIC_TXFIR_INT4:
+ fir_config = &bladerf2_rfic_tx_fir_config_int4;
+ enable = 1;
+ break;
+ default:
+ MUTEX_UNLOCK(&dev->lock);
+ assert(!"Bug: unhandled txfir selection");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ CHECK_AD936X(ad9361_set_tx_fir_config(phy, *fir_config));
+ CHECK_AD936X(ad9361_set_tx_fir_en_dis(phy, enable));
+
+ board_data->txfir = txfir;
+ } else {
+ AD9361_RXFIRConfig *fir_config = NULL;
+ uint8_t enable;
+
+ if (BLADERF_RFIC_RXFIR_CUSTOM == rxfir) {
+ log_warning("custom FIR not implemented, assuming default\n");
+ rxfir = BLADERF_RFIC_RXFIR_DEFAULT;
+ }
+
+ switch (rxfir) {
+ case BLADERF_RFIC_RXFIR_BYPASS:
+ fir_config = &bladerf2_rfic_rx_fir_config;
+ enable = 0;
+ break;
+ case BLADERF_RFIC_RXFIR_DEC1:
+ fir_config = &bladerf2_rfic_rx_fir_config;
+ enable = 1;
+ break;
+ case BLADERF_RFIC_RXFIR_DEC2:
+ fir_config = &bladerf2_rfic_rx_fir_config_dec2;
+ enable = 1;
+ break;
+ case BLADERF_RFIC_RXFIR_DEC4:
+ fir_config = &bladerf2_rfic_rx_fir_config_dec4;
+ enable = 1;
+ break;
+ default:
+ MUTEX_UNLOCK(&dev->lock);
+ assert(!"Bug: unhandled rxfir selection");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ CHECK_AD936X(ad9361_set_rx_fir_config(phy, *fir_config));
+ CHECK_AD936X(ad9361_set_rx_fir_en_dis(phy, enable));
+
+ board_data->rxfir = rxfir;
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* TX Mute */
+/******************************************************************************/
+
+static int _rfic_host_get_txmute(struct bladerf *dev,
+ bladerf_channel ch,
+ bool *state)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ return txmute_get(phy, ch, state);
+ }
+
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int _rfic_host_set_txmute(struct bladerf *dev,
+ bladerf_channel ch,
+ bool state)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ return txmute_set(phy, ch, state);
+ }
+
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+
+/******************************************************************************/
+/* Fastlock */
+/******************************************************************************/
+
+static int _rfic_host_store_fastlock_profile(struct bladerf *dev,
+ bladerf_channel ch,
+ uint32_t profile)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ CHECK_AD936X(ad9361_tx_fastlock_store(phy, profile));
+ } else {
+ CHECK_AD936X(ad9361_rx_fastlock_store(phy, profile));
+ }
+
+ return 0;
+}
+
+static int _rfic_host_save_fastlock_profile(struct bladerf *dev,
+ bladerf_channel ch,
+ uint32_t profile,
+ uint8_t *values)
+{
+ struct bladerf2_board_data *board_data = dev->board_data;
+ struct ad9361_rf_phy *phy = board_data->phy;
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ CHECK_AD936X(ad9361_tx_fastlock_save(phy, profile, values));
+ } else {
+ CHECK_AD936X(ad9361_rx_fastlock_save(phy, profile, values));
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+/* Function pointers */
+/******************************************************************************/
+
+struct controller_fns const rfic_host_control = {
+ FIELD_INIT(.is_present, _rfic_host_is_present),
+ FIELD_INIT(.is_standby, _rfic_host_is_standby),
+ FIELD_INIT(.is_initialized, _rfic_host_is_initialized),
+ FIELD_INIT(.get_init_state, _rfic_host_get_init_state),
+
+ FIELD_INIT(.initialize, _rfic_host_initialize),
+ FIELD_INIT(.standby, _rfic_host_standby),
+ FIELD_INIT(.deinitialize, _rfic_host_deinitialize),
+
+ FIELD_INIT(.enable_module, _rfic_host_enable_module),
+
+ FIELD_INIT(.get_sample_rate, _rfic_host_get_sample_rate),
+ FIELD_INIT(.set_sample_rate, _rfic_host_set_sample_rate),
+
+ FIELD_INIT(.get_frequency, _rfic_host_get_frequency),
+ FIELD_INIT(.set_frequency, _rfic_host_set_frequency),
+ FIELD_INIT(.select_band, _rfic_host_select_band),
+
+ FIELD_INIT(.get_bandwidth, _rfic_host_get_bandwidth),
+ FIELD_INIT(.set_bandwidth, _rfic_host_set_bandwidth),
+
+ FIELD_INIT(.get_gain_mode, _rfic_host_get_gain_mode),
+ FIELD_INIT(.set_gain_mode, _rfic_host_set_gain_mode),
+
+ FIELD_INIT(.get_gain, _rfic_host_get_gain),
+ FIELD_INIT(.set_gain, _rfic_host_set_gain),
+
+ FIELD_INIT(.get_gain_stage, _rfic_host_get_gain_stage),
+ FIELD_INIT(.set_gain_stage, _rfic_host_set_gain_stage),
+
+ FIELD_INIT(.get_rssi, _rfic_host_get_rssi),
+
+ FIELD_INIT(.get_filter, _rfic_host_get_filter),
+ FIELD_INIT(.set_filter, _rfic_host_set_filter),
+
+ FIELD_INIT(.get_txmute, _rfic_host_get_txmute),
+ FIELD_INIT(.set_txmute, _rfic_host_set_txmute),
+
+ FIELD_INIT(.store_fastlock_profile, _rfic_host_store_fastlock_profile),
+ FIELD_INIT(.save_fastlock_profile, _rfic_host_save_fastlock_profile),
+
+ FIELD_INIT(.command_mode, RFIC_COMMAND_HOST),
+};
diff --git a/Radio/HW/BladeRF/src/board/board.c b/Radio/HW/BladeRF/src/board/board.c
new file mode 100644
index 0000000..9b49242
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/board.c
@@ -0,0 +1,13 @@
+#include "host_config.h"
+
+#include "board.h"
+
+extern const struct board_fns bladerf1_board_fns;
+extern const struct board_fns bladerf2_board_fns;
+
+const struct board_fns *bladerf_boards[] = {
+ &bladerf1_board_fns,
+ &bladerf2_board_fns,
+};
+
+const unsigned int bladerf_boards_len = ARRAY_SIZE(bladerf_boards);
diff --git a/Radio/HW/BladeRF/src/board/board.h b/Radio/HW/BladeRF/src/board/board.h
new file mode 100644
index 0000000..279a0c1
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/board.h
@@ -0,0 +1,495 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2017 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef BOARD_BOARD_H_
+#define BOARD_BOARD_H_
+
+#include <stdint.h>
+
+#include <libbladeRF.h>
+
+#include "host_config.h"
+#include "thread.h"
+
+#include "backend/backend.h"
+
+/* Device capabilities are stored in a 64-bit mask.
+ *
+ * FPGA-oriented capabilities start at bit 0.
+ * FX3-oriented capabilities start at bit 24.
+ * Other/mixed capabilities start at bit 48.
+ */
+
+/**
+ * Prior to FPGA 0.0.4, the DAC register were located at a different address
+ */
+#define BLADERF_CAP_UPDATED_DAC_ADDR (1 << 0)
+
+/**
+ * FPGA version 0.0.5 introduced XB-200 support
+ */
+#define BLADERF_CAP_XB200 (1 << 1)
+
+/**
+ * FPGA version 0.1.0 introduced timestamp support
+ */
+#define BLADERF_CAP_TIMESTAMPS (1 << 2)
+
+/**
+ * FPGA v0.2.0 introduced scheduled retune support on the bladeRF 1, and FPGA
+ * v0.10.0 introduced it on the bladeRF 2.
+ */
+#define BLADERF_CAP_SCHEDULED_RETUNE (1 << 3)
+
+/**
+ * FPGA version 0.3.0 introduced new packet handler formats that pack
+ * operations into a single requests.
+ */
+#define BLADERF_CAP_PKT_HANDLER_FMT (1 << 4)
+
+/**
+ * A bug fix in FPGA version 0.3.2 allowed support for reading back
+ * the current VCTCXO trim dac value.
+ */
+#define BLADERF_CAP_VCTCXO_TRIMDAC_READ (1 << 5)
+
+/**
+ * FPGA v0.4.0 introduced the ability to write LMS6002D TX/RX PLL
+ * NINT & NFRAC regiters atomically.
+ */
+#define BLADERF_CAP_ATOMIC_NINT_NFRAC_WRITE (1 << 6)
+
+/**
+ * FPGA v0.4.1 fixed an issue with masked writes to the expansion GPIO
+ * and expansion GPIO direction registers.
+ *
+ * To help users avoid getting bitten by this bug, we'll mark this
+ * as a capability and disallow masked writes unless an FPGA with the
+ * fix is being used.
+ */
+#define BLADERF_CAP_MASKED_XBIO_WRITE (1 << 7)
+
+/**
+ * FPGA v0.5.0 introduces the ability to tame the VCTCXO via a 1 pps or 10 MHz
+ * input source on the mini expansion header.
+ */
+#define BLADERF_CAP_VCTCXO_TAMING_MODE (1 << 8)
+
+/**
+ * FPGA v0.6.0 introduced trx synchronization trigger via J71-4
+ */
+#define BLADERF_CAP_TRX_SYNC_TRIG (1 << 9)
+
+/**
+ * FPGA v0.7.0 introduced AGC DC correction Look-Up-Table
+ */
+#define BLADERF_CAP_AGC_DC_LUT (1 << 10)
+
+/**
+ * FPGA v0.2.0 introduced NIOS-based tuning support on the bladeRF 1, and FPGA
+ * v0.10.1 introduced it on the bladeRF 2.
+ */
+#define BLADERF_CAP_FPGA_TUNING (1 << 11)
+
+/**
+ * FPGA v0.12.0 introduces the packet meta format, allowing for variable length
+ * packets, with core target IDs.
+ */
+#define BLADERF_CAP_FPGA_PACKET_META (1 << 12)
+
+/**
+ * Firmware 1.7.1 introduced firmware-based loopback
+ */
+#define BLADERF_CAP_FW_LOOPBACK (((uint64_t)1) << 32)
+
+/**
+ * FX3 firmware version 1.8.0 introduced the ability to query when the
+ * device has become ready for use by the host. This was done to avoid
+ * opening and attempting to use the device while flash-based FPGA autoloading
+ * was occurring.
+ */
+#define BLADERF_CAP_QUERY_DEVICE_READY (((uint64_t)1) << 33)
+
+/**
+ * FX3 firmware v1.9.0 introduced a vendor request by which firmware log
+ * events could be retrieved.
+ */
+#define BLADERF_CAP_READ_FW_LOG_ENTRY (((uint64_t)1) << 34)
+
+/**
+ * FX3 firmware v2.1.0 introduced support for bladeRF 2
+ */
+#define BLADERF_CAP_FW_SUPPORTS_BLADERF2 (((uint64_t)1) << 35)
+
+/**
+ * FX3 firmware v2.3.0 introduced support for reporting the SPI Flash
+ * manufacturer ID and device ID.
+ */
+#define BLADERF_CAP_FW_FLASH_ID (((uint64_t)1) << 36)
+
+/**
+ * FX3 firmware v2.3.1 introduced support for querying the source of the
+ * currently-configured FPGA (e.g. flash autoload, host, etc)
+ */
+#define BLADERF_CAP_FW_FPGA_SOURCE (((uint64_t)1) << 37)
+
+/**
+ * FX3 firmware v2.4.0 introduced support for short packets.
+ */
+#define BLADERF_CAP_FW_SHORT_PACKET (((uint64_t)1) << 38)
+
+/**
+ * FPGA v0.15.0 introduces support for 8bit mode.
+ */
+#define BLADERF_CAP_FPGA_8BIT_SAMPLES (((uint64_t)1) << 39)
+
+/**
+ * Max number of gain calibration tables associated to max number of channels
+ */
+#define NUM_GAIN_CAL_TBLS 4
+
+struct bladerf {
+ /* Handle lock - to ensure atomic access to control and configuration
+ * operations */
+ MUTEX lock;
+
+ /* Identifying information */
+ struct bladerf_devinfo ident;
+
+ /* Backend-specific implementations */
+ const struct backend_fns *backend;
+
+ /* Backend's private data */
+ void *backend_data;
+
+ /* Board-specific implementations */
+ const struct board_fns *board;
+
+ /* Flash architecture */
+ struct bladerf_flash_arch *flash_arch;
+
+ /* Board's private data */
+ void *board_data;
+
+ /* XB attached */
+ bladerf_xb xb;
+
+ /* XB's private data */
+ void *xb_data;
+
+ /* Enabled feature */
+ bladerf_feature feature;
+
+ /* Calibration */
+ struct bladerf_gain_cal_tbl gain_tbls[NUM_GAIN_CAL_TBLS];
+};
+
+struct board_fns {
+ /* Board is compatible with backend */
+ bool (*matches)(struct bladerf *dev);
+
+ /* Open/close */
+ int (*open)(struct bladerf *dev, struct bladerf_devinfo *devinfo);
+ void (*close)(struct bladerf *dev);
+
+ /* Properties */
+ bladerf_dev_speed (*device_speed)(struct bladerf *dev);
+ int (*get_serial)(struct bladerf *dev, char *serial);
+ int (*get_fpga_size)(struct bladerf *dev, bladerf_fpga_size *size);
+ int (*get_fpga_bytes)(struct bladerf *dev, size_t *size);
+ int (*get_flash_size)(struct bladerf *dev, uint32_t *size, bool *is_guess);
+ int (*is_fpga_configured)(struct bladerf *dev);
+ int (*get_fpga_source)(struct bladerf *dev, bladerf_fpga_source *source);
+ uint64_t (*get_capabilities)(struct bladerf *dev);
+ size_t (*get_channel_count)(struct bladerf *dev, bladerf_direction dir);
+
+ /* Versions */
+ int (*get_fpga_version)(struct bladerf *dev,
+ struct bladerf_version *version);
+ int (*get_fw_version)(struct bladerf *dev, struct bladerf_version *version);
+
+ /* Gain */
+ int (*set_gain)(struct bladerf *dev, bladerf_channel ch, bladerf_gain gain);
+ int (*get_gain)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain *gain);
+ int (*set_gain_mode)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode mode);
+ int (*get_gain_mode)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_gain_mode *mode);
+ int (*get_gain_modes)(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_gain_modes **modes);
+ int (*get_gain_range)(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range);
+ int (*set_gain_stage)(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *stage,
+ bladerf_gain gain);
+ int (*get_gain_stage)(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *stage,
+ bladerf_gain *gain);
+ int (*get_gain_stage_range)(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *stage,
+ const struct bladerf_range **range);
+ int (*get_gain_stages)(struct bladerf *dev,
+ bladerf_channel ch,
+ const char **stages,
+ size_t count);
+
+ /* Sample Rate */
+ int (*set_sample_rate)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate rate,
+ bladerf_sample_rate *actual);
+ int (*set_rational_sample_rate)(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_rational_rate *rate,
+ struct bladerf_rational_rate *actual);
+ int (*get_sample_rate)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_sample_rate *rate);
+ int (*get_sample_rate_range)(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range);
+ int (*get_rational_sample_rate)(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_rational_rate *rate);
+
+ /* Bandwidth */
+ int (*set_bandwidth)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth bandwidth,
+ bladerf_bandwidth *actual);
+ int (*get_bandwidth)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_bandwidth *bandwidth);
+ int (*get_bandwidth_range)(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range);
+
+ /* Frequency */
+ int (*get_frequency)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency *frequency);
+ int (*set_frequency)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency);
+ int (*get_frequency_range)(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_range **range);
+ int (*select_band)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_frequency frequency);
+
+ /* RF Ports */
+ int (*set_rf_port)(struct bladerf *dev,
+ bladerf_channel ch,
+ const char *port);
+ int (*get_rf_port)(struct bladerf *dev,
+ bladerf_channel ch,
+ const char **port);
+ int (*get_rf_ports)(struct bladerf *dev,
+ bladerf_channel ch,
+ const char **ports,
+ unsigned int count);
+
+ /* Scheduled Tuning */
+ int (*get_quick_tune)(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_quick_tune *quick_tune);
+ int (*schedule_retune)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_timestamp timestamp,
+ bladerf_frequency frequency,
+ struct bladerf_quick_tune *quick_tune);
+ int (*cancel_scheduled_retunes)(struct bladerf *dev, bladerf_channel ch);
+
+ /* DC/Phase/Gain Correction */
+ int (*get_correction)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_correction corr,
+ int16_t *value);
+ int (*set_correction)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_correction corr,
+ int16_t value);
+
+ /* Trigger */
+ int (*trigger_init)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal signal,
+ struct bladerf_trigger *trigger);
+ int (*trigger_arm)(struct bladerf *dev,
+ const struct bladerf_trigger *trigger,
+ bool arm,
+ uint64_t resv1,
+ uint64_t resv2);
+ int (*trigger_fire)(struct bladerf *dev,
+ const struct bladerf_trigger *trigger);
+ int (*trigger_state)(struct bladerf *dev,
+ const struct bladerf_trigger *trigger,
+ bool *is_armed,
+ bool *has_fired,
+ bool *fire_requested,
+ uint64_t *resv1,
+ uint64_t *resv2);
+
+ /* Streaming */
+ int (*enable_module)(struct bladerf *dev, bladerf_channel ch, bool enable);
+ int (*init_stream)(struct bladerf_stream **stream,
+ struct bladerf *dev,
+ bladerf_stream_cb callback,
+ void ***buffers,
+ size_t num_buffers,
+ bladerf_format format,
+ size_t samples_per_buffer,
+ size_t num_transfers,
+ void *user_data);
+ int (*stream)(struct bladerf_stream *stream, bladerf_channel_layout layout);
+ int (*submit_stream_buffer)(struct bladerf_stream *stream,
+ void *buffer,
+ unsigned int timeout_ms,
+ bool nonblock);
+ void (*deinit_stream)(struct bladerf_stream *stream);
+ int (*set_stream_timeout)(struct bladerf *dev,
+ bladerf_direction dir,
+ unsigned int timeout);
+ int (*get_stream_timeout)(struct bladerf *dev,
+ bladerf_direction dir,
+ unsigned int *timeout);
+ int (*sync_config)(struct bladerf *dev,
+ bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int num_buffers,
+ unsigned int buffer_size,
+ unsigned int num_transfers,
+ unsigned int stream_timeout);
+ int (*sync_tx)(struct bladerf *dev,
+ const void *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms);
+ int (*sync_rx)(struct bladerf *dev,
+ void *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms);
+ int (*get_timestamp)(struct bladerf *dev,
+ bladerf_direction dir,
+ bladerf_timestamp *timestamp);
+
+ /* FPGA/Firmware Loading/Flashing */
+ int (*load_fpga)(struct bladerf *dev, const uint8_t *buf, size_t length);
+ int (*flash_fpga)(struct bladerf *dev, const uint8_t *buf, size_t length);
+ int (*erase_stored_fpga)(struct bladerf *dev);
+ int (*flash_firmware)(struct bladerf *dev,
+ const uint8_t *buf,
+ size_t length);
+ int (*device_reset)(struct bladerf *dev);
+
+ /* Tuning mode */
+ int (*set_tuning_mode)(struct bladerf *dev, bladerf_tuning_mode mode);
+ int (*get_tuning_mode)(struct bladerf *dev, bladerf_tuning_mode *mode);
+
+ /* Loopback */
+ int (*get_loopback_modes)(struct bladerf *dev,
+ const struct bladerf_loopback_modes **modes);
+ int (*set_loopback)(struct bladerf *dev, bladerf_loopback l);
+ int (*get_loopback)(struct bladerf *dev, bladerf_loopback *l);
+
+ /* Sample RX FPGA Mux */
+ int (*get_rx_mux)(struct bladerf *dev, bladerf_rx_mux *mode);
+ int (*set_rx_mux)(struct bladerf *dev, bladerf_rx_mux mode);
+
+ /* Low-level VCTCXO Tamer Mode */
+ int (*set_vctcxo_tamer_mode)(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode mode);
+ int (*get_vctcxo_tamer_mode)(struct bladerf *dev,
+ bladerf_vctcxo_tamer_mode *mode);
+
+ /* Low-level VCTCXO Trim DAC access */
+ int (*get_vctcxo_trim)(struct bladerf *dev, uint16_t *trim);
+ int (*trim_dac_read)(struct bladerf *dev, uint16_t *trim);
+ int (*trim_dac_write)(struct bladerf *dev, uint16_t trim);
+
+ /* Low-level Trigger control access */
+ int (*read_trigger)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t *val);
+ int (*write_trigger)(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t val);
+
+ /* Low-level Wishbone Master access */
+ int (*wishbone_master_read)(struct bladerf *dev, uint32_t addr, uint32_t *data);
+ int (*wishbone_master_write)(struct bladerf *dev, uint32_t addr, uint32_t data);
+
+ /* Low-level Configuration GPIO access */
+ int (*config_gpio_read)(struct bladerf *dev, uint32_t *val);
+ int (*config_gpio_write)(struct bladerf *dev, uint32_t val);
+
+ /* Low-level SPI flash access */
+ int (*erase_flash)(struct bladerf *dev,
+ uint32_t erase_block,
+ uint32_t count);
+ int (*read_flash)(struct bladerf *dev,
+ uint8_t *buf,
+ uint32_t page,
+ uint32_t count);
+ int (*write_flash)(struct bladerf *dev,
+ const uint8_t *buf,
+ uint32_t page,
+ uint32_t count);
+
+ /* Expansion support */
+ int (*expansion_attach)(struct bladerf *dev, bladerf_xb xb);
+ int (*expansion_get_attached)(struct bladerf *dev, bladerf_xb *xb);
+
+ /* Board name */
+ const char *name;
+};
+
+/* Information about the (SPI) flash architecture */
+struct bladerf_flash_arch {
+ enum { STATUS_FLASH_UNINITIALIZED, STATUS_SUCCESS, STATUS_ASSUMED } status;
+
+ uint8_t manufacturer_id; /**< Raw manufacturer ID */
+ uint8_t device_id; /**< Raw device ID */
+ uint32_t tsize_bytes; /**< Total size of flash, in bytes */
+ uint32_t psize_bytes; /**< Flash page size, in bytes */
+ uint32_t ebsize_bytes; /**< Flash erase block size, in bytes */
+ uint32_t num_pages; /**< Size of flash, in pages */
+ uint32_t num_ebs; /**< Size of flash, in erase blocks */
+};
+
+/* Boards */
+extern const struct board_fns *bladerf_boards[];
+extern const unsigned int bladerf_boards_len;
+
+#endif
diff --git a/Radio/HW/BladeRF/src/device_calibration.c b/Radio/HW/BladeRF/src/device_calibration.c
new file mode 100644
index 0000000..4495671
--- /dev/null
+++ b/Radio/HW/BladeRF/src/device_calibration.c
@@ -0,0 +1,522 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2023 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include "libbladeRF.h"
+#include "board/board.h"
+#include "helpers/version.h"
+#include "device_calibration.h"
+#include "log.h"
+#include "common.h"
+
+#define GAIN_CAL_HEADER_RX "RX Chain,RX Gain,VSG Power into bladeRF RX (dBm),Frequency of signal (Hz),Frequency of bladeRF+PXI (Hz),AD9361 RSSI register value,Power of Signal from Full Scale (dBFS)\0"
+#define GAIN_CAL_HEADER_TX "TX Chain,TX Gain,Frequency of Signal (Hz),Frequency of bladeRF+PXI (Hz),VSA Measured Power (dBm)\0"
+
+#define GAIN_CAL_VERSION (struct bladerf_version) { \
+ .describe = "gain calibration table", \
+ .major = 1, \
+ .minor = 0, \
+ .patch = 0, \
+}
+
+#define __round_int(x) (x >= 0 ? (int)(x + 0.5) : (int)(x - 0.5))
+
+#define RETURN_ERROR_STATUS(_what, _status) \
+ do { \
+ log_error("%s: %s failed: %s\n", __FUNCTION__, _what, \
+ bladerf_strerror(_status)); \
+ return _status; \
+ } while (0)
+
+#define CHECK_STATUS(_fn) \
+ do { \
+ int _s = _fn; \
+ if (_s < 0) { \
+ RETURN_ERROR_STATUS(#_fn, _s); \
+ } \
+ } while (0)
+
+static size_t count_csv_entries(const char *filename) {
+ char line[1024]; // Adjust buffer size as needed
+ int count = 0;
+
+ FILE *file = fopen(filename, "r");
+ if (file == NULL) {
+ perror("Error opening file");
+ return -1;
+ }
+
+ while (fgets(line, sizeof(line), file)) {
+ count++;
+ }
+
+ fclose(file);
+
+ return count - 2; // Subtract 2 to account for the serial number and header lines
+}
+
+int gain_cal_csv_to_bin(struct bladerf *dev, const char *csv_path, const char *binary_path, bladerf_channel ch)
+{
+ int status = 0;
+ struct bladerf_image *image;
+ size_t data_size;
+ size_t entry_size;
+ size_t offset = 0;
+
+ char line[256];
+ char current_dir[1000];
+ char expected_header[1024];
+ char device_serial[BLADERF_SERIAL_LENGTH];
+ char csv_serial[BLADERF_SERIAL_LENGTH];
+
+ uint64_t frequency;
+ float power;
+ uint64_t cw_freq;
+ uint8_t chain;
+ bladerf_gain gain;
+ int32_t rssi;
+ float vsg_power;
+ uint64_t signal_freq;
+
+ FILE *csvFile = fopen(csv_path, "r");
+ FILE *binaryFile = fopen(binary_path, "wb");
+ if (!csvFile || !binaryFile) {
+ status = BLADERF_ERR_NO_FILE;
+ if (getcwd(current_dir, sizeof(current_dir)) != NULL) {
+ log_error("Error opening calibration file: %s\n", strcat(current_dir, csv_path));
+ } else {
+ log_error("Error opening calibration file\n");
+ }
+ goto error;
+ }
+
+ strncpy(device_serial, dev->ident.serial, BLADERF_SERIAL_LENGTH);
+ device_serial[BLADERF_SERIAL_LENGTH - 1] = '\0';
+
+ if (!fgets(line, sizeof(line), csvFile)) {
+ status = BLADERF_ERR_INVAL;
+ log_error("Error reading serial number from CSV file or file is empty.\n");
+ goto error;
+ }
+
+ sscanf(line, "Serial: %s", csv_serial);
+ if (strcmp(device_serial, csv_serial) != 0) {
+ log_warning("Gain calibration file serial (%s) does not match device serial (%s)\n", csv_serial, device_serial);
+ }
+
+ size_t num_entries = count_csv_entries(csv_path);
+ if (num_entries == 0) {
+ status = BLADERF_ERR_INVAL;
+ log_error("Error reading header from CSV file or file is empty.\n");
+ goto error;
+ }
+
+ entry_size = (BLADERF_CHANNEL_IS_TX(ch))
+ ? sizeof(chain) + sizeof(gain) + sizeof(cw_freq) + sizeof(frequency) + sizeof(power)
+ : sizeof(chain) + sizeof(gain) + sizeof(vsg_power) + sizeof(signal_freq) + sizeof(frequency) + sizeof(rssi) + sizeof(power);
+
+ data_size = num_entries * entry_size + BLADERF_SERIAL_LENGTH;
+
+ image = bladerf_alloc_image(dev, BLADERF_IMAGE_TYPE_GAIN_CAL, 0xffffffff, data_size);
+ if (image == NULL) {
+ log_error("Failed to allocate image\n");
+ status = BLADERF_ERR_MEM;
+ goto error;
+ }
+
+ if (!fgets(line, sizeof(line), csvFile)) {
+ status = BLADERF_ERR_INVAL;
+ log_error("Error reading header from CSV file or file is empty.\n");
+ goto error;
+ }
+
+ strncpy(expected_header, (BLADERF_CHANNEL_IS_TX(ch)) ? GAIN_CAL_HEADER_TX : GAIN_CAL_HEADER_RX, sizeof(expected_header));
+ if (strncmp(line, expected_header, strlen(expected_header)) != 0) {
+ status = BLADERF_ERR_INVAL;
+ log_error("CSV format does not match expected %s headers\n", (BLADERF_CHANNEL_IS_TX(ch)) ? "TX" : "RX");
+ goto error;
+ }
+
+ image->version = GAIN_CAL_VERSION;
+
+ memcpy(&image->data[offset], device_serial, BLADERF_SERIAL_LENGTH);
+ offset += BLADERF_SERIAL_LENGTH;
+
+ for (size_t i = 0; i < num_entries; i++) {
+ if (!fgets(line, sizeof(line), csvFile)) {
+ break;
+ }
+
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ sscanf(line, "%" SCNu8 ",%" SCNi32 ",%" SCNu64 ",%" SCNu64 ",%f",
+ &chain, &gain, &cw_freq, &frequency, &power);
+
+ memcpy(&image->data[offset], &chain, sizeof(chain));
+ offset += sizeof(chain);
+ memcpy(&image->data[offset], &gain, sizeof(gain));
+ offset += sizeof(gain);
+ memcpy(&image->data[offset], &cw_freq, sizeof(cw_freq));
+ offset += sizeof(cw_freq);
+ memcpy(&image->data[offset], &frequency, sizeof(frequency));
+ offset += sizeof(frequency);
+ memcpy(&image->data[offset], &power, sizeof(power));
+ offset += sizeof(power);
+ } else {
+ sscanf(line, "%" SCNu8 ",%" SCNi32 ",%f,%" SCNu64 ",%" SCNu64 ",%" SCNi32 ",%f",
+ &chain, &gain, &vsg_power, &signal_freq, &frequency, &rssi, &power);
+
+ memcpy(&image->data[offset], &chain, sizeof(chain));
+ offset += sizeof(chain);
+ memcpy(&image->data[offset], &gain, sizeof(gain));
+ offset += sizeof(gain);
+ memcpy(&image->data[offset], &vsg_power, sizeof(vsg_power));
+ offset += sizeof(vsg_power);
+ memcpy(&image->data[offset], &signal_freq, sizeof(signal_freq));
+ offset += sizeof(signal_freq);
+ memcpy(&image->data[offset], &frequency, sizeof(frequency));
+ offset += sizeof(frequency);
+ memcpy(&image->data[offset], &rssi, sizeof(rssi));
+ offset += sizeof(rssi);
+ memcpy(&image->data[offset], &power, sizeof(power));
+ offset += sizeof(power);
+ }
+ }
+
+ log_debug("Writing image to file: %s\n", binary_path);
+ bladerf_image_write(dev, image, binary_path);
+ bladerf_free_image(image);
+
+error:
+ if (csvFile)
+ fclose(csvFile);
+ if (binaryFile)
+ fclose(binaryFile);
+ return status;
+}
+
+static int gain_cal_tbl_init(struct bladerf_gain_cal_tbl *tbl, uint32_t num_entries) {
+ if (tbl == NULL) {
+ log_error("calibration table is NULL\n");
+ return BLADERF_ERR_MEM;
+ }
+
+ tbl->version = (struct bladerf_version){0, 0, 0, NULL};
+ tbl->n_entries = num_entries;
+ tbl->start_freq = 0;
+ tbl->stop_freq = 0;
+ tbl->file_path_len = PATH_MAX;
+
+ tbl->entries = malloc(num_entries * sizeof(struct bladerf_gain_cal_entry));
+ if (tbl->entries == NULL) {
+ log_error("failed to allocate memory for calibration table entries\n");
+ return BLADERF_ERR_MEM;
+ }
+
+ tbl->file_path = malloc(tbl->file_path_len + 1);
+ if (tbl->file_path == NULL) {
+ log_error("failed to allocate memory for calibration table file path\n");
+ return BLADERF_ERR_MEM;
+ }
+
+ tbl->state = BLADERF_GAIN_CAL_LOADED;
+ return 0;
+}
+
+void gain_cal_tbl_free(struct bladerf_gain_cal_tbl *tbl) {
+ log_verbose("Freeing gain calibration table\n");
+
+ if (tbl->entries != NULL) {
+ free(tbl->entries);
+ tbl->entries = NULL;
+ }
+
+ if (tbl->file_path != NULL) {
+ free(tbl->file_path);
+ tbl->file_path = NULL;
+ }
+
+ tbl->version = (struct bladerf_version){0, 0, 0, NULL};
+ tbl->enabled = false;
+ tbl->ch = 0;
+ tbl->n_entries = 0;
+ tbl->start_freq = 0;
+ tbl->stop_freq = 0;
+ tbl->gain_target = 0;
+ tbl->file_path_len = 0;
+ tbl->state = BLADERF_GAIN_CAL_UNLOADED;
+}
+
+int load_gain_calibration(struct bladerf *dev, bladerf_channel ch, const char *binary_path) {
+ int num_channels = 4;
+ struct bladerf_gain_cal_tbl gain_tbls[num_channels];
+ bladerf_gain current_gain;
+ uint64_t frequency;
+ float power;
+ size_t entry_counter;
+ size_t offset;
+ int status = 0;
+
+ uint64_t cw_freq;
+ uint8_t chain;
+ bladerf_gain gain;
+ int32_t rssi;
+ float vsg_power;
+ bladerf_frequency signal_freq;
+
+ struct bladerf_image *image = NULL;
+ size_t entry_size;
+ size_t num_entries;
+ char device_serial[BLADERF_SERIAL_LENGTH];
+ char file_serial[BLADERF_SERIAL_LENGTH];
+
+ FILE *binaryFile = fopen(binary_path, "rb");
+ if (!binaryFile) {
+ log_error("Error opening binary file.\n");
+ status = BLADERF_ERR_NO_FILE;
+ goto error;
+ }
+
+ status = dev->board->get_gain(dev, ch, &current_gain);
+ if (status != 0) {
+ log_error("Failed to get gain: %s\n", bladerf_strerror(status));
+ goto error;
+ }
+
+ status = gain_cal_tbl_init(&gain_tbls[ch], (uint32_t) 10e3);
+ if (status != 0) {
+ log_error("Error initializing gain calibration table\n");
+ status = BLADERF_ERR_MEM;
+ goto error;
+ }
+
+ entry_size = (BLADERF_CHANNEL_IS_TX(ch))
+ ? sizeof(chain) + sizeof(gain) + sizeof(cw_freq) + sizeof(frequency) + sizeof(power)
+ : sizeof(chain) + sizeof(gain) + sizeof(vsg_power) + sizeof(signal_freq) + sizeof(frequency) + sizeof(rssi) + sizeof(power);
+
+ image = bladerf_alloc_image(dev, BLADERF_IMAGE_TYPE_GAIN_CAL, 0, 0);
+ status = bladerf_image_read(image, binary_path);
+ if (status != 0) {
+ log_error("Failed to read image: %s\n", bladerf_strerror(status));
+ goto error;
+ }
+
+ if (version_equal(&image->version, &GAIN_CAL_VERSION) == false) {
+ log_error("Expected gain calibration table: v%i.%i.%i\n",
+ GAIN_CAL_VERSION.major, GAIN_CAL_VERSION.minor, GAIN_CAL_VERSION.patch);
+ log_error("Imported gain calibration table: v%i.%i.%i\n",
+ image->version.major, image->version.minor, image->version.patch);
+ status = BLADERF_ERR_INVAL;
+ goto error;
+ }
+
+ strncpy(device_serial, dev->ident.serial, BLADERF_SERIAL_LENGTH);
+ device_serial[BLADERF_SERIAL_LENGTH - 1] = '\0';
+ memcpy(file_serial, image->data, BLADERF_SERIAL_LENGTH);
+ file_serial[BLADERF_SERIAL_LENGTH - 1] = '\0';
+
+ if (strcmp(device_serial, file_serial) != 0) {
+ log_warning("Calibration file serial (%s) does not match device serial (%s)\n", file_serial, device_serial);
+ }
+
+ offset = BLADERF_SERIAL_LENGTH;
+ entry_counter = 0;
+ num_entries = (image->length - BLADERF_SERIAL_LENGTH) / entry_size;
+ for (uint64_t i = 0; i < num_entries; i++) {
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ memcpy(&chain, &image->data[offset], sizeof(chain));
+ offset += sizeof(chain);
+ memcpy(&gain, &image->data[offset], sizeof(gain));
+ offset += sizeof(gain);
+ memcpy(&cw_freq, &image->data[offset], sizeof(cw_freq));
+ offset += sizeof(cw_freq);
+ memcpy(&frequency, &image->data[offset], sizeof(frequency));
+ offset += sizeof(frequency);
+ memcpy(&power, &image->data[offset], sizeof(power));
+ offset += sizeof(power);
+ } else {
+ memcpy(&chain, &image->data[offset], sizeof(chain));
+ offset += sizeof(chain);
+ memcpy(&gain, &image->data[offset], sizeof(gain));
+ offset += sizeof(gain);
+ memcpy(&vsg_power, &image->data[offset], sizeof(vsg_power));
+ offset += sizeof(vsg_power);
+ memcpy(&signal_freq, &image->data[offset], sizeof(signal_freq));
+ offset += sizeof(signal_freq);
+ memcpy(&frequency, &image->data[offset], sizeof(frequency));
+ offset += sizeof(frequency);
+ memcpy(&rssi, &image->data[offset], sizeof(rssi));
+ offset += sizeof(rssi);
+ memcpy(&power, &image->data[offset], sizeof(power));
+ offset += sizeof(power);
+ }
+
+ if (BLADERF_CHANNEL_IS_TX(ch) && chain == 0 && gain == 60) {
+ gain_tbls[ch].entries[entry_counter].freq = frequency;
+ gain_tbls[ch].entries[entry_counter].gain_corr = power;
+ entry_counter++;
+ }
+
+ if (!BLADERF_CHANNEL_IS_TX(ch) && chain == 0 && gain == 0) {
+ gain_tbls[ch].entries[entry_counter].freq = frequency;
+ gain_tbls[ch].entries[entry_counter].gain_corr = power - vsg_power;
+ entry_counter++;
+ }
+ }
+
+ if (entry_counter == 0) {
+ log_error("No valid entries found: %s\n", binary_path);
+ status = BLADERF_ERR_UNEXPECTED;
+ goto error;
+ }
+
+ gain_tbls[ch].version = image->version;
+ gain_tbls[ch].start_freq = gain_tbls[ch].entries[0].freq;
+ gain_tbls[ch].stop_freq = gain_tbls[ch].entries[entry_counter-1].freq;
+ gain_tbls[ch].n_entries = entry_counter;
+ gain_tbls[ch].ch = ch;
+ gain_tbls[ch].state = BLADERF_GAIN_CAL_LOADED;
+ gain_tbls[ch].enabled = true;
+ gain_tbls[ch].gain_target = current_gain;
+ strncpy(gain_tbls[ch].file_path, binary_path, gain_tbls[ch].file_path_len);
+
+ gain_cal_tbl_free(&dev->gain_tbls[ch]);
+ dev->gain_tbls[ch] = gain_tbls[ch];
+
+error:
+ if (status != 0) {
+ log_error("binary_path: %s\n", binary_path);
+ }
+
+ if (binaryFile)
+ fclose(binaryFile);
+ if (image)
+ bladerf_free_image(image);
+ return status;
+}
+
+static void find_floor_ceil_entries_by_frequency(const struct bladerf_gain_cal_tbl *tbl, bladerf_frequency freq,
+ struct bladerf_gain_cal_entry **floor, struct bladerf_gain_cal_entry **ceil) {
+ int mid = 0;
+ *floor = NULL;
+ *ceil = NULL;
+
+ if (tbl == NULL || tbl->entries == NULL || tbl->n_entries == 0) {
+ return;
+ }
+
+ int32_t low = 0;
+ int32_t high = tbl->n_entries - 1;
+
+ /* Binary search for the entry with the closest frequency to 'freq' */
+ while (low <= high && high >= 0) {
+ mid = (low + high) / 2;
+ if (tbl->entries[mid].freq == freq) {
+ *floor = &tbl->entries[mid];
+ *ceil = &tbl->entries[mid];
+ return;
+ } else if (tbl->entries[mid].freq < freq) {
+ low = mid + 1;
+ } else {
+ high = mid - 1;
+ }
+ }
+
+ /* At this point, 'low' points to the first entry greater than 'freq',
+ and 'high' points to the last entry less than 'freq'. */
+ if ((uint32_t)low < tbl->n_entries) {
+ *ceil = &tbl->entries[low];
+ }
+
+ /* If 'high' is negative, then there are no entries less than 'freq' */
+ *floor = (high >= 0) ? &tbl->entries[high] : &tbl->entries[0];
+}
+
+int get_gain_cal_entry(const struct bladerf_gain_cal_tbl *tbl, bladerf_frequency freq, struct bladerf_gain_cal_entry *result) {
+ struct bladerf_gain_cal_entry *floor_entry, *ceil_entry;
+
+ if (tbl == NULL || result == NULL) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ find_floor_ceil_entries_by_frequency(tbl, freq, &floor_entry, &ceil_entry);
+ if (!floor_entry || !ceil_entry) {
+ log_error("Could not find ceil or floor entries in the calibration table\n");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ if (floor_entry->freq == ceil_entry->freq) {
+ result->freq = freq;
+ result->gain_corr = floor_entry->gain_corr;
+ return 0;
+ }
+
+ double interpolated_gain_corr = floor_entry->gain_corr +
+ (freq - floor_entry->freq) *
+ (ceil_entry->gain_corr - floor_entry->gain_corr) /
+ (ceil_entry->freq - floor_entry->freq);
+
+ result->freq = freq;
+ result->gain_corr = interpolated_gain_corr;
+ return 0;
+}
+
+int get_gain_correction(struct bladerf *dev, bladerf_frequency freq, bladerf_channel ch, bladerf_gain *compensated_gain) {
+ int status = 0;
+ struct bladerf_gain_cal_tbl *cal_table = &dev->gain_tbls[ch];
+ struct bladerf_gain_cal_entry entry_next;
+
+ CHECK_STATUS(get_gain_cal_entry(cal_table, freq, &entry_next));
+
+ *compensated_gain = __round_int(cal_table->gain_target - entry_next.gain_corr);
+
+ log_verbose("Target gain: %i, Compen. gain: %i\n", dev->gain_tbls[ch].gain_target, *compensated_gain);
+ return status;
+}
+
+int apply_gain_correction(struct bladerf *dev, bladerf_channel ch, bladerf_frequency frequency) {
+ struct bladerf_range const *gain_range = NULL;
+ bladerf_frequency current_frequency;
+ bladerf_gain gain_compensated;
+
+ if (dev->gain_tbls[ch].enabled == false) {
+ log_error("Gain compensation disabled. Can't apply gain correction.\n");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ CHECK_STATUS(dev->board->get_gain_range(dev, ch, &gain_range));
+ CHECK_STATUS(dev->board->get_frequency(dev, ch, &current_frequency));
+ CHECK_STATUS(get_gain_correction(dev, frequency, ch, &gain_compensated));
+
+ if (gain_compensated > gain_range->max || gain_compensated < gain_range->min) {
+ log_warning("Power compensated gain out of range [%i:%i]: %i\n",
+ gain_range->min, gain_range->max, gain_compensated);
+ gain_compensated = (gain_compensated > gain_range->max) ? gain_range->max : gain_range->min;
+ log_warning("Gain clamped to: %i\n", gain_compensated);
+ }
+
+ CHECK_STATUS(dev->board->set_gain(dev, ch, gain_compensated););
+
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/src/devinfo.c b/Radio/HW/BladeRF/src/devinfo.c
new file mode 100644
index 0000000..cce70f8
--- /dev/null
+++ b/Radio/HW/BladeRF/src/devinfo.c
@@ -0,0 +1,420 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+
+#include "libbladeRF.h"
+
+#include "rel_assert.h"
+
+#include "devinfo.h"
+#include "conversions.h"
+#include "log.h"
+
+/******************************************************************************/
+/* Device List Probe */
+/******************************************************************************/
+
+int probe(backend_probe_target target_device, struct bladerf_devinfo **devices)
+{
+ int ret;
+ size_t num_devices;
+ struct bladerf_devinfo *devices_local;
+ int status;
+
+ status = backend_probe(target_device, &devices_local, &num_devices);
+
+ if (status < 0) {
+ ret = status;
+ } else {
+ assert(num_devices <= INT_MAX);
+ ret = (int)num_devices;
+ *devices = devices_local;
+ }
+
+ return ret;
+}
+
+int bladerf_get_device_list(struct bladerf_devinfo **devices)
+{
+ return probe(BACKEND_PROBE_BLADERF, devices);
+}
+
+void bladerf_free_device_list(struct bladerf_devinfo *devices)
+{
+ /* Admittedly, we could just have the user call free() directly,
+ * but this creates a 1:1 pair of calls, and this gives us a spot
+ * to do any additional cleanup here, if ever needed in the future */
+ free(devices);
+}
+
+/******************************************************************************/
+/* Device Information Helpers */
+/******************************************************************************/
+
+bool bladerf_instance_matches(const struct bladerf_devinfo *a,
+ const struct bladerf_devinfo *b)
+{
+ return a->instance == DEVINFO_INST_ANY ||
+ b->instance == DEVINFO_INST_ANY ||
+ a->instance == b->instance;
+}
+
+bool bladerf_serial_matches(const struct bladerf_devinfo *a,
+ const struct bladerf_devinfo *b)
+{
+ /* User specified a "Any serial number" so just report a match */
+ const bool wildcard_match = !strcmp(a->serial, DEVINFO_SERIAL_ANY) ||
+ !strcmp(b->serial, DEVINFO_SERIAL_ANY);
+
+ if (wildcard_match) {
+ return true;
+ } else {
+ /* The user-supplied serial number matches the a subset of the
+ * entire serial number, starting at the beginning.
+ *
+ * i.e., "abc01234" can be used to match "abc0123456789def..."
+ */
+ bool subset_match = (strstr(a->serial, b->serial) == a->serial) ||
+ (strstr(b->serial, a->serial) == b->serial);
+
+ return subset_match;
+ }
+}
+
+bool bladerf_bus_addr_matches(const struct bladerf_devinfo *a,
+ const struct bladerf_devinfo *b)
+{
+ bool bus_match, addr_match;
+
+ bus_match = a->usb_bus == DEVINFO_BUS_ANY ||
+ b->usb_bus == DEVINFO_BUS_ANY ||
+ a->usb_bus == b->usb_bus;
+
+ addr_match = a->usb_addr == DEVINFO_BUS_ANY ||
+ b->usb_addr == DEVINFO_BUS_ANY ||
+ a->usb_addr == b->usb_addr;
+
+ return bus_match && addr_match;
+}
+
+int bladerf_devinfo_list_init(struct bladerf_devinfo_list *list)
+{
+ int status = 0;
+
+ list->num_elt = 0;
+ list->backing_size = 5;
+
+ list->elt = malloc(list->backing_size * sizeof(struct bladerf_devinfo));
+
+ if (!list->elt) {
+ status = BLADERF_ERR_MEM;
+ }
+
+ return status;
+}
+
+int bladerf_devinfo_list_add(struct bladerf_devinfo_list *list,
+ struct bladerf_devinfo *info)
+{
+ int status = 0;
+ struct bladerf_devinfo *info_tmp;
+
+ if (list->num_elt >= list->backing_size) {
+ info_tmp = realloc(list->elt, list->backing_size * 2 * sizeof(*list->elt));
+ if (!info_tmp) {
+ status = BLADERF_ERR_MEM;
+ } else {
+ list->elt = info_tmp;
+ list->backing_size = list->backing_size * 2;
+ }
+ }
+
+ if (status == 0) {
+ memcpy(&list->elt[list->num_elt], info, sizeof(*info));
+ list->num_elt++;
+ }
+
+ return status;
+}
+
+void bladerf_init_devinfo(struct bladerf_devinfo *info)
+{
+ info->backend = BLADERF_BACKEND_ANY;
+
+ memset(info->serial, 0, BLADERF_SERIAL_LENGTH);
+ strncpy(info->serial, DEVINFO_SERIAL_ANY, BLADERF_SERIAL_LENGTH - 1);
+
+ info->usb_bus = DEVINFO_BUS_ANY;
+ info->usb_addr = DEVINFO_ADDR_ANY;
+ info->instance = DEVINFO_INST_ANY;
+
+ memset(info->manufacturer, 0, BLADERF_DESCRIPTION_LENGTH);
+ strncpy(info->manufacturer, "<unknown>", BLADERF_DESCRIPTION_LENGTH - 1);
+
+ memset(info->product, 0, BLADERF_DESCRIPTION_LENGTH);
+ strncpy(info->product, "<unknown>", BLADERF_DESCRIPTION_LENGTH - 1);
+}
+
+bool bladerf_devinfo_matches(const struct bladerf_devinfo *a,
+ const struct bladerf_devinfo *b)
+{
+ return bladerf_instance_matches(a, b) &&
+ bladerf_serial_matches(a, b) &&
+ bladerf_bus_addr_matches(a ,b);
+}
+
+bool bladerf_devstr_matches(const char *dev_str,
+ struct bladerf_devinfo *info)
+{
+ int status;
+ bool ret;
+ struct bladerf_devinfo from_str;
+
+ status = str2devinfo(dev_str, &from_str);
+ if (status < 0) {
+ ret = false;
+ log_debug("Failed to parse device string: %s\n",
+ bladerf_strerror(status));
+ } else {
+ ret = bladerf_devinfo_matches(&from_str, info);
+ }
+
+ return ret;
+}
+
+int bladerf_get_devinfo_from_str(const char *devstr,
+ struct bladerf_devinfo *info)
+{
+ return str2devinfo(devstr, info);
+}
+
+/******************************************************************************/
+/* str2devinfo */
+/******************************************************************************/
+
+#define DELIM_SPACE " \t\r\n\v\f"
+
+static int handle_backend(char *str, struct bladerf_devinfo *d)
+{
+ char *str_end;
+
+ if (!str || strlen(str) == 0) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Gobble up any leading whitespace */
+ while (*str && isspace((unsigned char) *str)) {
+ str++;
+ };
+
+ /* Likewise for trailing whitespace */
+ str_end = str + strlen(str) - 1;
+ while (str_end > str && isspace((unsigned char) *str_end)) { str_end--; };
+ str_end[1] = '\0';
+
+ return str2backend(str, &d->backend);
+}
+
+static int handle_device(struct bladerf_devinfo *d, char *value)
+{
+ int status = BLADERF_ERR_INVAL;
+ bool bus_ok, addr_ok;
+ char *bus = value;
+ char *addr = strchr(value, ':');
+
+ if (addr && addr[1] != '\0') {
+ /* Null-terminate bus and set addr to start of addr text */
+ *addr = '\0';
+ addr++;
+
+ d->usb_bus = str2uint(bus, 0, DEVINFO_BUS_ANY - 1, &bus_ok);
+ d->usb_addr = str2uint(addr, 0, DEVINFO_ADDR_ANY - 1, &addr_ok);
+
+ if (bus_ok && addr_ok) {
+ status = 0;
+ log_debug("Device: %d:%d\n", d->usb_bus, d->usb_addr);
+ } else {
+ log_debug("Bad bus (%s) or address (%s)\n", bus, addr);
+ }
+ }
+
+ return status;
+}
+
+static int handle_instance(struct bladerf_devinfo *d, char *value)
+{
+ bool ok;
+
+ if (value == NULL) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ d->instance = str2uint(value, 0, DEVINFO_INST_ANY - 1, &ok);
+ if (!ok) {
+ log_debug("Bad instance: %s\n", value);
+ return BLADERF_ERR_INVAL;
+ } else {
+ log_debug("Instance: %u\n", d->instance);
+ return 0;
+ }
+}
+
+static int handle_serial(struct bladerf_devinfo *d, char *value)
+{
+ char c;
+ size_t i;
+ size_t len;
+
+ if (value == NULL) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ len = strlen(value);
+ if (len > (BLADERF_SERIAL_LENGTH - 1)) {
+ log_debug("Provided serial # string too long: %"PRIu64"\n",
+ (uint64_t) len);
+
+ return BLADERF_ERR_INVAL;
+ }
+
+ for (i = 0; i < len; i++) {
+ c = value[i];
+ if (c >= 'A' && c <='F') {
+ value[i] = tolower((unsigned char) c);
+ }
+
+ if ((c < 'a' || c > 'f') && (c < '0' || c > '9')) {
+ log_debug("Bad serial: %s\n", value);
+ return BLADERF_ERR_INVAL;
+ }
+ }
+
+ strncpy(d->serial, value, sizeof(d->serial));
+ d->serial[sizeof(d->serial) - 1] = '\0';
+
+ if (len == (BLADERF_SERIAL_LENGTH - 1)) {
+ log_verbose("Requested serial number: %s\n", d->serial);
+ } else {
+ log_verbose("Requested serial number subset: %s\n", d->serial);
+ }
+ return 0;
+}
+
+/* Returns: 1 on arg and value populated
+ * 0 on no args left
+ * BLADERF_ERR_INVAL on bad format
+ */
+
+static int next_arg(char **saveptr, char **arg, char **value)
+{
+ char *saveptr_local;
+ char *token = strtok_r(NULL, DELIM_SPACE, saveptr);
+
+ /* No arguments left */
+ if (!token) {
+ return 0;
+ }
+
+ /* Argument name */
+ *arg = strtok_r(token, "=", &saveptr_local);
+
+ if (!*arg) {
+ *value = NULL;
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Argument value - gobble up the rest of the line*/
+ *value = strtok_r(NULL, "", &saveptr_local);
+
+ if (!*value) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ return 1;
+}
+
+int str2devinfo(const char *dev_id_const, struct bladerf_devinfo *d)
+{
+ char *dev_id = NULL;
+ char *token = NULL;
+ char *arg = NULL;
+ char *val = NULL;
+ char *saveptr = NULL;
+ int status = BLADERF_ERR_UNEXPECTED;
+ int arg_status = BLADERF_ERR_UNEXPECTED;
+
+ assert(d);
+
+ /* Prep our device info before we begin manpulating it, defaulting to
+ * a "wildcard" device indentification */
+ bladerf_init_devinfo(d);
+
+ /* No device indentifier -- pick anything we can find */
+ if (dev_id_const == NULL || strlen(dev_id_const) == 0) {
+ return 0;
+ }
+
+ /* Copy the string so we can butcher it a bit while parsing */
+ dev_id = strdup(dev_id_const);
+ if (!dev_id) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Extract backend */
+ token = strtok_r(dev_id, ":", &saveptr);
+
+ /* We require a valid backend -- args only is not supported */
+ if (token) {
+ status = handle_backend(token, d);
+
+ /* Loop over remainder of string, gathering up args */
+ arg_status = 1;
+ while (arg_status == 1 && status == 0) {
+ arg_status = next_arg(&saveptr, &arg, &val);
+ if (arg_status == 1) {
+
+ /* Handle argument if we can */
+ if (!strcasecmp("device", arg)) {
+ status = handle_device(d, val);
+ } else if (!strcasecmp("instance", arg)) {
+ status = handle_instance(d, val);
+ } else if (!strcasecmp("serial", arg)) {
+ status = handle_serial(d, val);
+ } else {
+ arg_status = BLADERF_ERR_INVAL;
+ }
+ }
+ };
+
+ if (arg_status < 0) {
+ status = arg_status;
+ }
+
+ } else {
+ status = BLADERF_ERR_INVAL;
+ }
+
+ free(dev_id);
+ return status;
+}
diff --git a/Radio/HW/BladeRF/src/devinfo.h b/Radio/HW/BladeRF/src/devinfo.h
new file mode 100644
index 0000000..db1c81b
--- /dev/null
+++ b/Radio/HW/BladeRF/src/devinfo.h
@@ -0,0 +1,131 @@
+/**
+ * @file devinfo.h
+ *
+ * @brief Routines for parsing and handling device identifier
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef DEVINFO_H_
+#define DEVINFO_H_
+
+#include <stddef.h>
+
+/* Reserved values for bladerf_devinfo fields to indicate "undefined" */
+#define DEVINFO_SERIAL_ANY "ANY"
+#define DEVINFO_BUS_ANY UINT8_MAX
+#define DEVINFO_ADDR_ANY UINT8_MAX
+#define DEVINFO_INST_ANY UINT_MAX
+
+struct bladerf_devinfo_list {
+ struct bladerf_devinfo *elt;
+ size_t num_elt; /* Number of elements in the list */
+ size_t backing_size; /* Size of backing array */
+};
+
+#include "backend/backend.h"
+
+int probe(backend_probe_target target_device, struct bladerf_devinfo **devices);
+
+int bladerf_get_device_list(struct bladerf_devinfo **devices);
+
+void bladerf_free_device_list(struct bladerf_devinfo *devices);
+
+/**
+ * Do the device instances for the two provided device info structures match
+ * (taking wildcards into account)?
+ *
+ * @param[in] a Device information to compare
+ * @param[in] b Device information to compare
+ *
+ * @return true on match, false otherwise
+ */
+bool bladerf_instance_matches(const struct bladerf_devinfo *a,
+ const struct bladerf_devinfo *b);
+
+/**
+ * Do the serials for the two provided device info structures match
+ * (taking wildcards into account)?
+ *
+ * @param[in] a Device information to compare
+ * @param[in] b Device information to compare
+ *
+ * @return true on match, false otherwise
+ */
+bool bladerf_serial_matches(const struct bladerf_devinfo *a,
+ const struct bladerf_devinfo *b);
+
+/**
+ * Do the bus and addr for the two provided device info structures match
+ * (taking wildcards into account)?
+ *
+ * @param[in] a Device information to compare
+ * @param[in] b Device information to compare
+ *
+ * @return true on match, false otherwise
+ */
+bool bladerf_bus_addr_matches(const struct bladerf_devinfo *a,
+ const struct bladerf_devinfo *b);
+
+/**
+ * Create list of devinfos
+ *
+ * @param[in] list List of devinfos
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int bladerf_devinfo_list_init(struct bladerf_devinfo_list *list);
+
+/**
+ * Get a pointer to the parent devinfo_list container of a devinfo
+ *
+ * @param[in] devinfo Device info
+ *
+ * @return pointer to container on success, NULL on error
+ */
+struct bladerf_devinfo_list *bladerf_get_devinfo_list(
+ struct bladerf_devinfo *devinfo);
+
+/**
+ * Add an item to our internal devinfo list
+ *
+ * @param[inout] list List to append to
+ * @param[in] info Info to copy into the list
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int bladerf_devinfo_list_add(struct bladerf_devinfo_list *list,
+ struct bladerf_devinfo *info);
+
+/**
+ * Fill out a device info structure based upon the provided device indentifer
+ * string. If a failure occurrs, the contents of d are undefined.
+ *
+ * For device identifier format, see the documentation for bladerf_open
+ * (in include/libbladeRF.h)
+ *
+ * @param[in] device_identifier Device identifier string
+ * @param[out] d Device info to fill in
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int str2devinfo(const char *device_identifier, struct bladerf_devinfo *d);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/driver/dac161s055.c b/Radio/HW/BladeRF/src/driver/dac161s055.c
new file mode 100644
index 0000000..241b4c3
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/dac161s055.c
@@ -0,0 +1,65 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <libbladeRF.h>
+
+#include "rel_assert.h"
+#include "host_config.h"
+#include "log.h"
+
+#include "dac161s055.h"
+
+int dac161s055_write(struct bladerf *dev, uint16_t value)
+{
+ int status;
+
+ /* Ensure device is in write-through mode */
+ status = dev->backend->vctcxo_dac_write(dev, 0x28, 0x0000);
+ if (status < 0) {
+ return status;
+ }
+
+ /* Write DAC value to channel 0 */
+ status = dev->backend->vctcxo_dac_write(dev, 0x08, value);
+ if (status < 0) {
+ return status;
+ }
+
+ log_verbose("%s: Wrote 0x%04x\n", __FUNCTION__, value);
+
+ return 0;
+}
+
+int dac161s055_read(struct bladerf *dev, uint16_t *value)
+{
+ int status;
+
+ /* Read DAC value for channel 0 */
+ status = dev->backend->vctcxo_dac_read(dev, 0x98, value);
+ if (status < 0) {
+ *value = 0;
+ return status;
+ }
+
+ log_verbose("%s: Read 0x%04x\n", __FUNCTION__, *value);
+
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/src/driver/dac161s055.h b/Radio/HW/BladeRF/src/driver/dac161s055.h
new file mode 100644
index 0000000..009827b
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/dac161s055.h
@@ -0,0 +1,51 @@
+/**
+ * @file dac161s055.h
+ *
+ * @brief DAC161S055 Support
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2017777777 LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef DRIVER_DAC161S055_H_
+#define DRIVER_DAC161S055_H_
+
+#include "board/board.h"
+
+/**
+ * Write the output value to the DAC.
+ *
+ * @param dev Device handle
+ * @param[in] value Value
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int dac161s055_write(struct bladerf *dev, uint16_t value);
+
+/**
+ * Read the output value of the DAC.
+ *
+ * @param dev Device handle
+ * @param[out] value Value
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int dac161s055_read(struct bladerf *dev, uint16_t *value);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/driver/fpga_trigger.c b/Radio/HW/BladeRF/src/driver/fpga_trigger.c
new file mode 100644
index 0000000..67818a0
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/fpga_trigger.c
@@ -0,0 +1,196 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2016 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <libbladeRF.h>
+
+#include "log.h"
+
+#include "fpga_trigger.h"
+
+static bool is_valid_signal(bladerf_trigger_signal signal)
+{
+ switch (signal) {
+ case BLADERF_TRIGGER_J71_4:
+ case BLADERF_TRIGGER_J51_1:
+ case BLADERF_TRIGGER_MINI_EXP_1:
+
+ case BLADERF_TRIGGER_USER_0:
+ case BLADERF_TRIGGER_USER_1:
+ case BLADERF_TRIGGER_USER_2:
+ case BLADERF_TRIGGER_USER_3:
+ case BLADERF_TRIGGER_USER_4:
+ case BLADERF_TRIGGER_USER_5:
+ case BLADERF_TRIGGER_USER_6:
+ case BLADERF_TRIGGER_USER_7:
+ return true;
+
+ default:
+ log_debug("Invalid trigger signal: %d\n", signal);
+ return false;
+ }
+}
+
+int fpga_trigger_read(struct bladerf *dev, bladerf_channel ch,
+ bladerf_trigger_signal signal, uint8_t *regval)
+{
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_TX(0))
+ return BLADERF_ERR_INVAL;
+
+ if (!is_valid_signal(signal))
+ return BLADERF_ERR_INVAL;
+
+ return dev->backend->read_trigger(dev, ch, signal, regval);
+}
+
+int fpga_trigger_write(struct bladerf *dev, bladerf_channel ch,
+ bladerf_trigger_signal signal, uint8_t regval)
+{
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_TX(0))
+ return BLADERF_ERR_INVAL;
+
+ if (!is_valid_signal(signal))
+ return BLADERF_ERR_INVAL;
+
+ return dev->backend->write_trigger(dev, ch, signal, regval);
+}
+
+int fpga_trigger_init(struct bladerf *dev, bladerf_channel ch,
+ bladerf_trigger_signal signal,
+ struct bladerf_trigger *trigger)
+
+{
+ int status;
+ uint8_t regval;
+
+ trigger->options = 0;
+
+ status = fpga_trigger_read(dev, ch, signal, &regval);
+ if (status != 0) {
+ trigger->channel = BLADERF_CHANNEL_INVALID;
+ trigger->role = BLADERF_TRIGGER_ROLE_INVALID;
+ trigger->signal = BLADERF_TRIGGER_INVALID;
+ return status;
+ }
+
+ if ((regval & BLADERF_TRIGGER_REG_MASTER) != 0) {
+ trigger->role = BLADERF_TRIGGER_ROLE_MASTER;
+ } else {
+ trigger->role = BLADERF_TRIGGER_ROLE_SLAVE;
+ }
+
+ trigger->channel = ch;
+ trigger->signal = signal;
+
+ return 0;
+}
+
+int fpga_trigger_arm(struct bladerf *dev,
+ const struct bladerf_trigger *trigger, bool arm)
+{
+ int status;
+ uint8_t regval;
+
+ status = fpga_trigger_read(dev, trigger->channel, trigger->signal, &regval);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Reset any previous fire request */
+ regval &= ~BLADERF_TRIGGER_REG_FIRE;
+
+ if (arm) {
+ regval |= BLADERF_TRIGGER_REG_ARM;
+ } else {
+ regval &= ~BLADERF_TRIGGER_REG_ARM;
+ }
+
+ switch (trigger->role) {
+ case BLADERF_TRIGGER_ROLE_MASTER:
+ regval |= BLADERF_TRIGGER_REG_MASTER;
+ break;
+
+ case BLADERF_TRIGGER_ROLE_SLAVE:
+ regval &= ~BLADERF_TRIGGER_REG_MASTER;
+ break;
+
+ case BLADERF_TRIGGER_ROLE_DISABLED:
+ regval = 0;
+ break;
+
+ default:
+ log_debug("Invalid trigger role: %d\n", trigger->role);
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = fpga_trigger_write(dev, trigger->channel, trigger->signal, regval);
+
+ return status;
+}
+
+int fpga_trigger_fire(struct bladerf *dev,
+ const struct bladerf_trigger *trigger)
+{
+ int status;
+ uint8_t regval;
+
+ status = fpga_trigger_read(dev, trigger->channel, trigger->signal, &regval);
+ if (status != 0) {
+ return status;
+ }
+
+ regval |= BLADERF_TRIGGER_REG_FIRE;
+ status = fpga_trigger_write(dev, trigger->channel, trigger->signal, regval);
+
+ return status;
+}
+
+int fpga_trigger_state(struct bladerf *dev, const struct bladerf_trigger *trigger,
+ bool *is_armed, bool *fired, bool *fire_requested)
+{
+ int status;
+ uint8_t regval;
+
+ status = fpga_trigger_read(dev, trigger->channel, trigger->signal, &regval);
+ if (status != 0) {
+ *fired = false;
+ return status;
+ }
+
+ if (is_armed != NULL) {
+ *is_armed = (regval & BLADERF_TRIGGER_REG_ARM) != 0;
+ }
+
+ if (fired != NULL) {
+ /* Signal is active-low */
+ *fired = (regval & BLADERF_TRIGGER_REG_LINE) == 0;
+ }
+
+ if (fire_requested != NULL) {
+ if (trigger->role == BLADERF_TRIGGER_ROLE_MASTER) {
+ *fire_requested = (regval & BLADERF_TRIGGER_REG_FIRE) != 0;
+ } else {
+ *fire_requested = false;
+ }
+ }
+
+ return status;
+}
+
diff --git a/Radio/HW/BladeRF/src/driver/fpga_trigger.h b/Radio/HW/BladeRF/src/driver/fpga_trigger.h
new file mode 100644
index 0000000..8995fed
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/fpga_trigger.h
@@ -0,0 +1,128 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2016 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef DRIVER_FPGA_TRIGGER_H_
+#define DRIVER_FPGA_TRIGGER_H_
+
+#include "board/board.h"
+
+/**
+ * Read trigger control register
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] signal Trigger signal control register to read from
+ * @param[out] val Pointer to variable that register is read into See the
+ * BLADERF_TRIGGER_REG_* macros for the meaning of each
+ * bit.
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int fpga_trigger_read(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t *val);
+
+/**
+ * Write trigger control register
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] signal Trigger signal to configure
+ * @param[in] val Data to write into the trigger control register. See
+ * the BLADERF_TRIGGER_REG_* macros for options.
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int fpga_trigger_write(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal trigger,
+ uint8_t val);
+
+
+/**
+ * Initialize a bladerf_trigger structure based upon the current state
+ * of a channel's trigger control register.
+ *
+ * @param dev Device to query
+ * @param[in] ch Channel
+ * @param[in] signal Trigger signal to query
+ * @param[out] trigger Updated to describe trigger
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int fpga_trigger_init(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_trigger_signal signal,
+ struct bladerf_trigger *trigger);
+
+/**
+ * Arm or re-arm the specified trigger.
+ *
+ * @param dev Device handle
+ * @param[in] trigger Description of trigger to arm
+ * @param[in] arm If true, the specified trigger will be armed. Setting
+ * this to false will disarm the trigger specified in
+ * `config`.
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int fpga_trigger_arm(struct bladerf *dev,
+ const struct bladerf_trigger *trigger,
+ bool arm);
+
+/**
+ * Fire a trigger event.
+ *
+ * Calling this functiona with a trigger whose role is anything other than
+ * ::BLADERF_TRIGGER_REG_MASTER will yield a BLADERF_ERR_INVAL return value.
+ *
+ * @param dev Device handle
+ * @param[in] trigger Trigger to assert
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int fpga_trigger_fire(struct bladerf *dev,
+ const struct bladerf_trigger *trigger);
+
+/**
+ * Query the fire request status of a master trigger
+ *
+ * @param dev Device handle
+ * @param[in] trigger Trigger to query
+ * @param[out] is_armed Set to true if the trigger is armed, and false
+ * otherwise. May be NULL.
+ * @param[out] has_fired Set to true if the trigger has fired, and false
+ * otherwise. May be NULL.
+ * @param[out] fire_requested Only applicable to a trigger master. Set to
+ * true if a fire request has been previously
+ * submitted. May be NULL.
+ * @param[out] resv1 Reserved parameter. Set to NULL.
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int fpga_trigger_state(struct bladerf *dev,
+ const struct bladerf_trigger *trigger,
+ bool *is_armed,
+ bool *has_fired,
+ bool *fire_requested);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/driver/fx3_fw.c b/Radio/HW/BladeRF/src/driver/fx3_fw.c
new file mode 100644
index 0000000..423322b
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/fx3_fw.c
@@ -0,0 +1,343 @@
+/*
+ * This file implements functionality for reading and validating an FX3 firmware
+ * image, and providing access to the image contents.
+ *
+ * Details about the image format can be found and FX3 bootloader can be found
+ * in Cypress AN76405: EZ-USB (R) FX3 (TM) Boot Options:
+ * http://www.cypress.com/?docID=49862
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include <stdint.h>
+
+#include <libbladeRF.h>
+
+#include "rel_assert.h"
+#include "host_config.h"
+#include "log.h"
+
+#include "fx3_fw.h"
+
+#define FX3_IMAGE_TYPE_NORMAL 0xb0 /* "Normal" image with checksum */
+
+#define FX3_HDR_SIG_IDX 0x00
+#define FX3_HDR_IMAGE_CTL_IDX 0x02
+#define FX3_HDR_IMAGE_TYPE_IDX 0x03
+#define FX3_HDR_IMAGE_LEN0_IDX 0x04
+#define FX3_HDR_IMAGE_ADDR0_IDX 0x08
+#define FX3_HDR_IMAGE_DATA0_IDX 0x0c
+
+#define FX3_HDR_LEN FX3_HDR_IMAGE_DATA0_IDX
+
+#define FX3_RAM_SIZE_WORDS (256 * 1024 / sizeof(uint32_t))
+
+struct fx3_firmware {
+ uint8_t *data;
+ uint32_t data_len;
+
+ uint32_t entry_addr;
+
+ uint32_t num_sections;
+ uint32_t curr_section;
+ uint32_t section_offset;
+};
+
+static inline uint32_t to_uint32(struct fx3_firmware *fw, uint32_t offset)
+{
+ uint32_t ret;
+
+ assert((offset + sizeof(uint32_t)) <= fw->data_len);
+
+ memcpy(&ret, &fw->data[offset], sizeof(ret));
+
+ return LE32_TO_HOST(ret);
+}
+
+static inline bool is_valid_fx3_ram_addr(uint32_t addr, uint32_t len) {
+ bool valid = true;
+
+ /* If you're doing something fun, wild, and crazy with the FX3 and your
+ * modifications of linker scripts has changed the firmware entry point,
+ * you'll need to add this compile-time definition to suppress this check.
+ *
+ * One potential improvement here would be to define the I-TCM and SYSMEM
+ * addresses at configuration/compilation-time to ensure they match
+ * what's in the FX3's linker script. The default values are assumed here.
+ */
+# ifndef BLADERF_SUPPRESS_FX3_FW_ENTRY_POINT_CHECK
+ const uint32_t itcm_base = 0x00000000;
+ const uint32_t itcm_len = 0x4000;
+ const uint32_t itcm_end = itcm_base + itcm_len;
+
+ const uint32_t sysmem_base = 0x40000000;
+ const uint32_t sysmem_len = 0x80000;
+ const uint32_t sysmem_end = sysmem_base + sysmem_len;
+
+ const bool in_itcm = (addr < itcm_end) &&
+ (len <= itcm_len) &&
+ ((addr + len) < itcm_end);
+
+ const bool in_sysmem = (addr >= sysmem_base) &&
+ (addr < sysmem_end) &&
+ (len <= sysmem_len) &&
+ ((addr + len) < sysmem_end);
+
+ /* In lieu of compilers issuing warnings over the fact that the condition
+ * (addr >= itcm_base) is always true, this condition has been removed.
+ *
+ * Instead, an assertion has been added to catch the attention of anyone
+ * making a change to the above itcm_base definition, albeit a *very*
+ * unlikely change to make. */
+ assert(itcm_base == 0); /* (addr >= itcm_base) guaranteed */
+
+ valid = in_itcm || in_sysmem;
+# endif
+
+ return valid;
+}
+
+static int scan_fw_sections(struct fx3_firmware *fw)
+{
+ int status = 0;
+ bool done = false; /* Have we read all the sections? */
+ uint32_t checksum = 0;
+
+ uint32_t offset, i; /* In bytes */
+ uint32_t next_section; /* Section offset in bytes */
+ uint32_t section_len_words; /* FW uses units of 32-bit words */
+ uint32_t section_len_bytes; /* Section length converted to bytes */
+
+ /* Byte offset where the checksum is expected to be */
+ const uint32_t checksum_off = fw->data_len - sizeof(uint32_t);
+
+ /* These assumptions should have been verified earlier */
+ assert(checksum_off > FX3_HDR_IMAGE_DATA0_IDX);
+ assert((checksum_off % 4) == 0);
+
+ offset = FX3_HDR_IMAGE_LEN0_IDX;
+
+ while (!done) {
+
+ /* Fetch the length of the current section */
+ section_len_words = to_uint32(fw, offset);
+
+ if (section_len_words > FX3_RAM_SIZE_WORDS) {
+ log_debug("Firmware section %u is unexpectedly large.\n",
+ fw->num_sections);
+ status = BLADERF_ERR_INVAL;
+ goto error;
+ } else {
+ section_len_bytes = (uint32_t)(section_len_words * sizeof(uint32_t));
+ offset += sizeof(uint32_t);
+ }
+
+ /* The list of sections is terminated by a 0 section length field */
+ if (section_len_bytes == 0) {
+ fw->entry_addr = to_uint32(fw, offset);
+ if (!is_valid_fx3_ram_addr(fw->entry_addr, 0)) {
+ status = BLADERF_ERR_INVAL;
+ goto error;
+ }
+
+ offset += sizeof(uint32_t);
+ done = true;
+ } else {
+# if LOGGING_ENABLED
+ /* Just a value to print in verbose output */
+ uint32_t section_start_offset = offset - sizeof(uint32_t);
+# endif
+
+ uint32_t addr = to_uint32(fw, offset);
+ if (!is_valid_fx3_ram_addr(addr, section_len_bytes)) {
+ status = BLADERF_ERR_INVAL;
+ goto error;
+ }
+
+ offset += sizeof(uint32_t);
+ if (offset >= checksum_off) {
+ log_debug("Firmware truncated after section address.\n");
+ status = BLADERF_ERR_INVAL;
+ goto error;
+ }
+
+ next_section = offset + section_len_bytes;
+
+ if (next_section >= checksum_off) {
+ log_debug("Firmware truncated in section %u\n",
+ fw->num_sections);
+ status = BLADERF_ERR_INVAL;
+ goto error;
+ }
+
+ for (i = offset; i < next_section; i += sizeof(uint32_t)) {
+ checksum += to_uint32(fw, i);
+ }
+
+ offset = next_section;
+ log_verbose("Scanned section %u at offset 0x%08x: "
+ "addr=0x%08x, len=0x%08x\n",
+ fw->num_sections, section_start_offset,
+ addr, section_len_words);
+
+ fw->num_sections++;
+ }
+ }
+
+ if (offset != checksum_off) {
+ log_debug("Invalid offset or junk at the end of the firmware image.\n");
+ status = BLADERF_ERR_INVAL;
+ } else {
+ const uint32_t expected_checksum = to_uint32(fw, checksum_off);
+
+ if (checksum != expected_checksum) {
+ log_debug("Bad checksum. Expected 0x%08x, got 0x%08x\n",
+ expected_checksum, checksum);
+
+ status = BLADERF_ERR_INVAL;
+ } else {
+ log_verbose("Firmware checksum OK.\n");
+ fw->section_offset = FX3_HDR_IMAGE_LEN0_IDX;
+ }
+ }
+
+error:
+ return status;
+}
+
+int fx3_fw_parse(struct fx3_firmware **fw, uint8_t *buf, size_t buf_len)
+{
+ int status;
+
+ if (buf_len > UINT32_MAX) {
+ /* This is just intended to catch a crazy edge case, since we're casting
+ * to 32-bits below. If this passes, the data length might still be well
+ * over the 512 KiB RAM limit. */
+ log_debug("Size of provided image is too large.\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (buf_len < FX3_HDR_LEN) {
+ log_debug("Provided image is too short.");
+ return BLADERF_ERR_INVAL;
+ }
+
+ if ((buf_len % 4) != 0) {
+ log_debug("Size of provided image is not a multiple of 4 bytes.\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (buf[FX3_HDR_SIG_IDX] != 'C' && buf[FX3_HDR_SIG_IDX + 1] != 'Y') {
+ log_debug("FX3 firmware does have 'CY' marker.\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (buf[3] != FX3_IMAGE_TYPE_NORMAL) {
+ log_debug("FX3 firmware header contained unexpected image type: "
+ "0x%02x\n", buf[FX3_HDR_IMAGE_TYPE_IDX]);
+ return BLADERF_ERR_INVAL;
+ }
+
+ *fw = calloc(1, sizeof(struct fx3_firmware));
+ if (*fw == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ (*fw)->data = malloc(buf_len);
+ if ((*fw)->data == NULL) {
+ free(*fw);
+ return BLADERF_ERR_MEM;
+ }
+
+ memcpy((*fw)->data, buf, buf_len);
+ (*fw)->data_len = (uint32_t)buf_len;
+
+ status = scan_fw_sections(*fw);
+ if (status != 0) {
+ goto error;
+ }
+
+ return 0;
+
+error:
+ fx3_fw_free(*fw);
+ return status;
+}
+
+void fx3_fw_free(struct fx3_firmware *fw)
+{
+ free(fw->data);
+ free(fw);
+}
+
+bool fx3_fw_next_section(struct fx3_firmware *fw, uint32_t *section_addr,
+ uint8_t **section_data, uint32_t *section_len)
+{
+ uint32_t len;
+ uint32_t addr;
+ uint8_t *data;
+
+ /* Max offset is the checksum address */
+ const uint32_t max_offset = fw->data_len - sizeof(uint32_t);
+
+ assert(fw != NULL);
+ assert(fw->data != NULL);
+
+ *section_addr = 0;
+ *section_data = NULL;
+ *section_len = 0;
+
+ if (fw->curr_section >= fw->num_sections) {
+ return false;
+ }
+
+ /* Length in bytes (as converted from 32-bit words) */
+ len = to_uint32(fw, fw->section_offset) * sizeof(uint32_t);
+ if (len == 0) {
+ return false;
+ }
+
+ /* Advance to address field */
+ fw->section_offset += sizeof(uint32_t);
+ assert(fw->section_offset < max_offset);
+ addr = to_uint32(fw, fw->section_offset);
+
+ /* Advance to data field */
+ fw->section_offset += sizeof(uint32_t);
+ assert(fw->section_offset < max_offset);
+ data = &fw->data[fw->section_offset];
+
+ /* Advance to the next section for the next call */
+ fw->section_offset += len;
+ assert(fw->section_offset < max_offset);
+ fw->curr_section++;
+
+ *section_addr = addr;
+ *section_data = data;
+ *section_len = len;
+ return true;
+}
+
+uint32_t fx3_fw_entry_point(const struct fx3_firmware *fw)
+{
+ assert(fw != NULL);
+ return fw->entry_addr;
+}
diff --git a/Radio/HW/BladeRF/src/driver/fx3_fw.h b/Radio/HW/BladeRF/src/driver/fx3_fw.h
new file mode 100644
index 0000000..5e715d5
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/fx3_fw.h
@@ -0,0 +1,79 @@
+/*
+ * This file defines functionality for reading and validating an FX3 firmware
+ * image, and providing access to the image contents.
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef DRIVER_FX3_FW_H_
+#define DRIVER_FX3_FW_H_
+
+#include "host_config.h"
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "board/board.h"
+
+struct fx3_firmware;
+
+/**
+ * Parse the contents of an FX3 firmware file into a fx3_firmware structure.
+ *
+ * @param[out] fw Handle to FX3 firmware data
+ * @param[in] buf Buffer containing a FX3 firmware image
+ * @param[in] buf_len Length of buffer
+ *
+ * @return 0 on success, BLADERF_ERR_INVAL if image validation fails,
+ * BLADERF_ERR_* values on other errors.
+ */
+int fx3_fw_parse(struct fx3_firmware **fw, uint8_t *buf, size_t buf_len);
+
+/**
+ * Free the data stored in the provided fx3_firmware structure.
+ *
+ * @param[inout] fw Structure to deallocate
+ */
+void fx3_fw_free(struct fx3_firmware *fw);
+
+/**
+ * This function allows each section to be iterated over by calling it
+ * repeatedly, until it returns false.
+ *
+ * @param[in] fw Handle FX3 firmware data
+ * @param[out] section_addr Target RAM address of the section (on the FX3)
+ * @param[out] section_data Updated to point to start of next section's data
+ * @parma[out] section_len Length of the next section
+ *
+ * @return true if this function returned section data, false if the end of the
+ * FW has been reached and no data is available.
+ */
+bool fx3_fw_next_section(struct fx3_firmware *fw,
+ uint32_t *section_addr,
+ uint8_t **section_data,
+ uint32_t *section_len);
+
+/**
+ * @param[in] fw Handle FX3 firmware data
+ *
+ * @return The 32-bit little-endian address of the firmware entry point.
+ */
+uint32_t fx3_fw_entry_point(const struct fx3_firmware *fw);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/driver/ina219.c b/Radio/HW/BladeRF/src/driver/ina219.c
new file mode 100644
index 0000000..f3cd23b
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/ina219.c
@@ -0,0 +1,154 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2017 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <libbladeRF.h>
+
+#include "log.h"
+
+#include "ina219.h"
+
+#define INA219_REG_CONFIGURATION 0x00
+#define INA219_REG_SHUNT_VOLTAGE 0x01
+#define INA219_REG_BUS_VOLTAGE 0x02
+#define INA219_REG_POWER 0x03
+#define INA219_REG_CURRENT 0x04
+#define INA219_REG_CALIBRATION 0x05
+
+int ina219_init(struct bladerf *dev, float r_shunt)
+{
+ int status;
+ uint16_t value;
+
+ /* Soft-reset INA219 */
+ value = 0x8000;
+ status = dev->backend->ina219_write(dev, INA219_REG_CONFIGURATION, value);
+ if (status < 0) {
+ log_error("INA219 soft reset error: %d\n", status);
+ return status;
+ }
+
+ /* Poll until we're out of reset */
+ while (value & 0x8000) {
+ status = dev->backend->ina219_read(dev, INA219_REG_CONFIGURATION, &value);
+ if (status < 0) {
+ log_error("INA219 soft reset poll error: %d\n", status);
+ return status;
+ }
+ }
+
+ /* Write configuration register */
+ /* BRNG (13) = 0 for 16V FSR
+ PG (12-11) = 00 for 40mV
+ BADC (10-7) = 0011 for 12-bit / 532uS
+ SADC (6-3) = 0011 for 12-bit / 532uS
+ MODE (2-0) = 111 for continuous shunt & bus */
+ value = 0x019f;
+ status = dev->backend->ina219_write(dev, INA219_REG_CONFIGURATION, value);
+ if (status < 0) {
+ log_error("INA219 configuration error: %d\n", status);
+ return status;
+ }
+
+ log_debug("Configuration register: 0x%04x\n", value);
+
+ /* Write calibration register */
+ /* Current_LSB = 0.001 A / LSB */
+ /* Calibration = 0.04096 / (Current_LSB * r_shunt) */
+ value = (uint16_t)((0.04096 / (0.001 * r_shunt)) + 0.5);
+ status = dev->backend->ina219_write(dev, INA219_REG_CALIBRATION, value);
+ if (status < 0) {
+ log_error("INA219 calibration error: %d\n", status);
+ return status;
+ }
+
+ log_debug("Calibration register: 0x%04x\n", value);
+
+ return 0;
+}
+
+int ina219_read_shunt_voltage(struct bladerf *dev, float *voltage)
+{
+ int status;
+ uint16_t data;
+
+ status = dev->backend->ina219_read(dev, INA219_REG_SHUNT_VOLTAGE, &data);
+ if (status < 0) {
+ return status;
+ }
+
+ /* Scale by 1e-5 LSB / Volt */
+ *voltage = ((float)((int16_t)data)) * 1e-5F;
+
+ return 0;
+}
+
+int ina219_read_bus_voltage(struct bladerf *dev, float *voltage)
+{
+ int status;
+ uint16_t data;
+
+ status = dev->backend->ina219_read(dev, INA219_REG_BUS_VOLTAGE, &data);
+ if (status < 0) {
+ return status;
+ }
+
+ /* If overflow flag is set */
+ if (data & 0x1) {
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* Scale by 0.004 LSB / Volt */
+ *voltage = ((float)(data >> 3)) * 0.004F;
+
+ return 0;
+}
+
+int ina219_read_current(struct bladerf *dev, float *current)
+{
+ int status;
+ uint16_t data;
+
+ status = dev->backend->ina219_read(dev, INA219_REG_CURRENT, &data);
+ if (status < 0) {
+ return status;
+ }
+
+ /* Scale by 0.001 LSB / Ampere */
+ *current = ((float)((int16_t)data)) * 0.001F;
+
+ return 0;
+}
+
+int ina219_read_power(struct bladerf *dev, float *power)
+{
+ int status;
+ uint16_t data;
+
+ status = dev->backend->ina219_read(dev, INA219_REG_POWER, &data);
+ if (status < 0) {
+ return status;
+ }
+
+ /* Scale by 0.020 LSB / Watt */
+ *power = ((float)((int16_t)data)) * 0.020F;
+
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/src/driver/ina219.h b/Radio/HW/BladeRF/src/driver/ina219.h
new file mode 100644
index 0000000..575d0da
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/ina219.h
@@ -0,0 +1,83 @@
+/**
+ * @file ina219.h
+ *
+ * @brief INA219 Support
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2017 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef DRIVER_INA219_H_
+#define DRIVER_INA219_H_
+
+#include <libbladeRF.h>
+
+#include "board/board.h"
+
+/**
+ * Initialize the INA219 voltage/current/power monitor.
+ *
+ * @param dev Device handle
+ * @param[in] r_shunt Shunt resistor in ohms
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int ina219_init(struct bladerf *dev, float r_shunt);
+
+/**
+ * Read the shunt voltage.
+ *
+ * @param dev Device handle
+ * @param[out] voltage Shunt voltage in volts
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int ina219_read_shunt_voltage(struct bladerf *dev, float *voltage);
+
+/**
+ * Read the bus voltage.
+ *
+ * @param dev Device handle
+ * @param[out] voltage Bus voltage in volts
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int ina219_read_bus_voltage(struct bladerf *dev, float *voltage);
+
+/**
+ * Read the load current.
+ *
+ * @param dev Device handle
+ * @param[out] current Load current in amps
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int ina219_read_current(struct bladerf *dev, float *current);
+
+/**
+ * Read the load power.
+ *
+ * @param dev Device handle
+ * @param[out] power Load power in watts
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int ina219_read_power(struct bladerf *dev, float *power);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/driver/si5338.c b/Radio/HW/BladeRF/src/driver/si5338.c
new file mode 100644
index 0000000..80cda11
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/si5338.c
@@ -0,0 +1,669 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include <inttypes.h>
+#include <limits.h>
+
+#include <libbladeRF.h>
+
+#include "rel_assert.h"
+#include "host_config.h"
+#include "log.h"
+
+#include "si5338.h"
+
+#define SI5338_EN_A 0x01
+#define SI5338_EN_B 0x02
+
+#define SI5338_F_VCO (38400000UL * 66UL)
+
+/**
+ * This is used set or recreate the si5338 frequency
+ * Each si5338 multisynth module can be set independently
+ */
+struct si5338_multisynth {
+ /* Multisynth to program (0-3) */
+ uint8_t index;
+
+ /* Base address of the multisynth */
+ uint16_t base;
+
+ /* Requested and actual sample rates */
+ struct bladerf_rational_rate requested;
+ struct bladerf_rational_rate actual;
+
+ /* Enables for A and/or B outputs */
+ uint8_t enable;
+
+ /* f_out = fvco / (a + b/c) / r */
+ uint32_t a, b, c, r;
+
+ /* (a, b, c) in multisynth (p1, p2, p3) form */
+ uint32_t p1, p2, p3;
+
+ /* (p1, p2, p3) in register form */
+ uint8_t regs[10];
+};
+
+static void si5338_log_read_error(int error, const char *s)
+{
+ log_debug("Could not read from si5338 (%d): %s\n", error, s);
+ return;
+}
+
+static void si5338_log_write_error(int error, const char *s)
+{
+ log_debug("Could not write to si5338 (%d): %s\n", error, s);
+ return;
+}
+
+static uint64_t si5338_gcd(uint64_t a, uint64_t b)
+{
+ uint64_t t;
+ while (b != 0) {
+ t = b;
+ b = a % t;
+ a = t;
+ }
+ return a;
+}
+
+static void si5338_rational_reduce(struct bladerf_rational_rate *r)
+{
+ int64_t val;
+
+ if ((r->den > 0) && (r->num >= r->den)) {
+ /* Get whole number */
+ uint64_t whole = r->num / r->den;
+ r->integer += whole;
+ r->num = r->num - whole*r->den;
+ }
+
+ /* Reduce fraction */
+ val = si5338_gcd(r->num, r->den);
+ if (val > 0) {
+ r->num /= val;
+ r->den /= val;
+ }
+
+ return ;
+}
+
+static void si5338_rational_double(struct bladerf_rational_rate *r)
+{
+ r->integer *= 2;
+ r->num *= 2;
+ si5338_rational_reduce(r);
+ return;
+}
+
+/**
+ * Update the base address of the selected multisynth
+ */
+static void si5338_update_base(struct si5338_multisynth *ms)
+{
+ ms->base = 53 + ms->index*11 ;
+ return;
+}
+
+/**
+ * Unpack the recently read registers into (p1, p2, p3) and (a, b, c)
+ *
+ * Precondition:
+ * regs[10] and r have been read
+ *
+ * Post-condition:
+ * (p1, p2, p3), (a, b, c) and actual are populated
+ */
+static void si5338_unpack_regs(struct si5338_multisynth *ms)
+{
+ uint64_t temp;
+
+ /* Zeroize */
+ ms->p1 = ms->p2 = ms->p3 = 0;
+
+ /* Populate */
+ ms->p1 = ((ms->regs[2]&3)<<16) | (ms->regs[1]<<8) | (ms->regs[0]);
+ ms->p2 = (ms->regs[5]<<22) | (ms->regs[4]<<14) | (ms->regs[3]<<6) | ((ms->regs[2]>>2)&0x3f);
+ ms->p3 = ((ms->regs[9]&0x3f)<<24) | (ms->regs[8]<<16) | (ms->regs[7]<<8) | (ms->regs[6]);
+
+ log_verbose("Unpacked P1: 0x%8.8x (%u) P2: 0x%8.8x (%u) P3: 0x%8.8x (%u)\n",
+ ms->p1, ms->p1, ms->p2, ms->p2, ms->p3, ms->p3);
+
+ /* c = p3 */
+ ms->c = ms->p3;
+
+ /* a = (p1+512)/128
+ *
+ * NOTE: The +64 is for rounding purposes.
+ */
+ ms->a = (ms->p1+512)/128;
+
+ /* b = (((p1+512)-128*a)*c + (b % c) + 64)/128 */
+ temp = (ms->p1+512)-128*(uint64_t)ms->a;
+ temp = (temp * ms->c) + ms->p2;
+ temp = (temp + 64) / 128;
+ assert(temp <= UINT32_MAX);
+ ms->b = (uint32_t)temp;
+
+ log_verbose("Unpacked a + b/c: %d + %d/%d\n", ms->a, ms->b, ms->c);
+ log_verbose("Unpacked r: %d\n", ms->r);
+}
+
+/*
+ * Pack (a, b, c, r) into (p1, p2, p3) and regs[]
+ */
+static void si5338_pack_regs(struct si5338_multisynth *ms)
+{
+ /* Precondition:
+ * (a, b, c) and r have been populated
+ *
+ * Post-condition:
+ * (p1, p2, p3) and regs[10] are populated
+ */
+
+ /* p1 = (a * c + b) * 128 / c - 512 */
+ uint64_t temp;
+ temp = (uint64_t)ms->a * ms->c + ms->b;
+ temp = temp * 128 ;
+ temp = temp / ms->c - 512;
+ assert(temp <= UINT32_MAX);
+ ms->p1 = (uint32_t)temp;
+ //ms->p1 = ms->a * ms->c + ms->b;
+ //ms->p1 = ms->p1 * 128;
+ //ms->p1 = ms->p1 / ms->c - 512;
+
+ /* p2 = (b * 128) % c */
+ temp = (uint64_t)ms->b * 128;
+ temp = temp % ms->c;
+ assert(temp <= UINT32_MAX);
+ ms->p2 = (uint32_t)temp;
+
+ /* p3 = c */
+ ms->p3 = ms->c;
+
+ log_verbose("MSx P1: 0x%8.8x (%u) P2: 0x%8.8x (%u) P3: 0x%8.8x (%u)\n",
+ ms->p1, ms->p1, ms->p2, ms->p2, ms->p3, ms->p3);
+
+ /* Regs */
+ ms->regs[0] = ms->p1 & 0xff;
+ ms->regs[1] = (ms->p1 >> 8) & 0xff;
+ ms->regs[2] = ((ms->p2 & 0x3f) << 2) | ((ms->p1 >> 16) & 0x3);
+ ms->regs[3] = (ms->p2 >> 6) & 0xff;
+ ms->regs[4] = (ms->p2 >> 14) & 0xff;
+ ms->regs[5] = (ms->p2 >> 22) & 0xff;
+ ms->regs[6] = ms->p3 & 0xff;
+ ms->regs[7] = (ms->p3 >> 8) & 0xff;
+ ms->regs[8] = (ms->p3 >> 16) & 0xff;
+ ms->regs[9] = (ms->p3 >> 24) & 0xff;
+
+ return ;
+}
+
+static int si5338_write_multisynth(struct bladerf *dev,
+ struct si5338_multisynth *ms)
+{
+ int i, status;
+ uint8_t r_power, r_count, val;
+
+ log_verbose("Writing MS%d\n", ms->index);
+
+ /* Write out the enables */
+ status = dev->backend->si5338_read(dev, 36 + ms->index, &val);
+ if (status < 0) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status;
+ }
+ val |= ms->enable;
+ log_verbose("Wrote enable register: 0x%2.2x\n", val);
+ status = dev->backend->si5338_write(dev, 36 + ms->index, val);
+ if (status < 0) {
+ si5338_log_write_error(status, bladerf_strerror(status));
+ return status;
+ }
+
+ /* Write out the registers */
+ for (i = 0 ; i < 10 ; i++) {
+ status = dev->backend->si5338_write(dev, ms->base + i, *(ms->regs+i));
+ if (status < 0) {
+ si5338_log_write_error(status, bladerf_strerror(status));
+ return status;
+ }
+ log_verbose("Wrote regs[%d]: 0x%2.2x\n", i, *(ms->regs+i));
+ }
+
+ /* Calculate r_power from c_count */
+ r_power = 0;
+ r_count = ms->r >> 1 ;
+ while (r_count > 0) {
+ r_count >>= 1;
+ r_power++;
+ }
+
+ /* Set the r value to the log2(r_count) to match Figure 18 */
+ val = 0xc0;
+ val |= (r_power<<2);
+
+ log_verbose("Wrote r register: 0x%2.2x\n", val);
+
+ status = dev->backend->si5338_write(dev, 31 + ms->index, val);
+ if (status < 0) {
+ si5338_log_write_error(status, bladerf_strerror(status));
+ }
+
+ return status ;
+}
+
+static int si5338_read_multisynth(struct bladerf *dev,
+ struct si5338_multisynth *ms)
+{
+ int i, status;
+ uint8_t val;
+
+ log_verbose("Reading MS%d\n", ms->index);
+
+ /* Read the enable bits */
+ status = dev->backend->si5338_read(dev, 36 + ms->index, &val);
+ if (status < 0) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status ;
+ }
+ ms->enable = val&7;
+ log_verbose("Read enable register: 0x%2.2x\n", val);
+
+ /* Read all of the multisynth registers */
+ for (i = 0; i < 10; i++) {
+ status = dev->backend->si5338_read(dev, ms->base + i, ms->regs+i);
+ if (status < 0) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status;
+ }
+ log_verbose("Read regs[%d]: 0x%2.2x\n", i, *(ms->regs+i));
+ }
+
+ /* Populate the RxDIV value from the register */
+ status = dev->backend->si5338_read(dev, 31 + ms->index, &val);
+ if (status < 0) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status;
+ }
+ /* RxDIV is stored as a power of 2, so restore it on readback */
+ log_verbose("Read r register: 0x%2.2x\n", val);
+ val = (val>>2)&7;
+ ms->r = (1<<val);
+
+ /* Unpack the regs into appropriate values */
+ si5338_unpack_regs(ms) ;
+
+ return 0;
+}
+
+static void si5338_calculate_ms_freq(struct si5338_multisynth *ms,
+ struct bladerf_rational_rate *rate)
+{
+ struct bladerf_rational_rate abc;
+ abc.integer = ms->a;
+ abc.num = ms->b;
+ abc.den = ms->c;
+
+ rate->integer = 0;
+ rate->num = SI5338_F_VCO * abc.den;
+ rate->den = (uint64_t)ms->r*(abc.integer * abc.den + abc.num);
+
+ /* Compensate for doubling of frequency for LMS sampling clocks */
+ if(ms->index == 1 || ms->index == 2) {
+ rate->den *= 2;
+ }
+
+ si5338_rational_reduce(rate);
+
+ log_verbose("Calculated multisynth frequency: %"
+ PRIu64" + %"PRIu64"/%"PRIu64"\n",
+ rate->integer, rate->num, rate->den);
+
+ return;
+}
+
+static int si5338_calculate_multisynth(struct si5338_multisynth *ms,
+ struct bladerf_rational_rate *rate)
+{
+
+ struct bladerf_rational_rate req;
+ struct bladerf_rational_rate abc;
+ uint8_t r_value;
+
+ /* Don't muss with the users data */
+ req = *rate;
+
+ /* Double requested frequency for sample clocks since LMS requires
+ * 2:1 clock:sample rate
+ */
+ if(ms->index == 1 || ms->index == 2) {
+ si5338_rational_double(&req);
+ }
+
+ /* Find a suitable R value */
+ r_value = 1;
+ while (req.integer < 5000000 && r_value < 32) {
+ si5338_rational_double(&req);
+ r_value <<= 1;
+ }
+
+ if (r_value == 32 && req.integer < 5000000) {
+ log_debug("Sample rate requires r > 32\n");
+ return BLADERF_ERR_INVAL;
+ } else {
+ log_verbose("Found r value of: %d\n", r_value);
+ }
+
+ /* Find suitable MS (a, b, c) values */
+ abc.integer = 0;
+ abc.num = SI5338_F_VCO * req.den;
+ abc.den = req.integer * req.den + req.num;
+ si5338_rational_reduce(&abc);
+
+ log_verbose("MSx a + b/c: %"PRIu64" + %"PRIu64"/%"PRIu64"\n",
+ abc.integer, abc.num, abc.den);
+
+ /* Check values to make sure they are OK */
+ if (abc.integer < 8) {
+ switch (abc.integer) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 5:
+ case 7:
+ log_debug("Integer portion too small: %"PRIu64"\n", abc.integer);
+ return BLADERF_ERR_INVAL;
+ }
+ } else if (abc.integer > 567) {
+ log_debug("Integer portion too large: %"PRIu64"\n", abc.integer);
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Loss of precision if num or den are greater than 2^30-1 */
+ while (abc.num > (1<<30) || abc.den > (1<<30) ) {
+ log_debug("Loss of precision in reducing fraction from "
+ "%"PRIu64"/%"PRIu64" to %"PRIu64"/%"PRIu64"\n",
+ abc.num, abc.den, abc.num>>1, abc.den>>1);
+ abc.num >>= 1;
+ abc.den >>= 1;
+ }
+
+ log_verbose("MSx a + b/c: %"PRIu64" + %"PRIu64"/%"PRIu64"\n",
+ abc.integer, abc.num, abc.den);
+
+ /* Set it in the multisynth */
+ assert(abc.integer <= UINT32_MAX);
+ assert(abc.num <= UINT32_MAX);
+ assert(abc.den <= UINT32_MAX);
+ ms->a = (uint32_t)abc.integer;
+ ms->b = (uint32_t)abc.num;
+ ms->c = (uint32_t)abc.den;
+ ms->r = r_value;
+
+ /* Pack the registers */
+ si5338_pack_regs(ms);
+
+ return 0;
+}
+
+/**
+ * Configure a multisynth for either the RX/TX sample clocks (index=1 or 2)
+ * or for the SMB output (index=3).
+ */
+static int si5338_set_rational_multisynth(struct bladerf *dev,
+ uint8_t index, uint8_t channel,
+ struct bladerf_rational_rate *rate,
+ struct bladerf_rational_rate *actual_ret)
+{
+ struct si5338_multisynth ms;
+ struct bladerf_rational_rate req;
+ struct bladerf_rational_rate actual;
+ int status;
+
+ si5338_rational_reduce(rate);
+
+ /* Save off the value */
+ req = *rate;
+
+ /* Setup the multisynth enables and index */
+ ms.index = index;
+ ms.enable = channel;
+
+ /* Update the base address register */
+ si5338_update_base(&ms);
+
+ /* Calculate multisynth values */
+ status = si5338_calculate_multisynth(&ms, &req);
+ if(status != 0) {
+ return status;
+ }
+
+ /* Get the actual rate */
+ si5338_calculate_ms_freq(&ms, &actual);
+ if (actual_ret) {
+ memcpy(actual_ret, &actual, sizeof(*actual_ret));
+ }
+
+ /* Program it to the part */
+ status = si5338_write_multisynth(dev, &ms);
+
+ /* Done */
+ return status ;
+}
+
+
+int si5338_set_rational_sample_rate(struct bladerf *dev, bladerf_channel ch,
+ const struct bladerf_rational_rate *rate,
+ struct bladerf_rational_rate *actual)
+{
+ struct bladerf_rational_rate rate_reduced = *rate;
+ uint8_t index = (ch == BLADERF_CHANNEL_RX(0)) ? 1 : 2;
+ uint8_t channel = SI5338_EN_A;
+
+ /* Enforce minimum sample rate */
+ si5338_rational_reduce(&rate_reduced);
+ if (rate_reduced.integer < BLADERF_SAMPLERATE_MIN) {
+ log_debug("%s: provided sample rate violates minimum\n", __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (ch == BLADERF_CHANNEL_TX(0)) {
+ channel |= SI5338_EN_B;
+ }
+
+ return si5338_set_rational_multisynth(dev, index, channel, &rate_reduced, actual);
+}
+
+int si5338_set_rational_smb_freq(struct bladerf *dev,
+ const struct bladerf_rational_rate *rate,
+ struct bladerf_rational_rate *actual)
+{
+ struct bladerf_rational_rate rate_reduced = *rate;
+
+ /* Enforce minimum and maximum frequencies */
+ si5338_rational_reduce(&rate_reduced);
+
+ if (rate_reduced.integer < BLADERF_SMB_FREQUENCY_MIN) {
+ log_debug("%s: provided SMB freq violates minimum\n", __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ } else if (rate_reduced.integer > BLADERF_SMB_FREQUENCY_MAX) {
+ log_debug("%s: provided SMB freq violates maximum\n", __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ }
+
+ return si5338_set_rational_multisynth(dev, 3, SI5338_EN_A, &rate_reduced, actual);
+}
+
+int si5338_set_sample_rate(struct bladerf *dev, bladerf_channel ch,
+ uint32_t rate, uint32_t *actual)
+{
+ struct bladerf_rational_rate req, act;
+ int status;
+
+ memset(&act, 0, sizeof(act));
+ log_verbose("Setting integer sample rate: %d\n", rate);
+ req.integer = rate;
+ req.num = 0;
+ req.den = 1;
+
+ status = si5338_set_rational_sample_rate(dev, ch, &req, &act);
+
+ if (status == 0 && act.num != 0) {
+ log_info("Non-integer sample rate set from integer sample rate, "
+ "truncating output.\n");
+ }
+
+ assert(act.integer <= UINT32_MAX);
+
+ if (actual) {
+ *actual = (uint32_t)act.integer;
+ }
+ log_verbose("Set actual integer sample rate: %d\n", act.integer);
+
+ return status ;
+}
+
+int si5338_set_smb_freq(struct bladerf *dev, uint32_t rate, uint32_t *actual)
+{
+ struct bladerf_rational_rate req, act;
+ int status;
+
+ memset(&act, 0, sizeof(act));
+ log_verbose("Setting integer SMB frequency: %d\n", rate);
+ req.integer = rate;
+ req.num = 0;
+ req.den = 1;
+
+ status = si5338_set_rational_smb_freq(dev, &req, &act);
+
+ if (status == 0 && act.num != 0) {
+ log_info("Non-integer SMB frequency set from integer frequency, "
+ "truncating output.\n");
+ }
+
+ assert(act.integer <= UINT32_MAX);
+
+ if (actual) {
+ *actual = (uint32_t)act.integer;
+ }
+ log_verbose("Set actual integer SMB frequency: %d\n", act.integer);
+
+ return status;
+}
+
+int si5338_get_rational_sample_rate(struct bladerf *dev, bladerf_channel ch,
+ struct bladerf_rational_rate *rate)
+{
+
+ struct si5338_multisynth ms;
+ int status;
+
+ /* Select the multisynth we want to read */
+ ms.index = (ch == BLADERF_CHANNEL_RX(0)) ? 1 : 2;
+
+ /* Update the base address */
+ si5338_update_base(&ms);
+
+ /* Readback */
+ status = si5338_read_multisynth(dev, &ms);
+
+ if (status) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status;
+ }
+
+ si5338_calculate_ms_freq(&ms, rate);
+
+ return 0;
+}
+
+int si5338_get_rational_smb_freq(struct bladerf *dev,
+ struct bladerf_rational_rate *rate)
+{
+ struct si5338_multisynth ms;
+ int status;
+
+ /* Select MS3 for the SMB output */
+ ms.index = 3;
+ si5338_update_base(&ms);
+
+ status = si5338_read_multisynth(dev, &ms);
+
+ if (status) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status;
+ }
+
+ si5338_calculate_ms_freq(&ms, rate);
+
+ return 0;
+}
+
+int si5338_get_sample_rate(struct bladerf *dev, bladerf_channel ch,
+ unsigned int *rate)
+{
+ struct bladerf_rational_rate actual;
+ int status;
+
+ status = si5338_get_rational_sample_rate(dev, ch, &actual);
+
+ if (status) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status;
+ }
+
+ if (actual.num != 0) {
+ log_debug("Fractional sample rate truncated during integer sample rate"
+ "retrieval\n");
+ }
+
+ assert(actual.integer <= UINT_MAX);
+ *rate = (unsigned int)actual.integer;
+
+ return 0;
+}
+
+int si5338_get_smb_freq(struct bladerf *dev, unsigned int *rate)
+{
+ struct bladerf_rational_rate actual;
+ int status;
+
+ status = si5338_get_rational_smb_freq(dev, &actual);
+
+ if (status) {
+ si5338_log_read_error(status, bladerf_strerror(status));
+ return status;
+ }
+
+ if (actual.num != 0) {
+ log_debug("Fractional SMB frequency truncated during integer SMB"
+ " frequency retrieval\n");
+ }
+
+ assert(actual.integer <= UINT_MAX);
+ *rate = (unsigned int)actual.integer;
+
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/src/driver/si5338.h b/Radio/HW/BladeRF/src/driver/si5338.h
new file mode 100644
index 0000000..331f8c1
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/si5338.h
@@ -0,0 +1,134 @@
+/**
+ * @file si5338.h
+ *
+ * @brief SI5339 Support
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef DRIVER_SI5338_H_
+#define DRIVER_SI5338_H_
+
+#include <libbladeRF.h>
+
+#include "board/board.h"
+
+/**
+ * Set the rational sample rate of the specified channel.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] rate Rational rate requested
+ * @param[out] actual Rational rate actually set
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int si5338_set_rational_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ const struct bladerf_rational_rate *rate,
+ struct bladerf_rational_rate *actual);
+
+/**
+ * Get the rational sample rate of the specified channel.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] rate Rational rate
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int si5338_get_rational_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ struct bladerf_rational_rate *rate);
+
+/**
+ * Set the integral sample rate of the specified channel.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] rate Integral rate requested
+ * @param[out] actual Integral rate actually set
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int si5338_set_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ uint32_t rate,
+ uint32_t *actual);
+
+/**
+ * Get the integral sample rate of the specified channel.
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] rate Integral rate
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int si5338_get_sample_rate(struct bladerf *dev,
+ bladerf_channel ch,
+ unsigned int *rate);
+
+/**
+ * Set the rational frequency of the external SMB port.
+ *
+ * @param dev Device handle
+ * @param[in] rate Rational rate requested
+ * @param[out] actual Rational rate actually set
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int si5338_set_rational_smb_freq(struct bladerf *dev,
+ const struct bladerf_rational_rate *rate,
+ struct bladerf_rational_rate *actual);
+
+/**
+ * Get the rational sample rate of the external SMB port.
+ *
+ * @param dev Device handle
+ * @param[out] rate Rational rate
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int si5338_get_rational_smb_freq(struct bladerf *dev,
+ struct bladerf_rational_rate *rate);
+
+/**
+ * Set the integral sample rate of the external SMB port.
+ *
+ * @param dev Device handle
+ * @param[in] rate Integral rate requested
+ * @param[out] actual Integral rate actually set
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int si5338_set_smb_freq(struct bladerf *dev, uint32_t rate, uint32_t *actual);
+
+/**
+ Get the integral sample rate of the external SMB port.
+ *
+ * @param dev Device handle
+ * @param[out] rate Integral rate
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int si5338_get_smb_freq(struct bladerf *dev, unsigned int *rate);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/driver/smb_clock.c b/Radio/HW/BladeRF/src/driver/smb_clock.c
new file mode 100644
index 0000000..65ccc04
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/smb_clock.c
@@ -0,0 +1,211 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2016 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "log.h"
+
+#include "smb_clock.h"
+#include "driver/si5338.h"
+#include "board/board.h"
+
+struct regvals {
+ uint8_t addr;
+ uint8_t data;
+};
+
+/* Power-on defaults with SMB clock port not in use */
+static const struct regvals default_config[] = {
+ { 6, 0x08 },
+ { 28, 0x0b },
+ { 29, 0x08 },
+ { 30, 0xb0 },
+ { 34, 0xe3 },
+ { 39, 0x00 },
+
+ /* Reset Multisynth 3 */
+ { 86, 0x00 },
+ { 87, 0x00 },
+ { 88, 0x00 },
+ { 89, 0x00 },
+ { 90, 0x00 },
+ { 91, 0x00 },
+ { 92, 0x00 },
+ { 93, 0x00 },
+ { 94, 0x00 },
+ { 95, 0x00 },
+};
+
+static const struct regvals input_config[] = {
+ { 6, 0x04 },
+ { 28, 0x2b },
+ { 29, 0x28 },
+ { 30, 0xa8 },
+};
+
+static const struct regvals output_config[] = {
+ { 34, 0x22 },
+};
+
+static int write_regs(struct bladerf *dev, const struct regvals *reg, size_t n)
+{
+ size_t i;
+ int status = 0;
+
+ for (i = 0; i < n && status == 0; i++) {
+ status = dev->backend->si5338_write(dev, reg[i].addr, reg[i].data);
+ }
+
+ return status;
+}
+
+static inline int smb_mode_input(struct bladerf *dev)
+{
+ int status;
+ uint8_t val;
+
+ status = write_regs(dev, input_config, ARRAY_SIZE(input_config));
+ if (status != 0) {
+ return status;
+ }
+
+ /* Turn off any SMB connector output */
+ status = dev->backend->si5338_read(dev, 39, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ val &= ~(1);
+ status = dev->backend->si5338_write(dev, 39, val);
+ if (status != 0) {
+ return status;
+ }
+
+ return status;
+}
+
+static inline int smb_mode_output(struct bladerf *dev)
+{
+ int status;
+ uint8_t val;
+
+ status = dev->backend->si5338_read(dev, 39, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ val |= 1;
+ status = dev->backend->si5338_write(dev, 39, val);
+ if (status != 0) {
+ return status;
+ }
+
+ status = write_regs(dev, output_config, ARRAY_SIZE(output_config));
+ if (status != 0) {
+ return status;
+ }
+
+ return status;
+}
+
+static inline int smb_mode_disabled(struct bladerf *dev)
+{
+ return write_regs(dev, default_config, ARRAY_SIZE(default_config));
+}
+
+int smb_clock_set_mode(struct bladerf *dev, bladerf_smb_mode mode)
+{
+ int status;
+
+ /* Reset initial state */
+ status = smb_mode_disabled(dev);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Apply changes */
+ switch (mode) {
+ case BLADERF_SMB_MODE_DISABLED:
+ break;
+
+ case BLADERF_SMB_MODE_OUTPUT:
+ status = smb_mode_output(dev);
+ break;
+
+ case BLADERF_SMB_MODE_INPUT:
+ status = smb_mode_input(dev);
+ break;
+
+ default:
+ log_debug("Invalid SMB clock port mode: %d\n", mode);
+ return BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
+
+/* For simplicity, we just check a few bits that are indicative of our known
+ * register configurations for our SMB clock port configurations.
+ *
+ * Inconsistent register states (or users manually changing registers) may
+ * cause this function to erroneously report the state.
+ */
+int smb_clock_get_mode(struct bladerf *dev, bladerf_smb_mode *mode)
+{
+ int status;
+ uint8_t val;
+
+ /* Check DRV3_FMT[2:0] for an output configuration */
+ status = dev->backend->si5338_read(dev, 39, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ switch (val & 0x7) {
+ case 0x00: /* No output */
+ break;
+
+ case 0x01: /* CLK3A CMOS/SSTL/HSTL - we're outputting a clock */
+ *mode = BLADERF_SMB_MODE_OUTPUT;
+ return 0;
+
+ case 0x02: /* CLK3B CMOS/SSTL/HSTL - used by the XB-200. */
+ *mode = BLADERF_SMB_MODE_UNAVAILBLE;
+ return 0;
+
+ default:
+ *mode = BLADERF_SMB_MODE_INVALID;
+ log_debug("Si5338[39] in unexpected state: 0x%02x\n", val);
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ /* Check P2DIV_IN[0] for an input configuration */
+ status = dev->backend->si5338_read(dev, 28, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ if ((val & (1 << 5)) != 0) {
+ *mode = BLADERF_SMB_MODE_INPUT;
+ } else {
+ *mode = BLADERF_SMB_MODE_DISABLED;
+ }
+
+ return status;
+}
diff --git a/Radio/HW/BladeRF/src/driver/smb_clock.h b/Radio/HW/BladeRF/src/driver/smb_clock.h
new file mode 100644
index 0000000..c0f206d
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/smb_clock.h
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2016 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef DRIVER_SMB_CLOCK_H_
+#define DRIVER_SMB_CLOCK_H_
+
+#include <libbladeRF.h>
+
+/**
+ * Set the current mode of operation of the SMB clock port
+ *
+ * @param dev Device handle
+ * @param[in] mode Desired mode
+ *
+ * @return 0 on success, or a BLADERF_ERR_* value on failure.
+ */
+int smb_clock_set_mode(struct bladerf *dev, bladerf_smb_mode mode);
+
+/**
+ * Get the current mode of operation of the SMB clock port
+ *
+ * @param dev Device handle
+ * @param[out] mode Desired mode
+ *
+ * @return 0 on success, or a value from \ref RETCODES list on failure.
+ */
+int smb_clock_get_mode(struct bladerf *dev, bladerf_smb_mode *mode);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/driver/spi_flash.c b/Radio/HW/BladeRF/src/driver/spi_flash.c
new file mode 100644
index 0000000..f0e1a1d
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/spi_flash.c
@@ -0,0 +1,134 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Daniel Gröber <dxld AT darkboxed DOT org>
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <libbladeRF.h>
+
+#include "rel_assert.h"
+#include "log.h"
+
+#include "spi_flash.h"
+#include "board/board.h"
+
+static inline int check_eb_access(struct bladerf *dev,
+ uint32_t erase_block, uint32_t count)
+{
+ if (erase_block >= dev->flash_arch->num_ebs) {
+ log_debug("Invalid erase block: %u\n", erase_block);
+ return BLADERF_ERR_INVAL;
+ } else if (count > dev->flash_arch->num_ebs) {
+ log_debug("Invalid number of erase blocks: %u\n", count);
+ return BLADERF_ERR_INVAL;
+ } else if ((erase_block + count) > dev->flash_arch->num_ebs) {
+ log_debug("Requested operation extends past end of flash: "
+ "eb=%u, count=%u\n", erase_block, count);
+ return BLADERF_ERR_INVAL;
+ } else {
+ return 0;
+ }
+}
+
+static inline int check_page_access(struct bladerf *dev,
+ uint32_t page, uint32_t count)
+{
+ if (page >= dev->flash_arch->num_pages) {
+ log_debug("Invalid page: %u\n", page);
+ return BLADERF_ERR_INVAL;
+ } else if (count > dev->flash_arch->num_pages) {
+ log_debug("Invalid number of pages: %u\n", count);
+ return BLADERF_ERR_INVAL;
+ } else if ((page + count) > dev->flash_arch->num_pages) {
+ log_debug("Requested operation extends past end of flash: "
+ "page=%u, count=%u\n", page, count);
+ return BLADERF_ERR_INVAL;
+ } else {
+ return 0;
+ }
+}
+
+int spi_flash_erase(struct bladerf *dev, uint32_t erase_block, uint32_t count)
+{
+ int status = check_eb_access(dev, erase_block, count);
+
+ if (status == 0) {
+ status = dev->backend->erase_flash_blocks(dev, erase_block, count);
+ }
+
+ return status;
+}
+
+int spi_flash_read(struct bladerf *dev, uint8_t *buf,
+ uint32_t page, uint32_t count)
+{
+ int status = check_page_access(dev, page, count);
+
+ if (status == 0) {
+ status = dev->backend->read_flash_pages(dev, buf, page, count);
+ }
+
+ return status;;
+}
+
+int spi_flash_write(struct bladerf *dev, const uint8_t *buf,
+ uint32_t page, uint32_t count)
+{
+ int status = check_page_access(dev, page, count);
+
+ if (status == 0) {
+ status = dev->backend->write_flash_pages(dev, buf, page, count);
+ }
+
+ return status;
+}
+
+int spi_flash_verify(struct bladerf *dev, uint8_t *readback_buf,
+ const uint8_t *expected_buf, uint32_t page,
+ uint32_t count)
+{
+ int status = 0;
+ size_t i;
+ const size_t len = count * dev->flash_arch->psize_bytes;
+
+ log_info("Verifying %u pages, starting at page %u\n", count, page);
+ status = spi_flash_read(dev, readback_buf, page, count);
+
+ if (status < 0) {
+ log_debug("Failed to read from flash: %s\n", bladerf_strerror(status));
+ return status;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (expected_buf[i] != readback_buf[i]) {
+ status = BLADERF_ERR_UNEXPECTED;
+ log_info("Flash verification failed at byte %llu. "
+ "Read %02x, expected %02x\n",
+ i, readback_buf[i], expected_buf[i]);
+ break;
+ }
+ }
+
+ return status;
+}
+
diff --git a/Radio/HW/BladeRF/src/driver/spi_flash.h b/Radio/HW/BladeRF/src/driver/spi_flash.h
new file mode 100644
index 0000000..c425494
--- /dev/null
+++ b/Radio/HW/BladeRF/src/driver/spi_flash.h
@@ -0,0 +1,99 @@
+/**
+ * @file flash.h
+ *
+ * @brief Flash conversion and alignment routines
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Daniel Gröber <dxld AT darkboxed DOT org>
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef DRIVER_SPI_FLASH_H_
+#define DRIVER_SPI_FLASH_H_
+
+#include <assert.h>
+#include <stdint.h>
+
+#include <libbladeRF.h>
+
+/**
+ * Erase regions of SPI flash
+ *
+ * @param dev Device handle
+ * @param[in] erase_block Erase block to start erasing at
+ * @param[in] count Number of blocks to erase.
+ *
+ * @return 0 on success, or BLADERF_ERR_INVAL on an invalid `erase_block` or
+ * `count` value, or a value from \ref RETCODES list on other failures
+ */
+int spi_flash_erase(struct bladerf *dev, uint32_t erase_block, uint32_t count);
+
+/**
+ * Read data from flash
+ *
+ * @param dev Device handle
+ * @param[out] buf Buffer to read data into. Must be `count` *
+ * flash-page-size bytes or larger.
+ * @param[in] page Page to begin reading from
+ * @param[in] count Number of pages to read
+ *
+ * @return 0 on success, or BLADERF_ERR_INVAL on an invalid `page` or `count`
+ * value, or a value from \ref RETCODES list on other failures.
+ */
+int spi_flash_read(struct bladerf *dev,
+ uint8_t *buf,
+ uint32_t page,
+ uint32_t count);
+
+/**
+ * Verify data in flash
+ *
+ * @param dev Device handle
+ * @param[out] readback_buf Buffer to read data into. Must be `count` *
+ * flash-page-size bytes or larger.
+ * @param[in] expected_buf Expected contents of buffer
+ * @param[in] page Page to begin reading from
+ * @param[in] count Number of pages to read
+ *
+ * @return 0 on success, or BLADERF_ERR_INVAL on an invalid `page` or `count`
+ * value, or a value from \ref RETCODES list on other failures.
+ */
+int spi_flash_verify(struct bladerf *dev,
+ uint8_t *readback_buf,
+ const uint8_t *expected_buf,
+ uint32_t page,
+ uint32_t count);
+
+/**
+ * Write data to flash
+ *
+ * @param dev Device handle
+ * @param[in] buf Data to write to flash
+ * @param[in] page Page to begin writing at
+ * @param[in] count Number of pages to write
+ *
+ * @return 0 on success, or BLADERF_ERR_INVAL on an invalid `page` or `count`
+ * value, or a value from \ref RETCODES list on other failures.
+ */
+int spi_flash_write(struct bladerf *dev,
+ const uint8_t *buf,
+ uint32_t page,
+ uint32_t count);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/expansion/xb100.c b/Radio/HW/BladeRF/src/expansion/xb100.c
new file mode 100644
index 0000000..b432616
--- /dev/null
+++ b/Radio/HW/BladeRF/src/expansion/xb100.c
@@ -0,0 +1,98 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "xb100.h"
+
+#include "log.h"
+
+int xb100_attach(struct bladerf *dev)
+{
+ return 0;
+}
+
+void xb100_detach(struct bladerf *dev)
+{
+}
+
+int xb100_enable(struct bladerf *dev, bool enable)
+{
+ int status = 0;
+
+ const uint32_t mask =
+ BLADERF_XB100_LED_D1 |
+ BLADERF_XB100_LED_D2 |
+ BLADERF_XB100_LED_D3 |
+ BLADERF_XB100_LED_D4 |
+ BLADERF_XB100_LED_D5 |
+ BLADERF_XB100_LED_D6 |
+ BLADERF_XB100_LED_D7 |
+ BLADERF_XB100_LED_D8 |
+ BLADERF_XB100_TLED_RED |
+ BLADERF_XB100_TLED_GREEN |
+ BLADERF_XB100_TLED_BLUE;
+
+ const uint32_t outputs = mask;
+ const uint32_t default_values = mask;
+
+ if (enable) {
+ status = dev->backend->expansion_gpio_dir_write(dev, mask, outputs);
+ if (status == 0) {
+ status = dev->backend->expansion_gpio_write(dev, mask, default_values);
+ }
+ }
+
+ return status;
+}
+
+int xb100_init(struct bladerf *dev)
+{
+ return 0;
+}
+
+int xb100_gpio_read(struct bladerf *dev, uint32_t *val)
+{
+ return dev->backend->expansion_gpio_read(dev, val);
+}
+
+int xb100_gpio_write(struct bladerf *dev, uint32_t val)
+{
+ return dev->backend->expansion_gpio_write(dev, 0xffffffff, val);
+}
+
+int xb100_gpio_masked_write(struct bladerf *dev, uint32_t mask, uint32_t val)
+{
+ return dev->backend->expansion_gpio_write(dev, mask, val);
+}
+
+int xb100_gpio_dir_read(struct bladerf *dev, uint32_t *val)
+{
+ return dev->backend->expansion_gpio_dir_read(dev, val);
+}
+
+int xb100_gpio_dir_write(struct bladerf *dev, uint32_t val)
+{
+ return xb100_gpio_dir_masked_write(dev, 0xffffffff, val);
+}
+
+int xb100_gpio_dir_masked_write(struct bladerf *dev, uint32_t mask, uint32_t val)
+{
+ return dev->backend->expansion_gpio_dir_write(dev, mask, val);
+}
diff --git a/Radio/HW/BladeRF/src/expansion/xb100.h b/Radio/HW/BladeRF/src/expansion/xb100.h
new file mode 100644
index 0000000..2a49830
--- /dev/null
+++ b/Radio/HW/BladeRF/src/expansion/xb100.h
@@ -0,0 +1,47 @@
+/**
+ * @file xb.h
+ *
+ * @brief XB-100 support
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef EXPANSION_XB100_H_
+#define EXPANSION_XB100_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "board/board.h"
+
+int xb100_attach(struct bladerf *dev);
+void xb100_detach(struct bladerf *dev);
+int xb100_enable(struct bladerf *dev, bool enable);
+int xb100_init(struct bladerf *dev);
+int xb100_gpio_read(struct bladerf *dev, uint32_t *val);
+int xb100_gpio_write(struct bladerf *dev, uint32_t val);
+int xb100_gpio_masked_write(struct bladerf *dev, uint32_t mask, uint32_t val);
+int xb100_gpio_dir_read(struct bladerf *dev, uint32_t *val);
+int xb100_gpio_dir_write(struct bladerf *dev, uint32_t val);
+int xb100_gpio_dir_masked_write(struct bladerf *dev,
+ uint32_t mask,
+ uint32_t val);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/expansion/xb200.c b/Radio/HW/BladeRF/src/expansion/xb200.c
new file mode 100644
index 0000000..1e6dba2
--- /dev/null
+++ b/Radio/HW/BladeRF/src/expansion/xb200.c
@@ -0,0 +1,543 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "xb200.h"
+
+#include "driver/si5338.h"
+#include "lms.h"
+#include "rel_assert.h"
+#include "log.h"
+
+#define BLADERF_XB_CONFIG_TX_PATH_MIX 0x04
+#define BLADERF_XB_CONFIG_TX_PATH_BYPASS 0x08
+#define BLADERF_XB_CONFIG_TX_BYPASS 0x04
+#define BLADERF_XB_CONFIG_TX_BYPASS_N 0x08
+#define BLADERF_XB_CONFIG_TX_BYPASS_MASK 0x0C
+#define BLADERF_XB_CONFIG_RX_PATH_MIX 0x10
+#define BLADERF_XB_CONFIG_RX_PATH_BYPASS 0x20
+#define BLADERF_XB_CONFIG_RX_BYPASS 0x10
+#define BLADERF_XB_CONFIG_RX_BYPASS_N 0x20
+#define BLADERF_XB_CONFIG_RX_BYPASS_MASK 0x30
+
+#define BLADERF_XB_RF_ON 0x0800
+#define BLADERF_XB_TX_ENABLE 0x1000
+#define BLADERF_XB_RX_ENABLE 0x2000
+
+#define BLADERF_XB_TX_RF_SW2 0x04000000
+#define BLADERF_XB_TX_RF_SW1 0x08000000
+#define BLADERF_XB_TX_MASK 0x0C000000
+#define BLADERF_XB_TX_SHIFT 26
+
+#define BLADERF_XB_RX_RF_SW2 0x10000000
+#define BLADERF_XB_RX_RF_SW1 0x20000000
+#define BLADERF_XB_RX_MASK 0x30000000
+#define BLADERF_XB_RX_SHIFT 28
+
+struct xb200_xb_data {
+ /* Track filterbank selection for RX and TX auto-selection */
+ bladerf_xb200_filter auto_filter[2];
+};
+
+int xb200_attach(struct bladerf *dev)
+{
+ struct xb200_xb_data *xb_data;
+ int status = 0;
+ uint32_t val;
+ uint8_t val8;
+ unsigned int muxout = 6;
+ const char *mux_lut[] = { "THREE-STATE OUTPUT",
+ "DVdd",
+ "DGND",
+ "R COUNTER OUTPUT",
+ "N DIVIDER OUTPUT",
+ "ANALOG LOCK DETECT",
+ "DIGITAL LOCK DETECT",
+ "RESERVED" };
+
+ xb_data = calloc(1, sizeof(struct xb200_xb_data));
+ if (xb_data == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ xb_data->auto_filter[BLADERF_CHANNEL_RX(0)] = -1;
+ xb_data->auto_filter[BLADERF_CHANNEL_TX(0)] = -1;
+
+ dev->xb_data = xb_data;
+
+ log_debug(" Attaching transverter board\n");
+ status = dev->backend->si5338_read(dev, 39, &val8);
+ if (status < 0) {
+ goto error;
+ }
+ val8 |= 2;
+ if ((status = dev->backend->si5338_write(dev, 39, val8))) {
+ goto error;
+ }
+ if ((status = dev->backend->si5338_write(dev, 34, 0x22))) {
+ goto error;
+ }
+ if ((status = dev->backend->config_gpio_read(dev, &val))) {
+ goto error;
+ }
+ val |= 0x80000000;
+ if ((status = dev->backend->config_gpio_write(dev, val))) {
+ goto error;
+ }
+ if ((status = dev->backend->expansion_gpio_read(dev, &val))) {
+ goto error;
+ }
+
+ if ((status = dev->backend->expansion_gpio_dir_write(dev, 0xffffffff,
+ 0x3C00383E))) {
+ goto error;
+ }
+
+ if ((status = dev->backend->expansion_gpio_write(dev, 0xffffffff, 0x800))) {
+ goto error;
+ }
+
+ // Load ADF4351 registers via SPI
+ // Refer to ADF4351 reference manual for register set
+ // The LO is set to a Int-N 1248MHz +3dBm tone
+ // Registers are written in order from 5 downto 0
+ if ((status = dev->backend->xb_spi(dev, 0x580005))) {
+ goto error;
+ }
+ if ((status = dev->backend->xb_spi(dev, 0x99A16C))) {
+ goto error;
+ }
+ if ((status = dev->backend->xb_spi(dev, 0xC004B3))) {
+ goto error;
+ }
+ log_debug(" MUXOUT: %s\n", mux_lut[muxout]);
+
+ if ((status = dev->backend->xb_spi(dev, 0x60008E42 | (1 << 8) |
+ (muxout << 26)))) {
+ goto error;
+ }
+ if ((status = dev->backend->xb_spi(dev, 0x08008011))) {
+ goto error;
+ }
+ if ((status = dev->backend->xb_spi(dev, 0x00410000))) {
+ goto error;
+ }
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (!status && (val & 0x1))
+ log_debug(" MUXOUT Bit set: OK\n");
+ else {
+ log_debug(" MUXOUT Bit not set: FAIL\n");
+ }
+ if ((status =
+ dev->backend->expansion_gpio_write(dev, 0xffffffff, 0x3C000800))) {
+ goto error;
+ }
+
+ return 0;
+
+error:
+ free(dev->xb_data);
+ dev->xb_data = NULL;
+ return status;
+}
+
+void xb200_detach(struct bladerf *dev)
+{
+ if (dev->xb_data) {
+ free(dev->xb_data);
+ dev->xb_data = NULL;
+ }
+}
+
+int xb200_enable(struct bladerf *dev, bool enable)
+{
+ int status;
+ uint32_t val, orig;
+
+ status = dev->backend->expansion_gpio_read(dev, &orig);
+ if (status)
+ return status;
+
+ val = orig;
+ if (enable)
+ val |= BLADERF_XB_RF_ON;
+ else
+ val &= ~BLADERF_XB_RF_ON;
+
+ if (status || (val == orig))
+ return status;
+
+ return dev->backend->expansion_gpio_write(dev, 0xffffffff, val);
+}
+
+int xb200_init(struct bladerf *dev)
+{
+ int status;
+
+ log_verbose( "Setting RX path\n" );
+ status = xb200_set_path(dev, BLADERF_CHANNEL_RX(0), BLADERF_XB200_BYPASS);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose( "Setting TX path\n" );
+ status = xb200_set_path(dev, BLADERF_CHANNEL_TX(0), BLADERF_XB200_BYPASS);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose( "Setting RX filter\n" );
+ status = xb200_set_filterbank(dev, BLADERF_CHANNEL_RX(0), BLADERF_XB200_AUTO_1DB);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose( "Setting TX filter\n" );
+ status = xb200_set_filterbank(dev, BLADERF_CHANNEL_TX(0), BLADERF_XB200_AUTO_1DB);
+ if (status != 0) {
+ return status;
+ }
+
+ return 0;
+}
+
+/**
+ * Validate XB-200 filter selection
+ *
+ * @param[in] f Filter supplied by API user.
+ *
+ * @return 0 for a valid enumeration value, BLADERF_ERR_INVAL otherwise.
+ */
+static int check_xb200_filter(bladerf_xb200_filter f)
+{
+ int status;
+
+ switch (f) {
+ case BLADERF_XB200_50M:
+ case BLADERF_XB200_144M:
+ case BLADERF_XB200_222M:
+ case BLADERF_XB200_CUSTOM:
+ case BLADERF_XB200_AUTO_3DB:
+ case BLADERF_XB200_AUTO_1DB:
+ status = 0;
+ break;
+
+ default:
+ log_debug("Invalid XB200 filter: %d\n", f);
+ status = BLADERF_ERR_INVAL;
+ break;
+ }
+
+ return status;
+}
+
+/**
+ * Validate XB-200 path selection
+ *
+ * @param[in] p Path supplied by API user.
+ *
+ * @return 0 for a valid enumeration value, BLADERF_ERR_INVAL otherwise.
+ */
+static int check_xb200_path(bladerf_xb200_path p)
+{
+ int status;
+
+ switch (p) {
+ case BLADERF_XB200_BYPASS:
+ case BLADERF_XB200_MIX:
+ status = 0;
+ break;
+
+ default:
+ status = BLADERF_ERR_INVAL;
+ log_debug("Invalid XB200 path: %d\n", p);
+ break;
+ }
+
+ return status;
+}
+int xb200_get_filterbank(struct bladerf *dev, bladerf_channel ch,
+ bladerf_xb200_filter *filter) {
+ int status;
+ uint32_t val;
+ unsigned int shift;
+
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_TX(0))
+ return BLADERF_ERR_INVAL;
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ if (ch == BLADERF_CHANNEL_RX(0)) {
+ shift = BLADERF_XB_RX_SHIFT;
+ } else {
+ shift = BLADERF_XB_TX_SHIFT;
+ }
+
+ *filter = (val >> shift) & 3;
+
+ status = check_xb200_filter(*filter);
+ if (status != 0) {
+ log_debug("Read back invalid GPIO state: 0x%08x\n", val);
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ return status;
+}
+
+static int set_filterbank_mux(struct bladerf *dev, bladerf_channel ch, bladerf_xb200_filter filter)
+{
+ int status;
+ uint32_t orig, val, mask;
+ unsigned int shift;
+ static const char *filters[] = { "50M", "144M", "222M", "custom" };
+
+ assert(filter >= 0);
+ assert(filter < ARRAY_SIZE(filters));
+
+ if (ch == BLADERF_CHANNEL_RX(0)) {
+ mask = BLADERF_XB_RX_MASK;
+ shift = BLADERF_XB_RX_SHIFT;
+ } else {
+ mask = BLADERF_XB_TX_MASK;
+ shift = BLADERF_XB_TX_SHIFT;
+ }
+
+ status = dev->backend->expansion_gpio_read(dev, &orig);
+ if (status != 0) {
+ return status;
+ }
+
+ val = orig & ~mask;
+ val |= filter << shift;
+
+ if (orig != val) {
+ log_debug("Engaging %s band XB-200 %s filter\n", filters[filter],
+ mask == BLADERF_XB_TX_MASK ? "TX" : "RX");
+
+ status = dev->backend->expansion_gpio_write(dev, 0xffffffff, val);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+
+ return 0;
+}
+
+int xb200_set_filterbank(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_filter filter)
+{
+ struct xb200_xb_data *xb_data = dev->xb_data;
+ uint64_t frequency;
+
+ int status = 0;
+
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_TX(0)) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (NULL == xb_data) {
+ log_error("xb_data is null (do you need to xb200_attach?)\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = check_xb200_filter(filter);
+ if (status != 0) {
+ return status;
+ }
+
+ if (filter == BLADERF_XB200_AUTO_1DB || filter == BLADERF_XB200_AUTO_3DB) {
+ /* Save which soft auto filter mode we're in */
+ xb_data->auto_filter[ch] = filter;
+
+ status = dev->board->get_frequency(dev, ch, &frequency);
+ if (status == 0) {
+ status = xb200_auto_filter_selection(dev, ch, frequency);
+ }
+
+ } else {
+ /* Invalidate the soft auto filter mode entry */
+ xb_data->auto_filter[ch] = -1;
+
+ status = set_filterbank_mux(dev, ch, filter);
+ }
+
+ return status;
+}
+
+int xb200_auto_filter_selection(struct bladerf *dev,
+ bladerf_channel ch,
+ uint64_t frequency)
+{
+ struct xb200_xb_data *xb_data = dev->xb_data;
+ bladerf_xb200_filter filter;
+
+ int status = 0;
+
+ if (frequency >= 300000000u) {
+ return 0;
+ }
+
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_TX(0)) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (NULL == xb_data) {
+ log_error("xb_data is null (do you need to xb200_attach?)\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (xb_data->auto_filter[ch] == BLADERF_XB200_AUTO_1DB) {
+ if (37774405 <= frequency && frequency <= 59535436) {
+ filter = BLADERF_XB200_50M;
+ } else if (128326173 <= frequency && frequency <= 166711171) {
+ filter = BLADERF_XB200_144M;
+ } else if (187593160 <= frequency && frequency <= 245346403) {
+ filter = BLADERF_XB200_222M;
+ } else {
+ filter = BLADERF_XB200_CUSTOM;
+ }
+
+ status = set_filterbank_mux(dev, ch, filter);
+ } else if (xb_data->auto_filter[ch] == BLADERF_XB200_AUTO_3DB) {
+ if (34782924 <= frequency && frequency <= 61899260) {
+ filter = BLADERF_XB200_50M;
+ } else if (121956957 <= frequency && frequency <= 178444099) {
+ filter = BLADERF_XB200_144M;
+ } else if (177522675 <= frequency && frequency <= 260140935) {
+ filter = BLADERF_XB200_222M;
+ } else {
+ filter = BLADERF_XB200_CUSTOM;
+ }
+
+ status = set_filterbank_mux(dev, ch, filter);
+ }
+
+ return status;
+}
+
+#define LMS_RX_SWAP 0x40
+#define LMS_TX_SWAP 0x08
+
+int xb200_set_path(struct bladerf *dev,
+ bladerf_channel ch, bladerf_xb200_path path) {
+ int status;
+ uint32_t val;
+ uint32_t mask;
+ uint8_t lval, lorig = 0;
+
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_TX(0))
+ return BLADERF_ERR_INVAL;
+
+ status = check_xb200_path(path);
+ if (status != 0) {
+ return status;
+ }
+
+ status = LMS_READ(dev, 0x5A, &lorig);
+ if (status != 0) {
+ return status;
+ }
+
+ lval = lorig;
+
+ if (path == BLADERF_XB200_MIX) {
+ lval |= (ch == BLADERF_CHANNEL_RX(0)) ? LMS_RX_SWAP : LMS_TX_SWAP;
+ } else {
+ lval &= ~((ch == BLADERF_CHANNEL_RX(0)) ? LMS_RX_SWAP : LMS_TX_SWAP);
+ }
+
+ status = LMS_WRITE(dev, 0x5A, lval);
+ if (status != 0) {
+ return status;
+ }
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ if (!(val & BLADERF_XB_RF_ON)) {
+ status = xb200_attach(dev);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ if (ch == BLADERF_CHANNEL_RX(0)) {
+ mask = (BLADERF_XB_CONFIG_RX_BYPASS_MASK | BLADERF_XB_RX_ENABLE);
+ } else {
+ mask = (BLADERF_XB_CONFIG_TX_BYPASS_MASK | BLADERF_XB_TX_ENABLE);
+ }
+
+ val |= BLADERF_XB_RF_ON;
+ val &= ~mask;
+
+ if (ch == BLADERF_CHANNEL_RX(0)) {
+ if (path == BLADERF_XB200_MIX) {
+ val |= (BLADERF_XB_RX_ENABLE | BLADERF_XB_CONFIG_RX_PATH_MIX);
+ } else {
+ val |= BLADERF_XB_CONFIG_RX_PATH_BYPASS;
+ }
+ } else {
+ if (path == BLADERF_XB200_MIX) {
+ val |= (BLADERF_XB_TX_ENABLE | BLADERF_XB_CONFIG_TX_PATH_MIX);
+ } else {
+ val |= BLADERF_XB_CONFIG_TX_PATH_BYPASS;
+ }
+ }
+
+ return dev->backend->expansion_gpio_write(dev, 0xffffffff, val);
+}
+
+int xb200_get_path(struct bladerf *dev,
+ bladerf_channel ch, bladerf_xb200_path *path) {
+ int status;
+ uint32_t val;
+
+ if (ch != BLADERF_CHANNEL_RX(0) && ch != BLADERF_CHANNEL_TX(0))
+ return BLADERF_ERR_INVAL;
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ if (ch == BLADERF_CHANNEL_RX(0)) {
+ *path = (val & BLADERF_XB_CONFIG_RX_BYPASS) ?
+ BLADERF_XB200_MIX : BLADERF_XB200_BYPASS;
+
+ } else if (ch == BLADERF_CHANNEL_TX(0)) {
+ *path = (val & BLADERF_XB_CONFIG_TX_BYPASS) ?
+ BLADERF_XB200_MIX : BLADERF_XB200_BYPASS;
+ }
+
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/src/expansion/xb200.h b/Radio/HW/BladeRF/src/expansion/xb200.h
new file mode 100644
index 0000000..3234a7c
--- /dev/null
+++ b/Radio/HW/BladeRF/src/expansion/xb200.h
@@ -0,0 +1,105 @@
+/**
+ * @file xb.h
+ *
+ * @brief XB-200 support
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef EXPANSION_XB200_H_
+#define EXPANSION_XB200_H_
+
+#include <stdbool.h>
+
+#include <libbladeRF.h>
+
+#include "board/board.h"
+
+int xb200_attach(struct bladerf *dev);
+void xb200_detach(struct bladerf *dev);
+int xb200_enable(struct bladerf *dev, bool enable);
+int xb200_init(struct bladerf *dev);
+
+/**
+ * Select an XB-200 filterbank
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] filter XB200 filterbank
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int xb200_set_filterbank(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_filter filter);
+
+/**
+ * Select an appropriate filterbank, based upon the specified frequency
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] frequency Frequency
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int xb200_auto_filter_selection(struct bladerf *dev,
+ bladerf_channel ch,
+ uint64_t frequency);
+
+/**
+ * Get the current selected XB-200 filterbank
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] filter Pointer to filterbank, only updated if return value
+ * is 0.
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int xb200_get_filterbank(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_filter *filter);
+/**
+ * Configure the XB-200 signal path
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[in] path Desired XB-200 signal path
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int xb200_set_path(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_path path);
+
+/**
+ * Get the current XB-200 signal path
+ *
+ * @param dev Device handle
+ * @param[in] ch Channel
+ * @param[out] path Pointer to XB200 signal path
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+int xb200_get_path(struct bladerf *dev,
+ bladerf_channel ch,
+ bladerf_xb200_path *path);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/expansion/xb300.c b/Radio/HW/BladeRF/src/expansion/xb300.c
new file mode 100644
index 0000000..15a099d
--- /dev/null
+++ b/Radio/HW/BladeRF/src/expansion/xb300.c
@@ -0,0 +1,299 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "xb300.h"
+
+#include "log.h"
+
+#define BLADERF_XB_AUX_EN 0x000002
+#define BLADERF_XB_TX_LED 0x000010
+#define BLADERF_XB_RX_LED 0x000020
+#define BLADERF_XB_TRX_TXn 0x000040
+#define BLADERF_XB_TRX_RXn 0x000080
+#define BLADERF_XB_TRX_MASK 0x0000c0
+#define BLADERF_XB_PA_EN 0x000200
+#define BLADERF_XB_LNA_ENn 0x000400
+#define BLADERF_XB_CS 0x010000
+#define BLADERF_XB_CSEL 0x040000
+#define BLADERF_XB_DOUT 0x100000
+#define BLADERF_XB_SCLK 0x400000
+
+int xb300_attach(struct bladerf *dev) {
+ int status;
+ uint32_t val;
+
+ val = BLADERF_XB_TX_LED | BLADERF_XB_RX_LED | BLADERF_XB_TRX_MASK;
+ val |= BLADERF_XB_PA_EN | BLADERF_XB_LNA_ENn;
+ val |= BLADERF_XB_CSEL | BLADERF_XB_SCLK | BLADERF_XB_CS;
+
+ if ((status = dev->backend->expansion_gpio_dir_write(dev, 0xffffffff, val)))
+ return status;
+
+ val = BLADERF_XB_CS | BLADERF_XB_LNA_ENn;
+ if ((status = dev->backend->expansion_gpio_write(dev, 0xffffffff, val)))
+ return status;
+
+ return status;
+}
+
+void xb300_detach(struct bladerf *dev)
+{
+}
+
+int xb300_enable(struct bladerf *dev, bool enable)
+{
+ int status;
+ uint32_t val;
+ float pwr;
+
+ val = BLADERF_XB_CS | BLADERF_XB_CSEL | BLADERF_XB_LNA_ENn;
+ if ((status = dev->backend->expansion_gpio_write(dev, 0xffffffff, val)))
+ return status;
+
+ status = xb300_get_output_power(dev, &pwr);
+
+ return status;
+}
+
+int xb300_init(struct bladerf *dev)
+{
+ int status;
+
+ log_verbose( "Setting TRX path to TX\n" );
+ status = xb300_set_trx(dev, BLADERF_XB300_TRX_TX);
+ if (status != 0) {
+ return status;
+ }
+
+ return 0;
+}
+
+int xb300_set_trx(struct bladerf *dev, bladerf_xb300_trx trx)
+{
+ int status;
+ uint32_t val;
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ val &= ~(BLADERF_XB_TRX_MASK);
+
+ switch (trx) {
+ case BLADERF_XB300_TRX_RX:
+ val |= BLADERF_XB_TRX_RXn;
+ break;
+
+ case BLADERF_XB300_TRX_TX:
+ val |= BLADERF_XB_TRX_TXn;
+ break;
+
+ case BLADERF_XB300_TRX_UNSET:
+ break;
+
+ default:
+ log_debug("Invalid TRX option: %d\n", trx);
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = dev->backend->expansion_gpio_write(dev, 0xffffffff, val);
+ return status;
+}
+
+int xb300_get_trx(struct bladerf *dev, bladerf_xb300_trx *trx)
+{
+ int status;
+ uint32_t val;
+
+ *trx = BLADERF_XB300_TRX_INVAL;
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ val &= BLADERF_XB_TRX_MASK;
+
+ if (!val) {
+ *trx = BLADERF_XB300_TRX_UNSET;
+ } else {
+ *trx = (val & BLADERF_XB_TRX_RXn) ? BLADERF_XB300_TRX_RX : BLADERF_XB300_TRX_TX;
+ }
+
+ /* Sanity check */
+ switch (*trx) {
+ case BLADERF_XB300_TRX_TX:
+ case BLADERF_XB300_TRX_RX:
+ case BLADERF_XB300_TRX_UNSET:
+ break;
+
+ default:
+ log_debug("Read back invalid TRX setting value: %d\n", *trx);
+ status = BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
+
+int xb300_set_amplifier_enable(struct bladerf *dev,
+ bladerf_xb300_amplifier amp, bool enable) {
+ int status;
+ uint32_t val;
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ switch (amp) {
+ case BLADERF_XB300_AMP_PA:
+ if (enable) {
+ val |= BLADERF_XB_TX_LED;
+ val |= BLADERF_XB_PA_EN;
+ } else {
+ val &= ~BLADERF_XB_TX_LED;
+ val &= ~BLADERF_XB_PA_EN;
+ }
+ break;
+
+ case BLADERF_XB300_AMP_LNA:
+ if (enable) {
+ val |= BLADERF_XB_RX_LED;
+ val &= ~BLADERF_XB_LNA_ENn;
+ } else {
+ val &= ~BLADERF_XB_RX_LED;
+ val |= BLADERF_XB_LNA_ENn;
+ }
+ break;
+
+ case BLADERF_XB300_AMP_PA_AUX:
+ if (enable) {
+ val |= BLADERF_XB_AUX_EN;
+ } else {
+ val &= ~BLADERF_XB_AUX_EN;
+ }
+ break;
+
+ default:
+ log_debug("Invalid amplifier selection: %d\n", amp);
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = dev->backend->expansion_gpio_write(dev, 0xffffffff, val);
+
+ return status;
+}
+
+int xb300_get_amplifier_enable(struct bladerf *dev,
+ bladerf_xb300_amplifier amp, bool *enable)
+{
+ int status;
+ uint32_t val;
+
+ *enable = false;
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ switch (amp) {
+ case BLADERF_XB300_AMP_PA:
+ *enable = (val & BLADERF_XB_PA_EN);
+ break;
+
+ case BLADERF_XB300_AMP_LNA:
+ *enable = !(val & BLADERF_XB_LNA_ENn);
+ break;
+
+ case BLADERF_XB300_AMP_PA_AUX:
+ *enable = (val & BLADERF_XB_AUX_EN);
+ break;
+
+ default:
+ log_debug("Read back invalid amplifier setting: %d\n", amp);
+ status = BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
+
+int xb300_get_output_power(struct bladerf *dev, float *pwr)
+{
+ uint32_t val, rval;
+ int i;
+ int ret = 0;
+ int status;
+ float volt, volt2, volt3, volt4;
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (status) {
+ return status;
+ }
+
+ val &= ~(BLADERF_XB_CS | BLADERF_XB_SCLK | BLADERF_XB_CSEL);
+
+ status = dev->backend->expansion_gpio_write(dev, 0xffffffff, BLADERF_XB_SCLK | val);
+ if (status) {
+ return status;
+ }
+
+ status = dev->backend->expansion_gpio_write(dev, 0xffffffff, BLADERF_XB_CS | BLADERF_XB_SCLK | val);
+ if (status) {
+ return status;
+ }
+
+ for (i = 1; i <= 14; i++) {
+ status = dev->backend->expansion_gpio_write(dev, 0xffffffff, val);
+ if (status) {
+ return status;
+ }
+
+ status = dev->backend->expansion_gpio_write(dev, 0xffffffff, BLADERF_XB_SCLK | val);
+ if (status) {
+ return status;
+ }
+
+ status = dev->backend->expansion_gpio_read(dev, &rval);
+ if (status) {
+ return status;
+ }
+
+ if (i >= 2 && i <= 11) {
+ ret |= (!!(rval & BLADERF_XB_DOUT)) << (11 - i);
+ }
+ }
+
+ volt = (1.8f/1024.0f) * ret;
+ volt2 = volt * volt;
+ volt3 = volt2 * volt;
+ volt4 = volt3 * volt;
+
+ *pwr = -503.933f * volt4 +
+ 1409.489f * volt3 -
+ 1487.84f * volt2 +
+ 722.9793f * volt -
+ 114.7529f;
+
+ return 0;
+
+}
diff --git a/Radio/HW/BladeRF/src/expansion/xb300.h b/Radio/HW/BladeRF/src/expansion/xb300.h
new file mode 100644
index 0000000..5fe50de
--- /dev/null
+++ b/Radio/HW/BladeRF/src/expansion/xb300.h
@@ -0,0 +1,94 @@
+/**
+ * @file xb.h
+ *
+ * @brief XB-300 support
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef EXPANSION_XB300_H_
+#define EXPANSION_XB300_H_
+
+#include <stdbool.h>
+
+#include <libbladeRF.h>
+
+#include "board/board.h"
+
+int xb300_attach(struct bladerf *dev);
+void xb300_detach(struct bladerf *dev);
+int xb300_enable(struct bladerf *dev, bool enable);
+int xb300_init(struct bladerf *dev);
+
+/**
+ * Configure the XB-300 TRX path
+ *
+ * @param dev Device handle
+ * @param[in] trx Desired XB-300 TRX setting
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int xb300_set_trx(struct bladerf *dev, bladerf_xb300_trx trx);
+
+/**
+ * Get the current XB-300 signal path
+ *
+ * @param dev Device handle
+ * @param[out] trx XB300 TRX antenna setting
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int xb300_get_trx(struct bladerf *dev, bladerf_xb300_trx *trx);
+
+/**
+ * Enable or disable selected XB-300 amplifier
+ *
+ * @param dev Device handle
+ * @param[in] amp XB-300 amplifier
+ * @param[in] enable Set true to enable or false to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int xb300_set_amplifier_enable(struct bladerf *dev,
+ bladerf_xb300_amplifier amp,
+ bool enable);
+/**
+ * Get state of selected XB-300 amplifier
+ *
+ * @param dev Device handle
+ * @param[in] amp XB-300 amplifier
+ * @param[out] enable Set true to enable or false to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int xb300_get_amplifier_enable(struct bladerf *dev,
+ bladerf_xb300_amplifier amp,
+ bool *enable);
+/**
+ * Get current PA PDET output power in dBm
+ *
+ * @param dev Device handle
+ * @param[out] val Output power in dBm
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+int xb300_get_output_power(struct bladerf *dev, float *val);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/helpers/configfile.c b/Radio/HW/BladeRF/src/helpers/configfile.c
new file mode 100644
index 0000000..bfa339c
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/configfile.c
@@ -0,0 +1,342 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013-2018 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <libbladeRF.h>
+
+#include "conversions.h"
+#include "helpers/file.h"
+#include "log.h"
+#include "parse.h"
+
+/******************************************************************************/
+/* Config file stuff */
+/******************************************************************************/
+
+const struct numeric_suffix freq_suffixes[] = { { "G", 1000 * 1000 * 1000 },
+ { "GHz", 1000 * 1000 * 1000 },
+ { "M", 1000 * 1000 },
+ { "MHz", 1000 * 1000 },
+ { "k", 1000 },
+ { "kHz", 1000 } };
+#define NUM_FREQ_SUFFIXES (sizeof(freq_suffixes) / sizeof(freq_suffixes[0]))
+#define MAX(a, b) (a > b ? a : b)
+#define MIN(a, b) (a < b ? a : b)
+
+static int apply_config_options(struct bladerf *dev, struct config_options opt)
+{
+ int status;
+ bladerf_frequency freq;
+ bladerf_bandwidth bw;
+ uint32_t val;
+ bool ok;
+ bladerf_gain_mode gain_mode;
+ const struct bladerf_range *rx_range = NULL;
+ const struct bladerf_range *tx_range = NULL;
+ bladerf_sampling sampling_mode;
+ bladerf_vctcxo_tamer_mode tamer_mode = BLADERF_VCTCXO_TAMER_INVALID;
+
+ struct bladerf_rational_rate rate, actual;
+
+ status = BLADERF_ERR_INVAL;
+
+ if (!strcasecmp(opt.key, "biastee_tx")) {
+ bool enable = false;
+
+ status = str2bool(opt.value, &enable);
+ if (status < 0) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_set_bias_tee(dev, BLADERF_CHANNEL_TX(0), enable);
+ if (status < 0) {
+ return status;
+ }
+ } else if (!strcasecmp(opt.key, "biastee_rx")) {
+ bool enable = false;
+
+ status = str2bool(opt.value, &enable);
+ if (status < 0) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_set_bias_tee(dev, BLADERF_CHANNEL_RX(0), enable);
+ if (status < 0) {
+ return status;
+ }
+ } else if (!strcasecmp(opt.key, "fpga")) {
+ status = bladerf_load_fpga(dev, opt.value);
+ if (status < 0) {
+ log_warning("Config line %d: could not load FPGA from `%s'\n",
+ opt.lineno, opt.value);
+ }
+ return status;
+ } else if (!strcasecmp(opt.key, "frequency")) {
+ status =
+ bladerf_get_frequency_range(dev, BLADERF_CHANNEL_RX(0), &rx_range);
+ if (status < 0) {
+ return status;
+ }
+
+ status =
+ bladerf_get_frequency_range(dev, BLADERF_CHANNEL_TX(0), &tx_range);
+ if (status < 0) {
+ return status;
+ }
+
+ freq = str2uint64_suffix(opt.value, MAX(rx_range->min, tx_range->min),
+ MIN(rx_range->max, tx_range->max),
+ freq_suffixes, NUM_FREQ_SUFFIXES, &ok);
+ if (!ok) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_set_frequency(dev, BLADERF_CHANNEL_RX(0), freq);
+ if (status < 0) {
+ return status;
+ }
+
+ status = bladerf_set_frequency(dev, BLADERF_CHANNEL_TX(0), freq);
+ } else if (!strcasecmp(opt.key, "samplerate")) {
+ status = bladerf_get_sample_rate_range(dev, BLADERF_CHANNEL_RX(0),
+ &rx_range);
+ if (status < 0) {
+ return status;
+ }
+
+ status = bladerf_get_sample_rate_range(dev, BLADERF_CHANNEL_TX(0),
+ &tx_range);
+ if (status < 0) {
+ return status;
+ }
+
+ freq = str2uint64_suffix(opt.value, MAX(rx_range->min, tx_range->min),
+ MIN(rx_range->max, tx_range->max),
+ freq_suffixes, NUM_FREQ_SUFFIXES, &ok);
+ if (!ok) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ rate.integer = freq;
+ rate.num = 0;
+ rate.den = 1;
+
+ status = bladerf_set_rational_sample_rate(dev, BLADERF_CHANNEL_RX(0),
+ &rate, &actual);
+ if (status < 0) {
+ return status;
+ }
+
+ status = bladerf_set_rational_sample_rate(dev, BLADERF_CHANNEL_TX(0),
+ &rate, &actual);
+ } else if (!strcasecmp(opt.key, "bandwidth")) {
+ status =
+ bladerf_get_bandwidth_range(dev, BLADERF_CHANNEL_RX(0), &rx_range);
+ if (status < 0) {
+ return status;
+ }
+
+ status =
+ bladerf_get_bandwidth_range(dev, BLADERF_CHANNEL_TX(0), &tx_range);
+ if (status < 0) {
+ return status;
+ }
+
+ if (MIN(rx_range->max, tx_range->max) >= UINT32_MAX) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ bw = str2uint_suffix(
+ opt.value, (bladerf_bandwidth)MAX(rx_range->min, tx_range->min),
+ (bladerf_bandwidth)MIN(rx_range->max, tx_range->max), freq_suffixes,
+ NUM_FREQ_SUFFIXES, &ok);
+ if (!ok) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_set_bandwidth(dev, BLADERF_CHANNEL_RX(0), bw, NULL);
+ if (status < 0) {
+ return status;
+ }
+
+ status = bladerf_set_bandwidth(dev, BLADERF_CHANNEL_TX(0), bw, NULL);
+ } else if (!strcasecmp(opt.key, "agc")) {
+ bool agcval = false;
+
+ status = str2bool(opt.value, &agcval);
+ if (status != 0) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ gain_mode = agcval ? BLADERF_GAIN_AUTOMATIC : BLADERF_GAIN_MANUAL;
+ status = bladerf_set_gain_mode(dev, BLADERF_CHANNEL_RX(0), gain_mode);
+ } else if (!strcasecmp(opt.key, "gpio")) {
+ val = str2uint(opt.key, 0, -1, &ok);
+ if (!ok) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_config_gpio_write(dev, val);
+ } else if (!strcasecmp(opt.key, "sampling")) {
+ if (!strcasecmp(opt.value, "internal")) {
+ sampling_mode = BLADERF_SAMPLING_INTERNAL;
+ } else if (!strcasecmp(opt.value, "external")) {
+ sampling_mode = BLADERF_SAMPLING_EXTERNAL;
+ } else {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_set_sampling(dev, sampling_mode);
+ } else if (!strcasecmp(opt.key, "trimdac")) {
+ val = str2uint(opt.value, 0, -1, &ok);
+ if (!ok) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_dac_write(dev, val);
+ } else if (!strcasecmp(opt.key, "vctcxo_tamer")) {
+ if (!strcasecmp(opt.value, "disabled") ||
+ !strcasecmp(opt.value, "off")) {
+ tamer_mode = BLADERF_VCTCXO_TAMER_DISABLED;
+ } else if (!strcasecmp(opt.value, "1PPS") ||
+ !strcasecmp(opt.value, "1 PPS")) {
+ tamer_mode = BLADERF_VCTCXO_TAMER_1_PPS;
+ } else if (!strcasecmp(opt.value, "10MHZ") ||
+ !strcasecmp(opt.value, "10 MHZ")) {
+ tamer_mode = BLADERF_VCTCXO_TAMER_10_MHZ;
+ } else if (!strcasecmp(opt.value, "10M")) {
+ tamer_mode = BLADERF_VCTCXO_TAMER_10_MHZ;
+ } else {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_set_vctcxo_tamer_mode(dev, tamer_mode);
+ } else if (!strcasecmp(opt.key, "clock_ref")) {
+ bool enable = false;
+
+ status = str2bool(opt.value, &enable);
+ if (status != 0) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_set_pll_enable(dev, enable);
+ } else if (!strcasecmp(opt.key, "refin_freq")) {
+ status = bladerf_get_pll_refclk_range(dev, &rx_range);
+ if (status < 0) {
+ return status;
+ }
+
+ freq = str2uint64_suffix(opt.value, rx_range->min, rx_range->max,
+ freq_suffixes, NUM_FREQ_SUFFIXES, &ok);
+ if (!ok) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_set_pll_refclk(dev, freq);
+ } else if (!strcasecmp(opt.key, "clock_sel")) {
+ bladerf_clock_select clock_sel = CLOCK_SELECT_ONBOARD;
+
+ if (!strcasecmp(opt.value, "onboard") ||
+ !strcasecmp(opt.value, "internal")) {
+ clock_sel = CLOCK_SELECT_ONBOARD;
+ } else if (!strcasecmp(opt.value, "external")) {
+ clock_sel = CLOCK_SELECT_EXTERNAL;
+ } else {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_set_clock_select(dev, clock_sel);
+ } else if (!strcasecmp(opt.key, "clock_out")) {
+ bool enable = false;
+
+ status = str2bool(opt.value, &enable);
+ if (status != 0) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = bladerf_set_clock_output(dev, enable);
+ } else {
+ log_warning("Invalid key `%s' on line %d\n", opt.key, opt.lineno);
+ }
+
+ if (status < 0)
+ log_warning("Error message for option (%s) on line %d:\n%s\n", opt.key,
+ opt.lineno, bladerf_strerror(status));
+
+ return status;
+}
+
+int config_load_options_file(struct bladerf *dev)
+{
+ char *filename = NULL;
+ int status = 0;
+
+ uint8_t *buf = NULL;
+ size_t buf_size;
+
+ int optc;
+ int j;
+ struct config_options *optv;
+
+ filename = file_find("bladeRF.conf");
+ if (!filename) {
+ filename = file_find("bladerf.conf");
+
+ /* A missing file that is optional is not an error */
+ if (!filename) {
+ return 0;
+ }
+ }
+
+ status = file_read_buffer(filename, &buf, &buf_size);
+ if (status < 0) {
+ goto out;
+ }
+
+ optc = str2options(dev, (const char *)buf, buf_size, &optv);
+ if (optc < 0) {
+ status = BLADERF_ERR_INVAL;
+ goto out_buf;
+ }
+
+ for (j = 0; j < optc; j++) {
+ status = apply_config_options(dev, optv[j]);
+ if (status < 0) {
+ log_warning("Invalid config option `%s' on line %d\n", optv[j].key,
+ optv[j].lineno);
+ /* Some config options will require the FPGA to be loaded, however
+ * this function is called during bladerf_open(). The solution is
+ * to treat BLADERF_ERR_NOT_INIT as a warning and continue. */
+ if (status == BLADERF_ERR_NOT_INIT) {
+ status = 0;
+ } else {
+ break;
+ }
+ }
+ }
+
+ free_opts(optv, optc);
+
+out_buf:
+ free(buf);
+out:
+ free(filename);
+ return status;
+}
diff --git a/Radio/HW/BladeRF/src/helpers/configfile.h b/Radio/HW/BladeRF/src/helpers/configfile.h
new file mode 100644
index 0000000..390a4cf
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/configfile.h
@@ -0,0 +1,27 @@
+/**
+ * @file configfile.h
+ *
+ * @brief Configuration file support for libbladeRF
+ *
+ * Copyright (C) 2013-2018 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef CONFIGFILE_H_
+#define CONFIGFILE_H_
+
+int config_load_options_file(struct bladerf *dev);
+
+#endif // CONFIGFILE_H_
diff --git a/Radio/HW/BladeRF/src/helpers/file.c b/Radio/HW/BladeRF/src/helpers/file.c
new file mode 100644
index 0000000..0977137
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/file.c
@@ -0,0 +1,511 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013-2014 Nuand LLC
+ * Copyright (C) 2013 Daniel Gröber <dxld ÄT darkboxed DOT org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+
+#include <libbladeRF.h>
+
+#include "host_config.h"
+#include "minmax.h"
+#include "log.h"
+#include "rel_assert.h"
+
+#include "helpers/file.h"
+
+/* Paths to search for bladeRF files */
+struct search_path_entries {
+ bool prepend_home;
+ const char *path;
+};
+
+int file_write(FILE *f, uint8_t *buf, size_t len)
+{
+ size_t rv;
+
+ rv = fwrite(buf, 1, len, f);
+ if(rv < len) {
+ log_debug("File write failed: %s\n", strerror(errno));
+ return BLADERF_ERR_IO;
+ }
+
+ return 0;
+}
+
+int file_read(FILE *f, char *buf, size_t len)
+{
+ size_t rv;
+
+ rv = fread(buf, 1, len, f);
+ if(rv < len) {
+ if(feof(f))
+ log_debug("Unexpected end of file: %s\n", strerror(errno));
+ else
+ log_debug("Error reading file: %s\n", strerror(errno));
+
+ return BLADERF_ERR_IO;
+ }
+
+ return 0;
+}
+
+ssize_t file_size(FILE *f)
+{
+ ssize_t rv = BLADERF_ERR_IO;
+ long int fpos = ftell(f);
+ long len;
+
+ if(fpos < 0) {
+ log_verbose("ftell failed: %s\n", strerror(errno));
+ goto out;
+ }
+
+ if(fseek(f, 0, SEEK_END)) {
+ log_verbose("fseek failed: %s\n", strerror(errno));
+ goto out;
+ }
+
+ len = ftell(f);
+ if(len < 0) {
+ log_verbose("ftell failed: %s\n", strerror(errno));
+ goto out;
+ } else if (len == LONG_MAX) {
+ log_debug("ftell called with a directory?\n");
+ goto out;
+ }
+
+ if(fseek(f, fpos, SEEK_SET)) {
+ log_debug("fseek failed: %s\n", strerror(errno));
+ goto out;
+ }
+
+ rv = (ssize_t) len;
+ assert(rv == len);
+
+out:
+ return rv;
+}
+
+int file_read_buffer(const char *filename, uint8_t **buf_ret, size_t *size_ret)
+{
+ int status = BLADERF_ERR_UNEXPECTED;
+ FILE *f;
+ uint8_t *buf = NULL;
+ ssize_t len;
+
+ f = fopen(filename, "rb");
+ if (!f) {
+ log_error("%s: could not open %s: %s\n", __FUNCTION__, filename,
+ strerror(errno));
+ switch (errno) {
+ case ENOENT:
+ return BLADERF_ERR_NO_FILE;
+
+ case EACCES:
+ return BLADERF_ERR_PERMISSION;
+
+ default:
+ return BLADERF_ERR_IO;
+ }
+ }
+
+ len = file_size(f);
+ if (len < 0) {
+ status = BLADERF_ERR_IO;
+ goto out;
+ }
+
+ buf = (uint8_t *)malloc(len);
+ if (!buf) {
+ status = BLADERF_ERR_MEM;
+ goto out;
+ }
+
+ status = file_read(f, (char *)buf, len);
+ if (status < 0) {
+ goto out;
+ }
+
+ *buf_ret = buf;
+ *size_ret = len;
+ fclose(f);
+ return 0;
+
+out:
+ free(buf);
+ if (f) {
+ fclose(f);
+ }
+
+ return status;
+}
+
+/* Remove the last entry in a path. This is used to strip the executable name
+* from a path to get the directory that the executable resides in. */
+static size_t strip_last_path_entry(char *buf, char dir_delim)
+{
+ size_t len = strlen(buf);
+
+ while (len > 0 && buf[len - 1] != dir_delim) {
+ buf[len - 1] = '\0';
+ len--;
+ }
+
+ return len;
+}
+
+#if BLADERF_OS_LINUX || BLADERF_OS_OSX || BLADERF_OS_FREEBSD
+#define ACCESS_FILE_EXISTS F_OK
+#define DIR_DELIMETER '/'
+
+static const struct search_path_entries search_paths[] = {
+ { false, "" },
+ { true, "/.config/Nuand/bladeRF/" },
+ { true, "/.Nuand/bladeRF/" },
+
+ /* LIBBLADERF_SEARCH_PATH_PREFIX is defined in the libbladeRF
+ * CMakeLists.txt file. It defaults to ${CMAKE_INSTALL_PREFIX}, but
+ * can be overridden via -DLIBBLADERF_SEARCH_PATH_OVERRIDE
+ */
+ //{ false, LIBBLADERF_SEARCH_PREFIX "/etc/Nuand/bladeRF/" },
+ //{ false, LIBBLADERF_SEARCH_PREFIX "/share/Nuand/bladeRF/" },
+
+ /* These two entries are here for reverse compatibility.
+ *
+ * We failed to prefix ${CMAKE_INSTALL_PREFIX} on these from the beginning,
+ * forcing package maintainers to hard-code one of these locations,
+ * despite having a different ${CMAKE_INSTALL_PREFIX}.
+ *
+ * We'll keep these around for some time as fall-backs, as not to break
+ * existing packaging scripts.
+ */
+ { false, "/etc/Nuand/bladeRF/" },
+ { false, "/usr/share/Nuand/bladeRF/" },
+};
+
+static inline size_t get_home_dir(char *buf, size_t max_len)
+{
+ const char *home;
+
+ home = getenv("HOME");
+ if (home != NULL && strlen(home) > 0 && strlen(home) < max_len) {
+ strncat(buf, home, max_len);
+ } else {
+ const struct passwd *passwd;
+ const uid_t uid = getuid();
+ passwd = getpwuid(uid);
+ strncat(buf, passwd->pw_dir, max_len);
+ }
+
+ return strlen(buf);
+}
+
+static inline size_t get_install_dir(char *buf, size_t max_len)
+{
+ return 0;
+}
+
+#if BLADERF_OS_LINUX
+static inline size_t get_binary_dir(char *buf, size_t max_len)
+{
+ ssize_t result = readlink("/proc/self/exe", buf, max_len);
+
+ if (result > 0) {
+ return strip_last_path_entry(buf, DIR_DELIMETER);
+ } else {
+ return 0;
+ }
+}
+#elif BLADERF_OS_FREEBSD
+static inline size_t get_binary_dir(char *buf, size_t max_len)
+{
+ int mib[4];
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PATHNAME;
+ mib[3] = -1;
+ ssize_t result = sysctl(mib, 4, buf, &max_len, NULL, 0);
+
+ if (result > 0) {
+ return strip_last_path_entry(buf, DIR_DELIMETER);
+ } else {
+ return 0;
+ }
+}
+#elif BLADERF_OS_OSX
+#include <mach-o/dyld.h>
+static inline size_t get_binary_dir(char *buf, size_t max_len)
+{
+ uint32_t buf_size = max_len;
+ int status = _NSGetExecutablePath(buf, &buf_size);
+
+ if (status == 0) {
+ return strip_last_path_entry(buf, DIR_DELIMETER);
+ } else {
+ return 0;
+ }
+
+}
+#endif
+
+#elif BLADERF_OS_WINDOWS
+#define ACCESS_FILE_EXISTS 0
+#define DIR_DELIMETER '\\'
+#include <shlobj.h>
+
+static const struct search_path_entries search_paths[] = {
+ { false, "" },
+ { true, "/Nuand/bladeRF/" },
+};
+
+static inline size_t get_home_dir(char *buf, size_t max_len)
+{
+ /* Explicitly link to a runtime DLL to get SHGetKnownFolderPath.
+ * This deals with the case where we might not be able to staticly
+ * link it at build time, e.g. mingw32.
+ *
+ * http://msdn.microsoft.com/en-us/library/784bt7z7.aspx
+ */
+ typedef HRESULT (CALLBACK* LPFNSHGKFP_T)(REFKNOWNFOLDERID, DWORD, HANDLE, PWSTR*);
+ HINSTANCE hDLL; // Handle to DLL
+ LPFNSHGKFP_T lpfnSHGetKnownFolderPath; // Function pointer
+
+ const KNOWNFOLDERID folder_id = FOLDERID_RoamingAppData;
+ PWSTR path;
+ HRESULT status;
+
+ assert(max_len < INT_MAX);
+
+ hDLL = LoadLibrary("Shell32");
+ if (hDLL == NULL) {
+ // DLL couldn't be loaded, bail out.
+ return 0;
+ }
+
+ lpfnSHGetKnownFolderPath = (LPFNSHGKFP_T)GetProcAddress(hDLL, "SHGetKnownFolderPath");
+
+ if (!lpfnSHGetKnownFolderPath) {
+ // Can't find the procedure we want. Free and bail.
+ FreeLibrary(hDLL);
+ return 0;
+ }
+
+ status = lpfnSHGetKnownFolderPath(&folder_id, 0, NULL, &path);
+ if (status == S_OK) {
+ WideCharToMultiByte(CP_ACP, 0, path, -1, buf, (int)max_len, NULL, NULL);
+ CoTaskMemFree(path);
+ }
+
+ FreeLibrary(hDLL);
+
+ return strlen(buf);
+}
+
+static inline size_t get_binary_dir(char *buf, size_t max_len)
+{
+ DWORD status;
+
+ assert(max_len <= MAXDWORD);
+ status = GetModuleFileName(NULL, buf, (DWORD) max_len);
+
+ if (status > 0) {
+ return strip_last_path_entry(buf, DIR_DELIMETER);
+ } else {
+ return 0;
+ }
+}
+
+static inline size_t get_install_dir(char *buf, size_t max_len)
+{
+ typedef LONG (CALLBACK* LPFNREGOPEN_T)(HKEY, LPCTSTR, DWORD, REGSAM, PHKEY);
+ typedef LONG (CALLBACK* LPFNREGQUERY_T)(HKEY, LPCTSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD);
+ typedef LONG (CALLBACK* LPFNREGCLOSE_T)(HKEY);
+
+ HINSTANCE hDLL; // Handle to DLL
+ LPFNREGOPEN_T lpfnRegOpenKeyEx; // Function pointer
+ LPFNREGQUERY_T lpfnRegQueryValueEx; // Function pointer
+ LPFNREGCLOSE_T lpfnRegCloseKey; // Function pointer
+ HKEY hk;
+ DWORD len;
+
+ assert(max_len < INT_MAX);
+
+ memset(buf, 0, max_len);
+ hDLL = LoadLibrary("Advapi32");
+ if (hDLL == NULL) {
+ // DLL couldn't be loaded, bail out.
+ return 0;
+ }
+
+ lpfnRegOpenKeyEx = (LPFNREGOPEN_T)GetProcAddress(hDLL, "RegOpenKeyExA");
+ if (!lpfnRegOpenKeyEx) {
+ // Can't find the procedure we want. Free and bail.
+ FreeLibrary(hDLL);
+ return 0;
+ }
+
+ lpfnRegQueryValueEx = (LPFNREGQUERY_T)GetProcAddress(hDLL, "RegQueryValueExA");
+ if (!lpfnRegQueryValueEx) {
+ // Can't find the procedure we want. Free and bail.
+ FreeLibrary(hDLL);
+ return 0;
+ }
+
+ lpfnRegCloseKey = (LPFNREGCLOSE_T)GetProcAddress(hDLL, "RegCloseKey");
+ if (!lpfnRegCloseKey) {
+ // Can't find the procedure we want. Free and bail.
+ FreeLibrary(hDLL);
+ return 0;
+ }
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Nuand LLC", 0, KEY_READ, &hk)) {
+ FreeLibrary(hDLL);
+ return 0;
+ }
+
+ len = (DWORD)max_len;
+ if (RegQueryValueEx(hk, "Path", 0, NULL, (LPBYTE) buf, &len) == ERROR_SUCCESS) {
+ if (len > 0 && len < max_len && buf[len - 1] != '\\')
+ strcat(buf, "\\");
+ } else
+ len = 0;
+
+ if (len) {
+ lpfnRegCloseKey(hk);
+ }
+
+ FreeLibrary(hDLL);
+
+ return len;
+}
+
+#else
+#error "Unknown OS or missing BLADERF_OS_* definition"
+#endif
+
+/* We're not using functions that use the *nix PATH_MAX (which is known to be
+ * problematic), or the WIN32 MAX_PATH. Therefore, we'll just use this
+ * arbitrary, but "sufficiently" large max buffer size for paths */
+#define PATH_MAX_LEN 4096
+
+char *file_find(const char *filename)
+{
+ size_t i, max_len;
+ char *full_path;
+ const char *env_var;
+
+ full_path = calloc(PATH_MAX_LEN+1, 1);
+ if (full_path == NULL) {
+ return NULL;
+ }
+
+ /* Check directory specified by environment variable */
+ env_var = getenv("BLADERF_SEARCH_DIR");
+ if (env_var != NULL) {
+ strncat(full_path, env_var, PATH_MAX_LEN - 1);
+ full_path[strlen(full_path)] = DIR_DELIMETER;
+
+ max_len = PATH_MAX_LEN - strlen(full_path);
+
+ if (max_len >= strlen(filename)) {
+ strncat(full_path, filename, max_len);
+ if (access(full_path, ACCESS_FILE_EXISTS) != -1) {
+ return full_path;
+ }
+ }
+ }
+
+ /* Check the directory containing the currently running binary */
+ memset(full_path, 0, PATH_MAX_LEN);
+ max_len = PATH_MAX_LEN - 1;
+
+ if (get_binary_dir(full_path, max_len) != 0) {
+ max_len -= strlen(full_path);
+
+ if (max_len >= strlen(filename)) {
+ strncat(full_path, filename, max_len);
+ if (access(full_path, ACCESS_FILE_EXISTS) != -1) {
+ return full_path;
+ }
+ } else {
+ log_debug("Skipping search for %s in %s. "
+ "Path would be truncated.\n",
+ filename, full_path);
+ }
+ }
+
+ /* Search our list of pre-determined paths */
+ for (i = 0; i < ARRAY_SIZE(search_paths); i++) {
+ memset(full_path, 0, PATH_MAX_LEN);
+ max_len = PATH_MAX_LEN;
+
+ if (search_paths[i].prepend_home) {
+ const size_t len = get_home_dir(full_path, max_len);
+
+ if (len != 0) {
+ max_len -= len;
+ } else {
+ continue;
+ }
+
+ }
+
+ strncat(full_path, search_paths[i].path, max_len);
+ max_len = PATH_MAX_LEN - strlen(full_path);
+
+ if (max_len >= strlen(filename)) {
+ strncat(full_path, filename, max_len);
+
+ if (access(full_path, ACCESS_FILE_EXISTS) != -1) {
+ return full_path;
+ }
+ } else {
+ log_debug("Skipping search for %s in %s. "
+ "Path would be truncated.\n",
+ filename, full_path);
+ }
+ }
+
+ /* Search the installation directory, if applicable */
+ if (get_install_dir(full_path, PATH_MAX_LEN)) {
+ max_len = PATH_MAX_LEN - strlen(full_path);
+
+ if (max_len >= strlen(filename)) {
+ strncat(full_path, filename, max_len);
+ if (access(full_path, ACCESS_FILE_EXISTS) != -1) {
+ return full_path;
+ }
+ } else {
+ log_debug("Skipping search for %s in %s. "
+ "Path would be truncated.\n",
+ filename, full_path);
+ }
+ }
+
+ free(full_path);
+ return NULL;
+}
diff --git a/Radio/HW/BladeRF/src/helpers/file.h b/Radio/HW/BladeRF/src/helpers/file.h
new file mode 100644
index 0000000..a1db20a
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/file.h
@@ -0,0 +1,97 @@
+/**
+ * @file file.h
+ *
+ * @brief Wrappers around misc. file operations.
+ *
+ * These are collected here so that they may easily be dummied out for NIOS
+ * headless builds in the future.
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ * Copyright (C) 2013 Daniel Gröber <dxld ÄT darkboxed DOT org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef HELPERS_FILE_H_
+#define HELPERS_FILE_H_
+
+#include <stdint.h>
+#include <stdio.h>
+
+/**
+ * Read file contents into a buffer allocated internally and returned to the
+ * caller through `buf'.
+ *
+ * The caller is responsible for freeing the allocated buffer
+ *
+ * @param[in] filename File open
+ * @param[out] buf Upon success, this will point to a heap-allocated
+ * buffer containing the file contents
+ * @parma[out] size Upon success, this will be updated to reflect the
+ * size of the buffered file
+ *
+ * @return 0 on success, negative BLADERF_ERR_* value on failure
+ */
+int file_read_buffer(const char *filename, uint8_t **buf, size_t *size);
+
+/**
+ * Write to an open file stream.
+ *
+ * @param[in] f Open file stream.
+ * @param[in] buf Data to write to the stream.
+ * @parma[in] len Number of bytes to write to the stream.
+ *
+ * @return 0 on success, negative BLADERF_ERR_* value on failure
+ */
+int file_write(FILE *f, uint8_t *buf, size_t len);
+
+/**
+ * Read data from an open file stream.
+ *
+ * @param[in] f Open file stream.
+ * @param[out] buf Buffer to fill with data read.
+ * @parma[in] len Number of bytes to read. If EOF is encountered
+ * before this many bytes have been read will return
+ * an error.
+ *
+ * @return 0 on success, negative BLADERF_ERR_* value on failure
+ */
+int file_read(FILE *f, char *buf, size_t len);
+
+/**
+ * Determine the size of an open file stream.
+ *
+ * @param[in] f Open file stream.
+ *
+ * @return positive size of file on success, negative BLADERF_ERR_* value on
+ * failure
+ */
+ssize_t file_size(FILE *f);
+
+/**
+ * Search for the specified filename in bladeRF config directories. If found,
+ * the full path is returned. There is a chance that the file will be removed
+ * in between this call indicating it exists and attempting to open it.
+ *
+ * @param[in] filename File to search for
+ *
+ * @return Full path if the file is found, NULL otherwise.
+ */
+char *file_find(const char *filename);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/helpers/have_cap.h b/Radio/HW/BladeRF/src/helpers/have_cap.h
new file mode 100644
index 0000000..f6ed471
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/have_cap.h
@@ -0,0 +1,43 @@
+/**
+ * @file have_cap.h
+ *
+ * @brief Convenience wrapper for testing capabilities mask
+ *
+ * Copyright (C) 2020 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef HAVE_CAP_H_
+#define HAVE_CAP_H_
+
+#include <stdint.h>
+
+/**
+ * Convenience wrapper for testing capabilities mask
+ */
+static inline bool have_cap(uint64_t capabilities, uint64_t capability)
+{
+ return (capabilities & capability) != 0;
+}
+
+/**
+ * Convenience wrapper for testing capabilities mask
+ */
+static inline bool have_cap_dev(struct bladerf *dev, uint64_t capability)
+{
+ uint64_t capabilities = dev->board->get_capabilities(dev);
+ return (capabilities & capability) != 0;
+}
+#endif // HAVE_CAP_H_
diff --git a/Radio/HW/BladeRF/src/helpers/interleave.c b/Radio/HW/BladeRF/src/helpers/interleave.c
new file mode 100644
index 0000000..0989c4c
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/interleave.c
@@ -0,0 +1,182 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2017 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+
+#include <libbladeRF.h>
+
+#include "helpers/interleave.h"
+
+size_t _interleave_calc_num_channels(bladerf_channel_layout layout)
+{
+ switch (layout) {
+ case BLADERF_RX_X1:
+ case BLADERF_TX_X1:
+ return 1;
+ case BLADERF_RX_X2:
+ case BLADERF_TX_X2:
+ return 2;
+ }
+
+ return 0;
+}
+
+size_t _interleave_calc_bytes_per_sample(bladerf_format format)
+{
+ switch (format) {
+ case BLADERF_FORMAT_SC8_Q7:
+ case BLADERF_FORMAT_SC8_Q7_META:
+ return 2;
+
+ case BLADERF_FORMAT_SC16_Q11:
+ case BLADERF_FORMAT_SC16_Q11_META:
+ case BLADERF_FORMAT_PACKET_META:
+ return 4;
+ }
+
+ return 0;
+}
+
+size_t _interleave_calc_metadata_bytes(bladerf_format format)
+{
+ switch (format) {
+ case BLADERF_FORMAT_SC8_Q7_META:
+ case BLADERF_FORMAT_SC16_Q11_META:
+ case BLADERF_FORMAT_PACKET_META:
+ return 0x10;
+ case BLADERF_FORMAT_SC8_Q7:
+ case BLADERF_FORMAT_SC16_Q11:
+ return 0;
+ }
+
+ return 0;
+}
+
+int _interleave_interleave_buf(bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int buffer_size,
+ void *samples)
+{
+ void *buf;
+ uint8_t *srcptr, *dstptr;
+ size_t num_channels = _interleave_calc_num_channels(layout);
+ size_t samp_size, meta_size, samps_per_ch;
+ size_t srcidx, dstidx, samp, ch;
+
+ // Easy:
+ if (num_channels < 2) {
+ return 0;
+ }
+
+ // Placeholder for an actually efficient algorithm
+ samp_size = _interleave_calc_bytes_per_sample(format);
+ meta_size = _interleave_calc_metadata_bytes(format);
+ samps_per_ch = buffer_size / num_channels;
+ buf = malloc(samp_size * buffer_size);
+ srcptr = samples;
+ dstptr = buf;
+
+ if (NULL == buf) {
+ return BLADERF_ERR_MEM;
+ }
+
+ // Copy metadata if applicable
+ if (meta_size > 0) {
+ memcpy(dstptr, srcptr, meta_size);
+ srcptr += meta_size;
+ dstptr += meta_size;
+ samps_per_ch -= (meta_size / samp_size / num_channels);
+ }
+
+ // Iterate...
+ for (ch = 0; ch < num_channels; ++ch) {
+ srcidx = samps_per_ch * ch;
+ for (samp = 0; samp < samps_per_ch; ++samp) {
+ dstidx = (samp * num_channels) + ch;
+ memcpy(dstptr + (dstidx * samp_size),
+ srcptr + ((srcidx + samp) * samp_size),
+ samp_size);
+ }
+ }
+
+ // Copy back...
+ memcpy(samples, buf, buffer_size * samp_size);
+
+ // Done
+ free(buf);
+
+ return 0;
+}
+
+int _interleave_deinterleave_buf(bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int buffer_size,
+ void *samples)
+{
+ void *buf;
+ uint8_t *srcptr, *dstptr;
+ size_t num_channels = _interleave_calc_num_channels(layout);
+ size_t samp_size, meta_size, samps_per_ch;
+ size_t srcidx, dstidx, samp, ch;
+
+ // Easy:
+ if (num_channels < 2) {
+ return 0;
+ }
+
+ // Placeholder for an actually efficient algorithm
+ samp_size = _interleave_calc_bytes_per_sample(format);
+ meta_size = _interleave_calc_metadata_bytes(format);
+ samps_per_ch = buffer_size / num_channels;
+ buf = malloc(samp_size * buffer_size);
+ srcptr = samples;
+ dstptr = buf;
+
+ if (NULL == buf) {
+ return BLADERF_ERR_MEM;
+ }
+
+ // Copy metadata if applicable
+ if (meta_size > 0) {
+ memcpy(dstptr, srcptr, meta_size);
+ srcptr += meta_size;
+ dstptr += meta_size;
+ samps_per_ch -= (meta_size / samp_size / num_channels);
+ }
+
+ // Iterate...
+ for (samp = 0; samp < samps_per_ch; ++samp) {
+ srcidx = num_channels * samp;
+ for (ch = 0; ch < num_channels; ++ch) {
+ dstidx = (samps_per_ch * ch) + samp;
+ memcpy(dstptr + (dstidx * samp_size),
+ srcptr + ((srcidx + ch) * samp_size), samp_size);
+ }
+ }
+
+ // Copy back...
+ memcpy(samples, buf, buffer_size * samp_size);
+
+ // Done
+ free(buf);
+
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/src/helpers/interleave.h b/Radio/HW/BladeRF/src/helpers/interleave.h
new file mode 100644
index 0000000..526dc41
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/interleave.h
@@ -0,0 +1,44 @@
+/**
+ * @file interleave.h
+ *
+ * This file is not part of the API and may be changed at any time.
+ * If you're interfacing with libbladeRF, DO NOT use this file.
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2017 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef HELPERS_INTERLEAVE_H_
+#define HELPERS_INTERLEAVE_H_
+
+size_t _interleave_calc_bytes_per_sample(bladerf_format format);
+size_t _interleave_calc_metadata_bytes(bladerf_format format);
+size_t _interleave_calc_num_channels(bladerf_channel_layout layout);
+
+int _interleave_interleave_buf(bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int buffer_size,
+ void *samples);
+
+int _interleave_deinterleave_buf(bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int buffer_size,
+ void *samples);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/helpers/timeout.c b/Radio/HW/BladeRF/src/helpers/timeout.c
new file mode 100644
index 0000000..d0f680b
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/timeout.c
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "host_config.h"
+
+#if BLADERF_OS_WINDOWS || BLADERF_OS_OSX
+#include "clock_gettime.h"
+#else
+#include <time.h>
+#endif
+
+#include <libbladeRF.h>
+
+int populate_abs_timeout(struct timespec *t, unsigned int timeout_ms)
+{
+ static const int nsec_per_sec = 1000 * 1000 * 1000;
+ const unsigned int timeout_sec = timeout_ms / 1000;
+ int status;
+
+ status = clock_gettime(CLOCK_REALTIME, t);
+ if (status != 0) {
+ return BLADERF_ERR_UNEXPECTED;
+ } else {
+ t->tv_sec += timeout_sec;
+ t->tv_nsec += (timeout_ms % 1000) * 1000 * 1000;
+
+ if (t->tv_nsec >= nsec_per_sec) {
+ t->tv_sec += t->tv_nsec / nsec_per_sec;
+ t->tv_nsec %= nsec_per_sec;
+ }
+
+ return 0;
+ }
+}
diff --git a/Radio/HW/BladeRF/src/helpers/timeout.h b/Radio/HW/BladeRF/src/helpers/timeout.h
new file mode 100644
index 0000000..1b6cb7c
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/timeout.h
@@ -0,0 +1,40 @@
+/**
+ * @file timeout.h
+ *
+ * This file is not part of the API and may be changed at any time.
+ * If you're interfacing with libbladeRF, DO NOT use this file.
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013-2015 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef HELPERS_TIMEOUT_H_
+#define HELPERS_TIMEOUT_H_
+
+/**
+ * Populate the provided timeval structure for the specified timeout
+ *
+ * @param[out] t_abs Absolute timeout structure to populate
+ * @param[in] timeout_ms Desired timeout in ms.
+ *
+ * 0 on success, BLADERF_ERR_UNEXPECTED on failure
+ */
+int populate_abs_timeout(struct timespec *t_abs, unsigned int timeout_ms);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/helpers/version.c b/Radio/HW/BladeRF/src/helpers/version.c
new file mode 100644
index 0000000..a378d23
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/version.c
@@ -0,0 +1,177 @@
+/**
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014-2015 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "log.h"
+#include "rel_assert.h"
+
+#include "helpers/version.h"
+
+bool version_equal(const struct bladerf_version *v1,
+ const struct bladerf_version *v2)
+{
+ return v1->major == v2->major &&
+ v1->minor == v2->minor &&
+ v1->patch == v2->patch;
+}
+
+bool version_greater_or_equal(const struct bladerf_version *v1,
+ const struct bladerf_version *v2)
+{
+ return version_fields_greater_or_equal(v1, v2->major, v2->minor, v2->patch);
+}
+
+bool version_less_than(const struct bladerf_version *v1,
+ const struct bladerf_version *v2)
+{
+ return version_fields_less_than(v1, v2->major, v2->minor, v2->patch);
+}
+
+bool version_fields_greater_or_equal(const struct bladerf_version *version,
+ unsigned int major, unsigned int minor,
+ unsigned int patch)
+{
+ if (version->major > major) {
+ return true;
+ } else if ( (version->major == major) && (version->minor > minor) ) {
+ return true;
+ } else if ((version->major == major) &&
+ (version->minor == minor) &&
+ (version->patch >= patch) ) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool version_fields_less_than(const struct bladerf_version *version,
+ unsigned int major, unsigned int minor,
+ unsigned int patch)
+{
+ return !version_fields_greater_or_equal(version, major, minor, patch);
+}
+
+static const struct compat *find_fw_compat(const struct version_compat_table *fw_compat_table,
+ const struct bladerf_version *fw_version)
+{
+ const struct compat *newest_compat = &fw_compat_table->table[0];
+ size_t i;
+
+ /* Version is newer than what's in our table - complain */
+ if (version_less_than(&newest_compat->ver, fw_version)) {
+ log_info("Firmware version (v%u.%u.%u) is newer than entries in "
+ "libbladeRF's compatibility table. Please update libbladeRF "
+ "if problems arise.\n",
+ fw_version->major, fw_version->minor, fw_version->patch);
+ return newest_compat;
+ }
+
+ for (i = 0; i < fw_compat_table->len; i++) {
+ if (version_equal(fw_version, &fw_compat_table->table[i].ver)) {
+ return &fw_compat_table->table[i];
+ }
+ }
+
+ return NULL;
+}
+
+static const struct compat *find_fpga_compat(const struct version_compat_table *fpga_compat_table,
+ const struct bladerf_version *fpga_version)
+{
+ const struct compat *newest_compat = &fpga_compat_table->table[0];
+ size_t i;
+
+ /* Device's FPGA is newer than what's in our table - complain */
+ if (version_less_than(&newest_compat->ver, fpga_version)) {
+ log_info("FPGA version (v%u.%u.%u) is newer than entries in "
+ "libbladeRF's compatibility table. Please update libbladeRF "
+ "if problems arise.\n",
+ fpga_version->major, fpga_version->minor, fpga_version->patch);
+ return newest_compat;
+ }
+
+ for (i = 0; i < fpga_compat_table->len; i++) {
+ if (version_equal(fpga_version, &fpga_compat_table->table[i].ver)) {
+ return &fpga_compat_table->table[i];
+ }
+ }
+
+ return NULL;
+}
+
+int version_check_fw(const struct version_compat_table *fw_compat_table,
+ const struct bladerf_version *fw_version,
+ struct bladerf_version *required_fw_version)
+{
+ const struct bladerf_version *oldest_version = &fw_compat_table->table[fw_compat_table->len-1].ver;
+
+ if (required_fw_version) {
+ *required_fw_version = *oldest_version;
+ }
+
+ if (version_greater_or_equal(fw_version, oldest_version)) {
+ return 0;
+ }
+
+ return BLADERF_ERR_UPDATE_FW;
+}
+
+int version_check(const struct version_compat_table *fw_compat_table,
+ const struct version_compat_table *fpga_compat_table,
+ const struct bladerf_version *fw_version,
+ const struct bladerf_version *fpga_version,
+ struct bladerf_version *required_fw_version,
+ struct bladerf_version *required_fpga_version)
+{
+ const struct compat *fw_compat = find_fw_compat(fw_compat_table, fw_version);
+ const struct compat *fpga_compat = find_fpga_compat(fpga_compat_table, fpga_version);
+
+ if (fw_compat == NULL) {
+ log_debug("%s is missing FW version compat table entry?\n",
+ __FUNCTION__);
+ return BLADERF_ERR_UPDATE_FW;
+ } else if (fpga_compat == NULL) {
+ log_debug("%s is missing FPGA version compat table entry?\n",
+ __FUNCTION__);
+ return BLADERF_ERR_UPDATE_FPGA;
+ }
+
+ if (required_fw_version) {
+ *required_fw_version = fpga_compat->requires;
+ }
+ if (required_fpga_version) {
+ *required_fpga_version = fw_compat->requires;
+ }
+
+ /* Check if the FPGA meets the minimum requirements dictated by the
+ * firmware version */
+ if (version_less_than(fpga_version, &fw_compat->requires)) {
+ return BLADERF_ERR_UPDATE_FPGA;
+ }
+
+ /* Check if the firmware version meets the minimum requirements dictated
+ * by the FPGA version */
+ if (version_less_than(fw_version, &fpga_compat->requires)) {
+ return BLADERF_ERR_UPDATE_FW;
+ }
+
+ return 0;
+}
+
diff --git a/Radio/HW/BladeRF/src/helpers/version.h b/Radio/HW/BladeRF/src/helpers/version.h
new file mode 100644
index 0000000..b4e4559
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/version.h
@@ -0,0 +1,142 @@
+/**
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef HELPERS_VERSION_H_
+#define HELPERS_VERSION_H_
+
+#include <libbladeRF.h>
+
+#define BLADERF_VERSION_STR_MAX 32
+
+/**
+ * Test if two versions are equal. The "describe" field is not checked.
+ *
+ * @return true if equal, false otherwise
+ */
+bool version_equal(const struct bladerf_version *v1,
+ const struct bladerf_version *v2);
+
+/**
+ * Check if version v1 is greater than or equal to version v2.
+ *
+ * @param[in] v1 First version
+ * @param[in] v2 Second version
+ *
+ * @return true for greater or equal than, false otherwise
+ */
+bool version_greater_or_equal(const struct bladerf_version *v1,
+ const struct bladerf_version *v2);
+
+/**
+ * Check if version v1 is less than version v2.
+ *
+ * @param[in] v1 First version
+ * @param[in] v2 Second version
+ *
+ * @return true for less than, false otherwise
+ */
+bool version_less_than(const struct bladerf_version *v1,
+ const struct bladerf_version *v2);
+
+/**
+ * Check if version in the provided structure is greater or equal to
+ * the version specified by the provided major, minor, and patch values
+ *
+ * @param[in] version Version structure to check
+ * @param[in] major Minor version
+ * @param[in] minor Minor version
+ * @param[in] patch Patch version
+ *
+ * @return true for greater or equal than, false otherwise
+ */
+bool version_fields_greater_or_equal(const struct bladerf_version *version,
+ unsigned int major,
+ unsigned int minor,
+ unsigned int patch);
+
+/**
+ * Check if version in the provided structure is less than
+ * the version specied by the provided major, minor, and patch values
+ *
+ * @param[in] version Version structure to check
+ * @param[in] major Minor version
+ * @param[in] minor Minor version
+ * @param[in] patch Patch version
+ *
+ * @return true for less than, false otherwise
+ */
+bool version_fields_less_than(const struct bladerf_version *version,
+ unsigned int major,
+ unsigned int minor,
+ unsigned int patch);
+
+
+/* Version compatibility table structure. */
+struct version_compat_table {
+ const struct compat {
+ struct bladerf_version ver;
+ struct bladerf_version requires;
+ } * table;
+ unsigned int len;
+};
+
+/**
+ * Check if the firmware version is sufficient for the current libbladeRF
+ * version. If it's not, the user will need to use the bootloader to flash a
+ * newer version.
+ *
+ * @param[in] fw_compat_table Firmware-FPGA version compat. table
+ * @param[in] fw_version Firmware version
+ * @param[out] required_fw_version If not-NULL, populated with minimum
+ * required firmware version
+ *
+ * @return 0 if the FW version is sufficient for normal operation,
+ * BLADERF_ERR_UPDATE_FW if a firmware update is required.
+ */
+int version_check_fw(const struct version_compat_table *fw_compat_table,
+ const struct bladerf_version *fw_version,
+ struct bladerf_version *required_fw_version);
+
+/**
+ * Check if the firmware and FPGA versions are sufficient and compatible.
+ *
+ * @param[in] fw_compat_table Firmware-FPGA version compat. table
+ * @param[in] fpga_compat_table FPGA-Firmware version compat. table
+ * @param[in] fw_version Firmware version
+ * @param[in] fpga_version Firmware version
+ * @param[out] required_fw_version If not-NULL, populated with minimum
+ * required firmware version
+ * @param[out] required_fpga_version If not-NULL, populated with minimum
+ * required FPGA version
+ *
+ * @return 0 if the FPGA version is sufficient for normal operation,
+ * BLADERF_ERR_UPDATE_FPGA if the firmware requires a newer FPGA,
+ * BLADERF_ERR_UPDATE_FW if a firmware update is required to support
+ * the currently loaded FPGA.
+ */
+int version_check(const struct version_compat_table *fw_compat_table,
+ const struct version_compat_table *fpga_compat_table,
+ const struct bladerf_version *fw_version,
+ const struct bladerf_version *fpga_version,
+ struct bladerf_version *required_fw_version,
+ struct bladerf_version *required_fpga_version);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/helpers/wallclock.c b/Radio/HW/BladeRF/src/helpers/wallclock.c
new file mode 100644
index 0000000..c75ae1e
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/wallclock.c
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013-2018 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "wallclock.h"
+#include "host_config.h"
+
+#if BLADERF_OS_WINDOWS || BLADERF_OS_OSX
+#include "clock_gettime.h"
+#else
+#include <time.h>
+#endif
+
+uint64_t wallclock_get_current_nsec()
+{
+ static const int nsec_per_sec = 1000 * 1000 * 1000;
+ struct timespec t;
+ int status;
+ uint64_t rv;
+
+ status = clock_gettime(CLOCK_REALTIME, &t);
+ if (status != 0) {
+ rv = 0;
+ } else {
+ rv = (t.tv_sec * nsec_per_sec);
+ rv += (t.tv_nsec);
+ }
+
+ return rv;
+}
diff --git a/Radio/HW/BladeRF/src/helpers/wallclock.h b/Radio/HW/BladeRF/src/helpers/wallclock.h
new file mode 100644
index 0000000..d6fcbb9
--- /dev/null
+++ b/Radio/HW/BladeRF/src/helpers/wallclock.h
@@ -0,0 +1,29 @@
+/**
+ * @file wallclock.h
+ *
+ * @brief System clock access helpers
+ *
+ * Copyright (C) 2013-2018 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef WALLCLOCK_H_
+#define WALLCLOCK_H_
+
+#include <stdint.h>
+
+uint64_t wallclock_get_current_nsec();
+
+#endif // WALLCLOCK_H_
diff --git a/Radio/HW/BladeRF/src/init_fini.c b/Radio/HW/BladeRF/src/init_fini.c
new file mode 100644
index 0000000..f38eaf5
--- /dev/null
+++ b/Radio/HW/BladeRF/src/init_fini.c
@@ -0,0 +1,103 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include "conversions.h"
+#include "version.h"
+#if !defined(WIN32) && !defined(__CYGWIN__) && defined(LOG_SYSLOG_ENABLED)
+#include <syslog.h>
+#endif
+#include "log.h"
+
+#if !defined(WIN32) && !defined(__CYGWIN__)
+#if !defined(__clang__) && !defined(__GNUC__)
+#error init/fini mechanism not known to work for your compiler.
+#endif
+#define __init __attribute__((constructor))
+#define __fini __attribute__((destructor))
+#else
+/* Corresponding syntax for Windows (TBD) */
+#define __init
+#define __fini
+#endif
+
+#ifdef LOG_SYSLOG_ENABLED
+# define DEF_LOG_LEVEL BLADERF_LOG_LEVEL_WARNING
+#else
+# define DEF_LOG_LEVEL BLADERF_LOG_LEVEL_INFO
+#endif
+#define ENV_LOG_LEVEL "BLADERF_LOG_LEVEL"
+
+/* Module initializers/deinitializers. When used as library (who don't have
+ * a natural entry/exit function) these are used to initialize
+ * deinitialize. Use to set predefined/default states and cleanup.
+ *
+ * This will work with shared libraries as well as with static as they get
+ * invoked by RTL load/unload, with or without C++ code (i.e. functions will
+ * play nice with C++ normal ctors/dtors).
+ *
+ * Keep log in to at least once per new build-/run-environment assert that
+ * the mechanism works.
+ */
+
+static int get_loglevel(void) {
+ int log_level = DEF_LOG_LEVEL;
+
+ if ((getenv(ENV_LOG_LEVEL) != NULL)
+ && (strlen(getenv(ENV_LOG_LEVEL)) > 0)) {
+
+ bool valid_value;
+ log_level = str2loglevel(getenv(ENV_LOG_LEVEL), &valid_value);
+
+ if (!valid_value) {
+ log_level = DEF_LOG_LEVEL;
+ }
+ }
+ return log_level;
+}
+
+void __init __bladerf_init(void)
+{
+ int log_level = get_loglevel();
+
+#if !defined(WIN32) && !defined(__CYGWIN__) && defined(LOG_SYSLOG_ENABLED)
+ openlog("bladeRF",
+ LOG_CONS | LOG_NDELAY | LOG_NOWAIT | LOG_PERROR | LOG_PID,
+ LOG_USER);
+#endif
+
+ bladerf_log_set_verbosity(log_level);
+ log_debug("libbladeRF %s: initializing\n", LIBBLADERF_VERSION);
+}
+
+void __fini __bladerf_fini(void)
+{
+ int log_level = get_loglevel();
+
+ bladerf_log_set_verbosity(log_level);
+ log_debug("libbladeRF %s: deinitializing\n", LIBBLADERF_VERSION);
+ fflush(NULL);
+#if !defined(WIN32) && !defined(__CYGWIN__) && defined(LOG_SYSLOG_ENABLED)
+ closelog();
+#endif
+}
diff --git a/Radio/HW/BladeRF/src/libbladerf-Bridging-Header.h b/Radio/HW/BladeRF/src/libbladerf-Bridging-Header.h
new file mode 100644
index 0000000..a9fb786
--- /dev/null
+++ b/Radio/HW/BladeRF/src/libbladerf-Bridging-Header.h
@@ -0,0 +1,8 @@
+//
+// libbladerf-Bridging-Header.h
+// PrySDR
+//
+// Created by Jacky Jack on 01/11/2024.
+//
+
+#include "../include/libbladeRF.h"
diff --git a/Radio/HW/BladeRF/src/streaming/async.c b/Radio/HW/BladeRF/src/streaming/async.c
new file mode 100644
index 0000000..cffa9d2
--- /dev/null
+++ b/Radio/HW/BladeRF/src/streaming/async.c
@@ -0,0 +1,294 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "log.h"
+
+#include "backend/usb/usb.h"
+
+#include "async.h"
+#include "board/board.h"
+#include "helpers/timeout.h"
+#include "helpers/have_cap.h"
+
+int async_init_stream(struct bladerf_stream **stream,
+ struct bladerf *dev,
+ bladerf_stream_cb callback,
+ void ***buffers,
+ size_t num_buffers,
+ bladerf_format format,
+ size_t samples_per_buffer,
+ size_t num_transfers,
+ void *user_data)
+{
+ struct bladerf_stream *lstream;
+ size_t buffer_size_bytes;
+ size_t i;
+ int status = 0;
+
+ if (num_transfers > num_buffers) {
+ log_error("num_transfers must be <= num_buffers\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (samples_per_buffer < 1024 || samples_per_buffer % 1024 != 0) {
+ log_error("samples_per_buffer must be multiples of 1024\n");
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Create a stream and populate it with the appropriate information */
+ lstream = malloc(sizeof(struct bladerf_stream));
+
+ if (!lstream) {
+ return BLADERF_ERR_MEM;
+ }
+
+ MUTEX_INIT(&lstream->lock);
+
+ if (pthread_cond_init(&lstream->can_submit_buffer, NULL) != 0) {
+ free(lstream);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ if (pthread_cond_init(&lstream->stream_started, NULL) != 0) {
+ free(lstream);
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ lstream->dev = dev;
+ lstream->error_code = 0;
+ lstream->state = STREAM_IDLE;
+ lstream->samples_per_buffer = samples_per_buffer;
+ lstream->num_buffers = num_buffers;
+ lstream->format = format;
+ lstream->transfer_timeout = BULK_TIMEOUT_MS;
+ lstream->cb = callback;
+ lstream->user_data = user_data;
+ lstream->buffers = NULL;
+
+ if (format == BLADERF_FORMAT_PACKET_META) {
+ if (!have_cap_dev(dev, BLADERF_CAP_FW_SHORT_PACKET)) {
+ log_error("Firmware does not support short packets. "
+ "Upgrade to at least firmware version 2.4.0.");
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ if (!have_cap_dev(dev, BLADERF_CAP_FPGA_PACKET_META)) {
+ log_error("FPGA does not support packet meta format. "
+ "Upgrade to at least FPGA version 0.12.0 .");
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+ }
+
+ if (format == BLADERF_FORMAT_SC8_Q7 || format == BLADERF_FORMAT_SC8_Q7_META) {
+ if (!have_cap_dev(dev, BLADERF_CAP_FPGA_8BIT_SAMPLES)) {
+ log_error("FPGA does not support 8bit mode. "
+ "Upgrade to at least FPGA version 0.15.0.\n");
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+ }
+
+ switch(format) {
+ case BLADERF_FORMAT_SC8_Q7:
+ case BLADERF_FORMAT_SC8_Q7_META:
+ buffer_size_bytes = sc8q7_to_bytes(samples_per_buffer);
+ break;
+
+ case BLADERF_FORMAT_SC16_Q11:
+ case BLADERF_FORMAT_SC16_Q11_META:
+ buffer_size_bytes = sc16q11_to_bytes(samples_per_buffer);
+ break;
+
+ case BLADERF_FORMAT_PACKET_META:
+ buffer_size_bytes = samples_per_buffer;
+ break;
+
+ default:
+ status = BLADERF_ERR_INVAL;
+ break;
+ }
+
+ if (!status) {
+ lstream->buffers = calloc(num_buffers, sizeof(lstream->buffers[0]));
+ if (lstream->buffers) {
+ for (i = 0; i < num_buffers && !status; i++) {
+ lstream->buffers[i] = calloc(1, buffer_size_bytes);
+ if (!lstream->buffers[i]) {
+ status = BLADERF_ERR_MEM;
+ }
+ }
+ } else {
+ status = BLADERF_ERR_MEM;
+ }
+ }
+
+ /* Clean up everything we've allocated if we hit any errors */
+ if (status) {
+
+ if (lstream->buffers) {
+ for (i = 0; i < num_buffers; i++) {
+ free(lstream->buffers[i]);
+ }
+
+ free(lstream->buffers);
+ }
+
+ free(lstream);
+ } else {
+ /* Perform any backend-specific stream initialization */
+ status = dev->backend->init_stream(lstream, num_transfers);
+
+ if (status < 0) {
+ async_deinit_stream(lstream);
+ *stream = NULL;
+ } else {
+ /* Update the caller's pointers */
+ *stream = lstream;
+
+ if (buffers) {
+ *buffers = lstream->buffers;
+ }
+ }
+ }
+
+ return status;
+}
+
+int async_set_transfer_timeout(struct bladerf_stream *stream,
+ unsigned int transfer_timeout_ms)
+{
+ MUTEX_LOCK(&stream->lock);
+ stream->transfer_timeout = transfer_timeout_ms;
+ MUTEX_UNLOCK(&stream->lock);
+
+ return 0;
+}
+
+int async_get_transfer_timeout(struct bladerf_stream *stream,
+ unsigned int *transfer_timeout_ms)
+{
+ MUTEX_LOCK(&stream->lock);
+ *transfer_timeout_ms = stream->transfer_timeout;
+ MUTEX_UNLOCK(&stream->lock);
+
+ return 0;
+}
+
+int async_run_stream(struct bladerf_stream *stream, bladerf_channel_layout layout)
+{
+ int status;
+ struct bladerf *dev = stream->dev;
+
+ MUTEX_LOCK(&stream->lock);
+ stream->layout = layout;
+ stream->state = STREAM_RUNNING;
+ pthread_cond_signal(&stream->stream_started);
+ MUTEX_UNLOCK(&stream->lock);
+
+ status = dev->backend->stream(stream, layout);
+
+ /* Backend return value takes precedence over stream error status */
+ return status == 0 ? stream->error_code : status;
+}
+
+int async_submit_stream_buffer(struct bladerf_stream *stream,
+ void *buffer, size_t *length,
+ unsigned int timeout_ms,
+ bool nonblock)
+{
+ int status = 0;
+ struct timespec timeout_abs;
+
+ MUTEX_LOCK(&stream->lock);
+
+ if (buffer != BLADERF_STREAM_SHUTDOWN) {
+ if (stream->state != STREAM_RUNNING && timeout_ms != 0) {
+ status = populate_abs_timeout(&timeout_abs, timeout_ms);
+ if (status != 0) {
+ log_debug("Failed to populate timeout value\n");
+ goto error;
+ }
+ }
+
+ while (stream->state != STREAM_RUNNING) {
+ log_debug("Buffer submitted while stream's not running. "
+ "Waiting for stream to start.\n");
+
+ if (timeout_ms == 0) {
+ status = pthread_cond_wait(&stream->stream_started,
+ &stream->lock);
+ } else {
+ status = pthread_cond_timedwait(&stream->stream_started,
+ &stream->lock, &timeout_abs);
+ }
+
+ if (status == ETIMEDOUT) {
+ status = BLADERF_ERR_TIMEOUT;
+ log_debug("%s: %u ms timeout expired",
+ __FUNCTION__, timeout_ms);
+ goto error;
+ } else if (status != 0) {
+ status = BLADERF_ERR_UNEXPECTED;
+ goto error;
+ }
+ }
+ }
+
+ status = stream->dev->backend->submit_stream_buffer(stream, buffer,
+ length, timeout_ms, nonblock);
+
+error:
+ MUTEX_UNLOCK(&stream->lock);
+ return status;
+}
+
+void async_deinit_stream(struct bladerf_stream *stream)
+{
+ size_t i;
+
+ if (!stream) {
+ log_debug("%s called with NULL stream\n", __FUNCTION__);
+ return;
+ }
+
+ while(stream->state != STREAM_DONE && stream->state != STREAM_IDLE) {
+ log_verbose( "Stream not done...\n" );
+ usleep(1000000);
+ }
+
+ /* Free up the backend data */
+ stream->dev->backend->deinit_stream(stream);
+
+ /* Free up the buffers */
+ for (i = 0; i < stream->num_buffers; i++) {
+ free(stream->buffers[i]);
+ }
+
+ /* Free up the pointer to the buffers */
+ free(stream->buffers);
+
+ /* Free up the stream itself */
+ free(stream);
+}
+
diff --git a/Radio/HW/BladeRF/src/streaming/async.h b/Radio/HW/BladeRF/src/streaming/async.h
new file mode 100644
index 0000000..b0fd3b8
--- /dev/null
+++ b/Radio/HW/BladeRF/src/streaming/async.h
@@ -0,0 +1,111 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef STREAMING_ASYNC_H_
+#define STREAMING_ASYNC_H_
+
+#include <pthread.h>
+
+#include <libbladeRF.h>
+
+#include "thread.h"
+
+#include "format.h"
+
+typedef enum {
+ STREAM_IDLE, /* Idle and initialized */
+ STREAM_RUNNING, /* Currently running */
+ STREAM_SHUTTING_DOWN, /* Currently tearing down.
+ * See bladerf_stream->error_code to determine
+ * whether or not the shutdown was a clean exit
+ * or due to an error. */
+ STREAM_DONE /* Done and deallocated */
+} bladerf_stream_state;
+
+struct bladerf_stream {
+ /* These items are configured in async_init_stream() and should only be
+ * read (NOT MODIFIED) during the execution of the stream */
+ struct bladerf *dev;
+ bladerf_channel_layout layout;
+ bladerf_format format;
+ unsigned int transfer_timeout;
+ bladerf_stream_cb cb;
+ void *user_data;
+ size_t samples_per_buffer;
+ size_t num_buffers;
+ void **buffers;
+
+ MUTEX lock;
+
+ /* The following items must be accessed atomically */
+ int error_code;
+ bladerf_stream_state state;
+ pthread_cond_t can_submit_buffer;
+ pthread_cond_t stream_started;
+ void *backend_data;
+};
+
+/* Get the number of bytes per stream buffer */
+static inline size_t async_stream_buf_bytes(struct bladerf_stream *s)
+{
+ if (s->format == BLADERF_FORMAT_PACKET_META)
+ return s->samples_per_buffer;
+ return samples_to_bytes(s->format, s->samples_per_buffer);
+}
+
+int async_init_stream(struct bladerf_stream **stream,
+ struct bladerf *dev,
+ bladerf_stream_cb callback,
+ void ***buffers,
+ size_t num_buffers,
+ bladerf_format format,
+ size_t buffer_size,
+ size_t num_transfers,
+ void *user_data);
+
+/* Set the transfer timeout. This acquires stream->lock. */
+int async_set_transfer_timeout(struct bladerf_stream *stream,
+ unsigned int transfer_timeout_ms);
+
+/* Get the transfer timeout. This acquires stream->lock. */
+int async_get_transfer_timeout(struct bladerf_stream *stream,
+ unsigned int *transfer_timeout_ms);
+
+/* Backend code is responsible for acquiring stream->lock in their callbacks */
+int async_run_stream(struct bladerf_stream *stream,
+ bladerf_channel_layout layout);
+
+
+/* This function WILL acquire stream->lock before calling backend code.
+ *
+ * If nonblock=true and no transfers are available, this function shall return
+ * BLADERF_ERR_WOULD_BLOCK.
+ */
+int async_submit_stream_buffer(struct bladerf_stream *stream,
+ void *buffer,
+ size_t *length,
+ unsigned int timeout_ms,
+ bool nonblock);
+
+
+void async_deinit_stream(struct bladerf_stream *stream);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/streaming/format.h b/Radio/HW/BladeRF/src/streaming/format.h
new file mode 100644
index 0000000..95cf5da
--- /dev/null
+++ b/Radio/HW/BladeRF/src/streaming/format.h
@@ -0,0 +1,109 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef STREAMING_FORMAT_H_
+#define STREAMING_FORMAT_H_
+
+#include "rel_assert.h"
+
+/*
+ * Convert SC8Q8 samples to bytes
+ */
+static inline size_t sc8q7_to_bytes(size_t n_samples)
+{
+ const size_t sample_size = 2 * sizeof(int8_t);
+ assert(n_samples <= (SIZE_MAX / sample_size));
+ return n_samples * sample_size;
+}
+
+/*
+ * Convert bytes to SC8Q8 samples
+ */
+static inline size_t bytes_to_sc8q7(size_t n_bytes)
+{
+ const size_t sample_size = 2 * sizeof(int8_t);
+ assert((n_bytes % sample_size) == 0);
+ return n_bytes / sample_size;
+}
+
+/*
+ * Convert SC16Q11 samples to bytes
+ */
+static inline size_t sc16q11_to_bytes(size_t n_samples)
+{
+ const size_t sample_size = 2 * sizeof(int16_t);
+ assert(n_samples <= (SIZE_MAX / sample_size));
+ return n_samples * sample_size;
+}
+
+/*
+ * Convert bytes to SC16Q11 samples
+ */
+static inline size_t bytes_to_sc16q11(size_t n_bytes)
+{
+ const size_t sample_size = 2 * sizeof(int16_t);
+ assert((n_bytes % sample_size) == 0);
+ return n_bytes / sample_size;
+}
+
+/* Covert samples to bytes based upon the provided format */
+static inline size_t samples_to_bytes(bladerf_format format, size_t n)
+{
+ switch (format) {
+ case BLADERF_FORMAT_SC8_Q7:
+ case BLADERF_FORMAT_SC8_Q7_META:
+ return sc8q7_to_bytes(n);
+
+ case BLADERF_FORMAT_SC16_Q11:
+ case BLADERF_FORMAT_SC16_Q11_META:
+ return sc16q11_to_bytes(n);
+
+ case BLADERF_FORMAT_PACKET_META:
+ return n*4;
+
+ default:
+ assert(!"Invalid format");
+ return 0;
+ }
+}
+
+/* Convert bytes to samples based upon the provided format */
+static inline size_t bytes_to_samples(bladerf_format format, size_t n)
+{
+ switch (format) {
+ case BLADERF_FORMAT_SC8_Q7:
+ case BLADERF_FORMAT_SC8_Q7_META:
+ return bytes_to_sc8q7(n);
+
+ case BLADERF_FORMAT_SC16_Q11:
+ case BLADERF_FORMAT_SC16_Q11_META:
+ return bytes_to_sc16q11(n);
+
+ case BLADERF_FORMAT_PACKET_META:
+ return (n+3)/4;
+
+ default:
+ assert(!"Invalid format");
+ return 0;
+ }
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/src/streaming/metadata.h b/Radio/HW/BladeRF/src/streaming/metadata.h
new file mode 100644
index 0000000..e268559
--- /dev/null
+++ b/Radio/HW/BladeRF/src/streaming/metadata.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2014 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef STREAMING_METADATA_H_
+#define STREAMING_METADATA_H_
+
+/*
+ * Metadata layout
+ * ~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * The FPGA handles data in units of "messages." These messages are
+ * 1024 or 2048 bytes for USB 2.0 (Hi-Speed) or USB 3.0 (SuperSpeed),
+ * respectively.
+ *
+ * The first 16 bytes of the message form a header, which includes metadata
+ * for the samples within the message. This header is shown below:
+ *
+ * +-----------------+
+ * 0x00 | Packet length | 2 bytes, Little-endian uint16_t
+ * +-----------------+
+ * 0x02 | Packet flags | 1 byte
+ * +-----------------+
+ * 0x03 | Packet core ID | 1 byte
+ * +-----------------+
+ * 0x04 | Timestamp | 8 bytes, Little-endian uint64_t
+ * +-----------------+
+ * 0x0c | Flags | 4 bytes, Little-endian uint32_t
+ * +-----------------+
+ *
+ * The term "buffer" is used to describe a block of of data received from or
+ * sent to the device. The size of a "buffer" (in bytes) is always a multiple
+ * of the size of a "message." Said another way, a buffer will always evenly
+ * divide into multiple messages. Messages are *not* fragmented across
+ * consecutive buffers.
+ *
+ * +-----------------+ <-. <-.
+ * | header | | |
+ * +-----------------+ | |
+ * | | | |
+ * | samples | | |
+ * | | | |
+ * +-----------------+ | <-+---- message
+ * | header | |
+ * +-----------------+ |
+ * | | |
+ * | samples | |
+ * | | |
+ * +-----------------+ |
+ * | header | |
+ * +-----------------+ |
+ * | | |
+ * | samples | |
+ * | | |
+ * +-----------------+ |
+ * | header | |
+ * +-----------------+ |
+ * | | |
+ * | samples | |
+ * | | |
+ * +-----------------+ <-+---------- buffer
+ *
+ *
+ * When intentionally transmitting discontinuous groups of samples (such
+ * as bursts), it is important that the last two samples within a message
+ * be (0 + 0j). Otherwise, the DAC will not properly hold its output
+ * at (0 + 0j) for the duration of the discontinuity.
+ */
+
+/* Components of the metadata header */
+#define METADATA_RESV_SIZE (sizeof(uint32_t))
+#define METADATA_TIMESTAMP_SIZE (sizeof(uint64_t))
+#define METADATA_FLAGS_SIZE (sizeof(uint32_t))
+#define METADATA_PACKET_LEN_SIZE (sizeof(uint16_t))
+#define METADATA_PACKET_CORE_SIZE (sizeof(uint8_t))
+#define METADATA_PACKET_FLAGS_SIZE (sizeof(uint8_t))
+
+#define METADATA_RESV_OFFSET 0
+#define METADATA_PACKET_LEN_OFFSET 0
+#define METADATA_PACKET_FLAGS_OFFSET 2
+#define METADATA_PACKET_CORE_OFFSET 3
+#define METADATA_TIMESTAMP_OFFSET (METADATA_RESV_SIZE)
+#define METADATA_FLAGS_OFFSET \
+ (METADATA_TIMESTAMP_OFFSET + METADATA_TIMESTAMP_SIZE)
+
+#define METADATA_HEADER_SIZE (METADATA_FLAGS_OFFSET + METADATA_FLAGS_SIZE)
+
+static inline uint64_t metadata_get_timestamp(const uint8_t *header)
+{
+ uint64_t ret;
+ assert(sizeof(ret) == METADATA_TIMESTAMP_SIZE);
+ memcpy(&ret, &header[METADATA_TIMESTAMP_OFFSET], METADATA_TIMESTAMP_SIZE);
+
+ ret = LE64_TO_HOST(ret);
+
+ return ret;
+}
+
+static inline uint32_t metadata_get_flags(const uint8_t *header)
+{
+ uint32_t ret;
+ assert(sizeof(ret) == METADATA_FLAGS_SIZE);
+ memcpy(&ret, &header[METADATA_FLAGS_OFFSET], METADATA_FLAGS_SIZE);
+ return LE32_TO_HOST(ret);
+}
+
+static inline uint16_t metadata_get_packet_len(const uint8_t *header)
+{
+ uint16_t ret;
+ assert(sizeof(ret) == METADATA_PACKET_LEN_SIZE);
+ memcpy(&ret, &header[METADATA_PACKET_LEN_OFFSET], METADATA_PACKET_LEN_SIZE);
+ return LE16_TO_HOST(ret);
+}
+
+static inline uint8_t metadata_get_packet_core(const uint8_t *header)
+{
+ uint8_t ret;
+ assert(sizeof(ret) == METADATA_PACKET_CORE_SIZE);
+ memcpy(&ret, &header[METADATA_PACKET_CORE_OFFSET], METADATA_PACKET_CORE_SIZE);
+ return ret;
+}
+
+static inline uint8_t metadata_get_packet_flags(const uint8_t *header)
+{
+ uint8_t ret;
+ assert(sizeof(ret) == METADATA_PACKET_FLAGS_SIZE);
+ memcpy(&ret, &header[METADATA_PACKET_FLAGS_OFFSET], METADATA_PACKET_FLAGS_SIZE);
+ return ret;
+}
+
+static inline void metadata_set_packet(uint8_t *header,
+ uint64_t timestamp,
+ uint32_t flags,
+ uint16_t length,
+ uint8_t core,
+ uint8_t pkt_flags)
+{
+ timestamp = HOST_TO_LE64(timestamp);
+
+ flags = HOST_TO_LE32(flags);
+
+ length = HOST_TO_LE16(length);
+
+ assert(sizeof(timestamp) == METADATA_TIMESTAMP_SIZE);
+ assert(sizeof(flags) == METADATA_FLAGS_SIZE);
+
+ memset(&header[METADATA_RESV_OFFSET], 0, METADATA_RESV_SIZE);
+
+ memcpy(&header[METADATA_PACKET_LEN_OFFSET], &length, METADATA_PACKET_LEN_SIZE);
+ memcpy(&header[METADATA_PACKET_CORE_OFFSET], &core, METADATA_PACKET_CORE_SIZE);
+ memcpy(&header[METADATA_PACKET_FLAGS_OFFSET], &pkt_flags, METADATA_PACKET_FLAGS_SIZE);
+
+ memcpy(&header[METADATA_TIMESTAMP_OFFSET], &timestamp,
+ METADATA_TIMESTAMP_SIZE);
+
+ memcpy(&header[METADATA_FLAGS_OFFSET], &flags, METADATA_FLAGS_SIZE);
+}
+
+static inline void metadata_set(uint8_t *header,
+ uint64_t timestamp,
+ uint32_t flags)
+{
+ metadata_set_packet(header, timestamp, flags, 0, 0, 0);
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/src/streaming/sync.c b/Radio/HW/BladeRF/src/streaming/sync.c
new file mode 100644
index 0000000..feef101
--- /dev/null
+++ b/Radio/HW/BladeRF/src/streaming/sync.c
@@ -0,0 +1,1339 @@
+/*
+ * Copyright (C) 2014-2015 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+
+/* Only switch on the verbose debug prints in this file when we *really* want
+ * them. Otherwise, compile them out to avoid excessive log level checks
+ * in our data path */
+#include "log.h"
+#ifndef ENABLE_LIBBLADERF_SYNC_LOG_VERBOSE
+#undef log_verbose
+#define log_verbose(...)
+#endif
+#include "minmax.h"
+#include "rel_assert.h"
+
+#include "async.h"
+#include "sync.h"
+#include "sync_worker.h"
+#include "metadata.h"
+
+#include "board/board.h"
+#include "helpers/timeout.h"
+#include "helpers/have_cap.h"
+
+#ifdef ENABLE_LIBBLADERF_SYNC_LOG_VERBOSE
+static inline void dump_buf_states(struct bladerf_sync *s)
+{
+ static char *out = NULL;
+ struct buffer_mgmt *b = &s->buf_mgmt;
+ char *statestr = "UNKNOWN";
+
+ if (out == NULL) {
+ out = malloc((b->num_buffers + 1) * sizeof(char));
+ }
+
+ if (out == NULL) {
+ log_verbose("%s: malloc failed\n");
+ return;
+ }
+
+ out[b->num_buffers] = '\0';
+
+ for (size_t i = 0; i < b->num_buffers; ++i) {
+ switch (b->status[i]) {
+ case SYNC_BUFFER_EMPTY:
+ out[i] = '_';
+ break;
+ case SYNC_BUFFER_IN_FLIGHT:
+ out[i] = '-';
+ break;
+ case SYNC_BUFFER_FULL:
+ out[i] = '*';
+ break;
+ case SYNC_BUFFER_PARTIAL:
+ out[i] = 'o';
+ break;
+ }
+ }
+
+ switch (s->state) {
+ case SYNC_STATE_BUFFER_READY:
+ statestr = "BUFFER_READY";
+ break;
+ case SYNC_STATE_CHECK_WORKER:
+ statestr = "CHECK_WORKER";
+ break;
+ case SYNC_STATE_RESET_BUF_MGMT:
+ statestr = "RESET_BUF_MGMT";
+ break;
+ case SYNC_STATE_START_WORKER:
+ statestr = "START_WORKER";
+ break;
+ case SYNC_STATE_USING_BUFFER:
+ statestr = "USING_BUFFER";
+ break;
+ case SYNC_STATE_USING_BUFFER_META:
+ statestr = "USING_BUFFER_META";
+ break;
+ case SYNC_STATE_USING_PACKET_META:
+ statestr = "USING_PACKET_META";
+ break;
+ case SYNC_STATE_WAIT_FOR_BUFFER:
+ statestr = "WAIT_FOR_BUFFER";
+ break;
+ }
+
+ log_verbose("%s: %s (%s)\n", __FUNCTION__, out, statestr);
+}
+#else
+#define dump_buf_states(...)
+#endif // ENABLE_LIBBLADERF_SYNC_LOG_VERBOSE
+
+static inline size_t samples2bytes(struct bladerf_sync *s, size_t n) {
+ return s->stream_config.bytes_per_sample * n;
+}
+
+static inline unsigned int msg_per_buf(size_t msg_size, size_t buf_size,
+ size_t bytes_per_sample)
+{
+ size_t n = buf_size / (msg_size / bytes_per_sample);
+ assert(n <= UINT_MAX);
+ return (unsigned int) n;
+}
+
+static inline unsigned int samples_per_msg(size_t msg_size,
+ size_t bytes_per_sample)
+{
+ size_t n = (msg_size - METADATA_HEADER_SIZE) / bytes_per_sample;
+ assert(n <= UINT_MAX);
+ return (unsigned int) n;
+}
+
+int sync_init(struct bladerf_sync *sync,
+ struct bladerf *dev,
+ bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int num_buffers,
+ size_t buffer_size,
+ size_t msg_size,
+ unsigned int num_transfers,
+ unsigned int stream_timeout)
+
+{
+ int status = 0;
+ size_t i, bytes_per_sample;
+
+ if (num_transfers >= num_buffers) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (format == BLADERF_FORMAT_PACKET_META) {
+ if (!have_cap_dev(dev, BLADERF_CAP_FW_SHORT_PACKET)) {
+ log_error("Firmware does not support short packets. "
+ "Upgrade to at least firmware version 2.4.0.\n");
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+
+ if (!have_cap_dev(dev, BLADERF_CAP_FPGA_PACKET_META)) {
+ log_error("FPGA does not support packet meta format. "
+ "Upgrade to at least FPGA version 0.12.0.\n");
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+ }
+
+ if (format == BLADERF_FORMAT_SC8_Q7 || format == BLADERF_FORMAT_SC8_Q7_META) {
+ if (!have_cap_dev(dev, BLADERF_CAP_FPGA_8BIT_SAMPLES)) {
+ log_error("FPGA does not support 8bit mode. "
+ "Upgrade to at least FPGA version 0.15.0.\n");
+ return BLADERF_ERR_UNSUPPORTED;
+ }
+ }
+
+ switch (format) {
+ case BLADERF_FORMAT_SC8_Q7:
+ case BLADERF_FORMAT_SC8_Q7_META:
+ bytes_per_sample = 2;
+ break;
+
+ case BLADERF_FORMAT_SC16_Q11:
+ case BLADERF_FORMAT_SC16_Q11_META:
+ case BLADERF_FORMAT_PACKET_META:
+ bytes_per_sample = 4;
+ break;
+
+ default:
+ log_debug("Invalid format value: %d\n", format);
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* bladeRF GPIF DMA requirement */
+ if ((bytes_per_sample * buffer_size) % 4096 != 0) {
+ assert(!"Invalid buffer size");
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Deinitialize sync handle if it's initialized */
+ sync_deinit(sync);
+
+ MUTEX_INIT(&sync->lock);
+
+ switch (layout & BLADERF_DIRECTION_MASK) {
+ case BLADERF_TX:
+ sync->buf_mgmt.submitter = SYNC_TX_SUBMITTER_FN;
+ break;
+ case BLADERF_RX:
+ sync->buf_mgmt.submitter = SYNC_TX_SUBMITTER_INVALID;
+ break;
+ }
+
+ sync->dev = dev;
+ sync->state = SYNC_STATE_CHECK_WORKER;
+
+ sync->buf_mgmt.num_buffers = num_buffers;
+ sync->buf_mgmt.resubmit_count = 0;
+
+ sync->stream_config.layout = layout;
+ sync->stream_config.format = format;
+ sync->stream_config.samples_per_buffer = (unsigned int)buffer_size;
+ sync->stream_config.num_xfers = num_transfers;
+ sync->stream_config.timeout_ms = stream_timeout;
+ sync->stream_config.bytes_per_sample = bytes_per_sample;
+
+ sync->meta.state = SYNC_META_STATE_HEADER;
+ sync->meta.msg_size = msg_size;
+ sync->meta.msg_per_buf = msg_per_buf(msg_size, buffer_size, bytes_per_sample);
+ sync->meta.samples_per_msg = samples_per_msg(msg_size, bytes_per_sample);
+ sync->meta.samples_per_ts = (layout == BLADERF_RX_X2 || layout == BLADERF_TX_X2) ? 2:1;
+
+ log_verbose("%s: Buffer size (in bytes): %u\n",
+ __FUNCTION__, buffer_size * bytes_per_sample);
+
+ log_verbose("%s: Buffer size (in samples): %u\n",
+ __FUNCTION__, buffer_size);
+
+ log_verbose("%s: Msg per buffer: %u\n",
+ __FUNCTION__, sync->meta.msg_per_buf);
+
+ log_verbose("%s: Samples per msg: %u\n",
+ __FUNCTION__, sync->meta.samples_per_msg);
+
+ MUTEX_INIT(&sync->buf_mgmt.lock);
+ pthread_cond_init(&sync->buf_mgmt.buf_ready, NULL);
+
+ sync->buf_mgmt.status = (sync_buffer_status*) malloc(num_buffers * sizeof(sync_buffer_status));
+ if (sync->buf_mgmt.status == NULL) {
+ status = BLADERF_ERR_MEM;
+ goto error;
+ }
+
+ sync->buf_mgmt.actual_lengths = (size_t *) malloc(num_buffers * sizeof(size_t));
+ if (sync->buf_mgmt.actual_lengths == NULL) {
+ status = BLADERF_ERR_MEM;
+ goto error;
+ }
+
+ switch (layout & BLADERF_DIRECTION_MASK) {
+ case BLADERF_RX:
+ /* When starting up an RX stream, the first 'num_transfers'
+ * transfers will be submitted to the USB layer to grab data */
+ sync->buf_mgmt.prod_i = num_transfers;
+ sync->buf_mgmt.cons_i = 0;
+ sync->buf_mgmt.partial_off = 0;
+
+ for (i = 0; i < num_buffers; i++) {
+ if (i < num_transfers) {
+ sync->buf_mgmt.status[i] = SYNC_BUFFER_IN_FLIGHT;
+ } else {
+ sync->buf_mgmt.status[i] = SYNC_BUFFER_EMPTY;
+ }
+ }
+
+ sync->meta.msg_timestamp = 0;
+ sync->meta.msg_flags = 0;
+
+ break;
+
+ case BLADERF_TX:
+ sync->buf_mgmt.prod_i = 0;
+ sync->buf_mgmt.cons_i = BUFFER_MGMT_INVALID_INDEX;
+ sync->buf_mgmt.partial_off = 0;
+
+ for (i = 0; i < num_buffers; i++) {
+ sync->buf_mgmt.status[i] = SYNC_BUFFER_EMPTY;
+ }
+
+ sync->meta.msg_timestamp = 0;
+ sync->meta.in_burst = false;
+ sync->meta.now = false;
+ break;
+ }
+
+ status = sync_worker_init(sync);
+ if (status < 0) {
+ goto error;
+ }
+
+ sync->initialized = true;
+
+ return 0;
+
+error:
+ sync_deinit(sync);
+ return status;
+}
+
+void sync_deinit(struct bladerf_sync *sync)
+{
+ if (sync->initialized) {
+ if ((sync->stream_config.layout & BLADERF_DIRECTION_MASK) == BLADERF_TX) {
+ async_submit_stream_buffer(sync->worker->stream,
+ BLADERF_STREAM_SHUTDOWN, NULL, 0, false);
+ }
+
+ sync_worker_deinit(sync->worker, &sync->buf_mgmt.lock,
+ &sync->buf_mgmt.buf_ready);
+
+ if (sync->buf_mgmt.actual_lengths) {
+ free(sync->buf_mgmt.actual_lengths);
+ }
+ /* De-allocate our buffer management resources */
+ if (sync->buf_mgmt.status) {
+ MUTEX_DESTROY(&sync->buf_mgmt.lock);
+ free(sync->buf_mgmt.status);
+ }
+
+ MUTEX_DESTROY(&sync->lock);
+
+ sync->initialized = false;
+ }
+}
+
+static int wait_for_buffer(struct buffer_mgmt *b,
+ unsigned int timeout_ms,
+ const char *dbg_name,
+ unsigned int dbg_idx)
+{
+ int status;
+ struct timespec timeout;
+
+ if (timeout_ms == 0) {
+ log_verbose("%s: Infinite wait for buffer[%d] (status: %d).\n",
+ dbg_name, dbg_idx, b->status[dbg_idx]);
+ status = pthread_cond_wait(&b->buf_ready, &b->lock);
+ } else {
+ log_verbose("%s: Timed wait for buffer[%d] (status: %d).\n", dbg_name,
+ dbg_idx, b->status[dbg_idx]);
+ status = populate_abs_timeout(&timeout, timeout_ms);
+ if (status == 0) {
+ status = pthread_cond_timedwait(&b->buf_ready, &b->lock, &timeout);
+ }
+ }
+
+ if (status == ETIMEDOUT) {
+ log_error("%s: Timed out waiting for buf_ready after %d ms\n",
+ __FUNCTION__, timeout_ms);
+ status = BLADERF_ERR_TIMEOUT;
+ } else if (status != 0) {
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ return status;
+}
+
+#ifndef SYNC_WORKER_START_TIMEOUT_MS
+# define SYNC_WORKER_START_TIMEOUT_MS 250
+#endif
+
+/* Returns # of timestamps (or time steps) left in a message */
+static inline unsigned int ts_remaining(struct bladerf_sync *s)
+{
+ size_t ret = s->meta.samples_per_msg / s->meta.samples_per_ts - s->meta.curr_msg_off;
+ assert(ret <= UINT_MAX);
+
+ return (unsigned int) ret;
+}
+
+/* Returns # of samples left in a message (SC16Q11 mode only) */
+static inline unsigned int left_in_msg(struct bladerf_sync *s)
+{
+ size_t ret = s->meta.samples_per_msg - s->meta.curr_msg_off;
+ assert(ret <= UINT_MAX);
+
+ return (unsigned int) ret;
+}
+
+static inline void advance_rx_buffer(struct buffer_mgmt *b)
+{
+ log_verbose("%s: Marking buf[%u] empty.\n", __FUNCTION__, b->cons_i);
+
+ b->status[b->cons_i] = SYNC_BUFFER_EMPTY;
+ b->cons_i = (b->cons_i + 1) % b->num_buffers;
+}
+
+static inline unsigned int timestamp_to_msg(struct bladerf_sync *s, uint64_t t)
+{
+ uint64_t m = t / s->meta.samples_per_msg;
+ assert(m <= UINT_MAX);
+ return (unsigned int) m;
+}
+
+int sync_rx(struct bladerf_sync *s, void *samples, unsigned num_samples,
+ struct bladerf_metadata *user_meta, unsigned int timeout_ms)
+{
+ struct buffer_mgmt *b;
+
+ int status = 0;
+ bool exit_early = false;
+ bool copied_data = false;
+ unsigned int samples_returned = 0;
+ uint8_t *samples_dest = (uint8_t*)samples;
+ uint8_t *buf_src = NULL;
+ unsigned int samples_to_copy = 0;
+ unsigned int samples_per_buffer = 0;
+ uint64_t target_timestamp = UINT64_MAX;
+ unsigned int pkt_len_dwords = 0;
+
+ if (s == NULL || samples == NULL) {
+ log_debug("NULL pointer passed to %s\n", __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ } else if (!s->initialized) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (num_samples % s->meta.samples_per_ts != 0) {
+ log_debug("%s: %u samples %% %u channels != 0\n",
+ __FUNCTION__, num_samples, s->meta.samples_per_ts);
+ return BLADERF_ERR_INVAL;
+ }
+
+ MUTEX_LOCK(&s->lock);
+
+ if (s->stream_config.format == BLADERF_FORMAT_SC16_Q11_META ||
+ s->stream_config.format == BLADERF_FORMAT_SC8_Q7_META ||
+ s->stream_config.format == BLADERF_FORMAT_PACKET_META) {
+ if (user_meta == NULL) {
+ log_debug("NULL metadata pointer passed to %s\n", __FUNCTION__);
+ status = BLADERF_ERR_INVAL;
+ goto out;
+ } else {
+ user_meta->status = 0;
+ target_timestamp = user_meta->timestamp;
+ }
+ }
+
+ b = &s->buf_mgmt;
+ samples_per_buffer = s->stream_config.samples_per_buffer;
+
+ log_verbose("%s: Requests %u samples.\n", __FUNCTION__, num_samples);
+
+ while (!exit_early && samples_returned < num_samples && status == 0) {
+ dump_buf_states(s);
+
+ switch (s->state) {
+ case SYNC_STATE_CHECK_WORKER: {
+ int stream_error;
+ sync_worker_state worker_state =
+ sync_worker_get_state(s->worker, &stream_error);
+
+ /* Propagate stream error back to the caller.
+ * They can call this function again to restart the stream and
+ * try again.
+ */
+ if (stream_error != 0) {
+ status = stream_error;
+ } else {
+ if (worker_state == SYNC_WORKER_STATE_IDLE) {
+ log_debug("%s: Worker is idle. Going to reset buf "
+ "mgmt.\n", __FUNCTION__);
+ s->state = SYNC_STATE_RESET_BUF_MGMT;
+ } else if (worker_state == SYNC_WORKER_STATE_RUNNING) {
+ s->state = SYNC_STATE_WAIT_FOR_BUFFER;
+ } else {
+ status = BLADERF_ERR_UNEXPECTED;
+ log_debug("%s: Unexpected worker state=%d\n",
+ __FUNCTION__, worker_state);
+ }
+ }
+
+ break;
+ }
+
+ case SYNC_STATE_RESET_BUF_MGMT:
+ MUTEX_LOCK(&b->lock);
+ /* When the RX stream starts up, it will submit the first T
+ * transfers, so the consumer index must be reset to 0 */
+ b->cons_i = 0;
+ MUTEX_UNLOCK(&b->lock);
+ log_debug("%s: Reset buf_mgmt consumer index\n", __FUNCTION__);
+ s->state = SYNC_STATE_START_WORKER;
+ break;
+
+
+ case SYNC_STATE_START_WORKER:
+ sync_worker_submit_request(s->worker, SYNC_WORKER_START);
+
+ status = sync_worker_wait_for_state(
+ s->worker,
+ SYNC_WORKER_STATE_RUNNING,
+ SYNC_WORKER_START_TIMEOUT_MS);
+
+ if (status == 0) {
+ s->state = SYNC_STATE_WAIT_FOR_BUFFER;
+ log_debug("%s: Worker is now running.\n", __FUNCTION__);
+ } else {
+ log_debug("%s: Failed to start worker, (%d)\n",
+ __FUNCTION__, status);
+ }
+ break;
+
+ case SYNC_STATE_WAIT_FOR_BUFFER:
+ MUTEX_LOCK(&b->lock);
+
+ /* Check the buffer state, as the worker may have produced one
+ * since we last queried the status */
+ if (b->status[b->cons_i] == SYNC_BUFFER_FULL) {
+ s->state = SYNC_STATE_BUFFER_READY;
+ log_verbose("%s: buffer %u is ready to consume\n",
+ __FUNCTION__, b->cons_i);
+ } else {
+ status = wait_for_buffer(b, timeout_ms,
+ __FUNCTION__, b->cons_i);
+
+ if (status == 0) {
+ if (b->status[b->cons_i] != SYNC_BUFFER_FULL) {
+ s->state = SYNC_STATE_CHECK_WORKER;
+ } else {
+ s->state = SYNC_STATE_BUFFER_READY;
+ log_verbose("%s: buffer %u is ready to consume\n",
+ __FUNCTION__, b->cons_i);
+ }
+ }
+ }
+
+ MUTEX_UNLOCK(&b->lock);
+ break;
+
+ case SYNC_STATE_BUFFER_READY:
+ MUTEX_LOCK(&b->lock);
+ b->status[b->cons_i] = SYNC_BUFFER_PARTIAL;
+ b->partial_off = 0;
+
+ switch (s->stream_config.format) {
+ case BLADERF_FORMAT_SC16_Q11:
+ case BLADERF_FORMAT_SC8_Q7:
+ s->state = SYNC_STATE_USING_BUFFER;
+ break;
+
+ case BLADERF_FORMAT_SC16_Q11_META:
+ case BLADERF_FORMAT_SC8_Q7_META:
+ s->state = SYNC_STATE_USING_BUFFER_META;
+ s->meta.curr_msg_off = 0;
+ s->meta.msg_num = 0;
+ break;
+
+ case BLADERF_FORMAT_PACKET_META:
+ s->state = SYNC_STATE_USING_PACKET_META;
+ break;
+
+ default:
+ assert(!"Invalid stream format");
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ MUTEX_UNLOCK(&b->lock);
+ break;
+
+ case SYNC_STATE_USING_BUFFER: /* SC16Q11 buffers w/o metadata */
+ MUTEX_LOCK(&b->lock);
+
+ buf_src = (uint8_t*)b->buffers[b->cons_i];
+
+ samples_to_copy = uint_min(num_samples - samples_returned,
+ samples_per_buffer - b->partial_off);
+
+ memcpy(samples_dest + samples2bytes(s, samples_returned),
+ buf_src + samples2bytes(s, b->partial_off),
+ samples2bytes(s, samples_to_copy));
+
+ b->partial_off += samples_to_copy;
+ samples_returned += samples_to_copy;
+
+ log_verbose("%s: Provided %u samples to caller\n",
+ __FUNCTION__, samples_to_copy);
+
+ /* We've finished consuming this buffer and can start looking
+ * for available samples in the next buffer */
+ if (b->partial_off >= samples_per_buffer) {
+
+ /* Check for symptom of out-of-bounds accesses */
+ assert(b->partial_off == samples_per_buffer);
+
+ advance_rx_buffer(b);
+ s->state = SYNC_STATE_WAIT_FOR_BUFFER;
+ }
+
+ MUTEX_UNLOCK(&b->lock);
+ break;
+
+
+ case SYNC_STATE_USING_BUFFER_META: /* SC16Q11 buffers w/ metadata */
+ MUTEX_LOCK(&b->lock);
+
+ switch (s->meta.state) {
+ case SYNC_META_STATE_HEADER:
+
+ assert(s->meta.msg_num < s->meta.msg_per_buf);
+
+ buf_src = (uint8_t*)b->buffers[b->cons_i];
+
+ s->meta.curr_msg =
+ buf_src + s->meta.msg_size * s->meta.msg_num;
+
+ s->meta.msg_timestamp =
+ metadata_get_timestamp(s->meta.curr_msg);
+
+ s->meta.msg_flags =
+ metadata_get_flags(s->meta.curr_msg);
+
+ user_meta->status |= s->meta.msg_flags &
+ (BLADERF_META_FLAG_RX_HW_UNDERFLOW |
+ BLADERF_META_FLAG_RX_HW_MINIEXP1 |
+ BLADERF_META_FLAG_RX_HW_MINIEXP2);
+
+ s->meta.curr_msg_off = 0;
+
+ /* We've encountered a discontinuity and need to return
+ * what we have so far, setting the status flags */
+ if (copied_data &&
+ s->meta.msg_timestamp != s->meta.curr_timestamp) {
+
+ user_meta->status |= BLADERF_META_STATUS_OVERRUN;
+ exit_early = true;
+ log_debug("Sample discontinuity detected @ "
+ "buffer %u, message %u: Expected t=%llu, "
+ "got t=%llu\n",
+ b->cons_i, s->meta.msg_num,
+ (unsigned long long)s->meta.curr_timestamp,
+ (unsigned long long)s->meta.msg_timestamp);
+
+ } else {
+ log_verbose("Got header for message %u: "
+ "t_new=%u, t_old=%u\n",
+ s->meta.msg_num,
+ s->meta.msg_timestamp,
+ s->meta.curr_timestamp);
+ }
+
+ s->meta.curr_timestamp = s->meta.msg_timestamp;
+ s->meta.state = SYNC_META_STATE_SAMPLES;
+ break;
+
+ case SYNC_META_STATE_SAMPLES:
+ if (!copied_data &&
+ (user_meta->flags & BLADERF_META_FLAG_RX_NOW) == 0 &&
+ target_timestamp < s->meta.curr_timestamp) {
+
+ log_debug("Current timestamp is %llu, "
+ "target=%llu (user=%llu)\n",
+ (unsigned long long)s->meta.curr_timestamp,
+ (unsigned long long)target_timestamp,
+ (unsigned long long)user_meta->timestamp);
+
+ status = BLADERF_ERR_TIME_PAST;
+ } else if ((user_meta->flags & BLADERF_META_FLAG_RX_NOW) ||
+ target_timestamp == s->meta.curr_timestamp) {
+
+ /* Copy the request amount up to the end of a
+ * this message in the current buffer */
+ samples_to_copy =
+ uint_min(num_samples - samples_returned,
+ left_in_msg(s));
+
+ memcpy(samples_dest + samples2bytes(s, samples_returned),
+ s->meta.curr_msg +
+ METADATA_HEADER_SIZE +
+ samples2bytes(s, s->meta.curr_msg_off),
+ samples2bytes(s, samples_to_copy));
+
+ samples_returned += samples_to_copy;
+ s->meta.curr_msg_off += samples_to_copy;
+
+ if (!copied_data &&
+ (user_meta->flags & BLADERF_META_FLAG_RX_NOW)) {
+
+ /* Provide the user with the timestamp at the
+ * first returned sample when the
+ * NOW flag has been provided */
+ user_meta->timestamp = s->meta.curr_timestamp;
+ log_verbose("Updated user meta timestamp with: "
+ "%llu\n", (unsigned long long)
+ user_meta->timestamp);
+ }
+
+ copied_data = true;
+
+ s->meta.curr_timestamp += samples_to_copy / s->meta.samples_per_ts;
+
+ /* We've begun copying samples, so our target will
+ * just keep tracking the current timestamp. */
+ target_timestamp = s->meta.curr_timestamp;
+
+ log_verbose("After copying samples, t=%llu\n",
+ (unsigned long long)s->meta.curr_timestamp);
+
+ if (left_in_msg(s) == 0) {
+ assert(s->meta.curr_msg_off == s->meta.samples_per_msg);
+
+ s->meta.state = SYNC_META_STATE_HEADER;
+ s->meta.msg_num++;
+
+ if (s->meta.msg_num >= s->meta.msg_per_buf) {
+ assert(s->meta.msg_num == s->meta.msg_per_buf);
+ advance_rx_buffer(b);
+ s->meta.msg_num = 0;
+ s->state = SYNC_STATE_WAIT_FOR_BUFFER;
+ }
+ }
+
+ } else {
+ const uint64_t time_delta = target_timestamp - s->meta.curr_timestamp;
+ uint64_t samples_left = time_delta * s->meta.samples_per_ts;
+ uint64_t left_in_buffer =
+ (uint64_t) s->meta.samples_per_msg *
+ (s->meta.msg_per_buf - s->meta.msg_num);
+
+ /* Account for current position in buffer */
+ left_in_buffer -= s->meta.curr_msg_off;
+
+ if (samples_left >= left_in_buffer) {
+ /* Discard the remainder of this buffer */
+ advance_rx_buffer(b);
+ s->state = SYNC_STATE_WAIT_FOR_BUFFER;
+ s->meta.state = SYNC_META_STATE_HEADER;
+
+ log_verbose("%s: Discarding rest of buffer.\n",
+ __FUNCTION__);
+
+ } else if (time_delta <= ts_remaining(s)) {
+ /* Fast forward within the current message */
+ assert(time_delta <= SIZE_MAX);
+
+ s->meta.curr_msg_off += (size_t)samples_left;
+ s->meta.curr_timestamp += time_delta;
+
+ log_verbose("%s: Seeking within message (t=%llu)\n",
+ __FUNCTION__,
+ s->meta.curr_timestamp);
+ } else {
+ s->meta.state = SYNC_META_STATE_HEADER;
+ s->meta.msg_num += timestamp_to_msg(s, samples_left);
+
+ log_verbose("%s: Seeking to message %u.\n",
+ __FUNCTION__, s->meta.msg_num);
+ }
+ }
+ break;
+
+ default:
+ assert(!"Invalid state");
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ MUTEX_UNLOCK(&b->lock);
+ break;
+
+ case SYNC_STATE_USING_PACKET_META: /* Packet buffers w/ metadata */
+ MUTEX_LOCK(&b->lock);
+
+ buf_src = (uint8_t*)b->buffers[b->cons_i];
+
+ pkt_len_dwords = metadata_get_packet_len(buf_src);
+
+ if (pkt_len_dwords > 0) {
+ samples_returned += num_samples;
+ user_meta->actual_count = pkt_len_dwords;
+ memcpy(samples_dest, buf_src + METADATA_HEADER_SIZE, samples2bytes(s, pkt_len_dwords));
+ }
+
+ advance_rx_buffer(b);
+ s->state = SYNC_STATE_WAIT_FOR_BUFFER;
+ MUTEX_UNLOCK(&b->lock);
+ break;
+
+
+ }
+ }
+
+ if (user_meta && s->stream_config.format != BLADERF_FORMAT_PACKET_META) {
+ user_meta->actual_count = samples_returned;
+ }
+
+out:
+ MUTEX_UNLOCK(&s->lock);
+
+ return status;
+}
+
+/* Assumes buffer lock is held */
+static int advance_tx_buffer(struct bladerf_sync *s, struct buffer_mgmt *b)
+{
+ int status = 0;
+ const unsigned int idx = b->prod_i;
+
+ if (b->submitter == SYNC_TX_SUBMITTER_FN) {
+ /* Mark buffer in flight because we're going to send it out.
+ * This ensures that if the callback fires before this function
+ * completes, its state will be correct. */
+ b->status[idx] = SYNC_BUFFER_IN_FLIGHT;
+
+ /* This call may block and it results in a per-stream lock being held,
+ * so the buffer lock must be dropped.
+ *
+ * A callback may occur in the meantime, but this will not touch the
+ * status for this this buffer, or the producer index.
+ */
+ MUTEX_UNLOCK(&b->lock);
+ size_t len;
+ if (s->stream_config.format == BLADERF_FORMAT_PACKET_META) {
+ len = b->actual_lengths[idx];
+ } else {
+ len = async_stream_buf_bytes(s->worker->stream);
+ }
+ status = async_submit_stream_buffer(s->worker->stream,
+ b->buffers[idx],
+ &len,
+ s->stream_config.timeout_ms,
+ true);
+ MUTEX_LOCK(&b->lock);
+
+ if (status == 0) {
+ log_verbose("%s: buf[%u] submitted.\n",
+ __FUNCTION__, idx);
+
+ } else if (status == BLADERF_ERR_WOULD_BLOCK) {
+ log_verbose("%s: Deferring buf[%u] submission to worker callback.\n",
+ __FUNCTION__, idx);
+
+ /* Mark this buffer as being full of data, but not in flight */
+ b->status[idx] = SYNC_BUFFER_FULL;
+
+ /* Assign callback the duty of submitting deferred buffers,
+ * and use buffer_mgmt.cons_i to denote which it should submit
+ * (i.e., consume). */
+ b->submitter = SYNC_TX_SUBMITTER_CALLBACK;
+ b->cons_i = idx;
+
+ /* This is expected and we are handling it. Don't propagate this
+ * status back up */
+ status = 0;
+ } else {
+ /* Unmark this as being in flight */
+ b->status[idx] = SYNC_BUFFER_FULL;
+
+ log_debug("%s: Failed to submit buf[%u].\n", __FUNCTION__, idx);
+ return status;
+ }
+ } else {
+ /* We are not submitting this buffer; this is deffered to the worker
+ * call back. Just update its state to being full of samples. */
+ b->status[idx] = SYNC_BUFFER_FULL;
+ }
+
+ /* Advance "producer" insertion index. */
+ b->prod_i = (idx + 1) % b->num_buffers;
+
+ /* Determine our next state based upon the state of the next buffer we
+ * want to use. */
+ if (b->status[b->prod_i] == SYNC_BUFFER_EMPTY) {
+ /* Buffer is empty and ready for use */
+ s->state = SYNC_STATE_BUFFER_READY;
+ } else {
+ /* We'll have to wait on this buffer to become ready. First, we'll
+ * verify that the worker is running. */
+ s->state = SYNC_STATE_CHECK_WORKER;
+ }
+
+ return status;
+}
+
+static inline bool timestamp_in_past(struct bladerf_metadata *user_meta,
+ struct bladerf_sync *s)
+{
+ const bool in_past = user_meta->timestamp < s->meta.curr_timestamp;
+
+ if (in_past) {
+ log_debug("Provided timestamp=%"PRIu64" is in past: current=%"PRIu64"\n",
+ user_meta->timestamp, s->meta.curr_timestamp);
+ }
+
+ return in_past;
+}
+
+struct tx_options {
+ bool flush;
+ bool zero_pad;
+};
+
+static inline int handle_tx_parameters(struct bladerf_metadata *user_meta,
+ struct bladerf_sync *s,
+ struct tx_options *options)
+{
+ if (s->stream_config.format == BLADERF_FORMAT_SC16_Q11_META) {
+ if (user_meta == NULL) {
+ log_debug("NULL metadata pointer passed to %s\n", __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (user_meta->flags & BLADERF_META_FLAG_TX_BURST_START) {
+ bool now = user_meta->flags & BLADERF_META_FLAG_TX_NOW;
+
+ if (s->meta.in_burst) {
+ log_debug("%s: BURST_START provided while already in a burst.\n",
+ __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ } else if (!now && timestamp_in_past(user_meta, s)) {
+ return BLADERF_ERR_TIME_PAST;
+ }
+
+ s->meta.in_burst = true;
+ if (now) {
+ s->meta.now = true;
+ log_verbose("%s: Starting burst \"now\"\n", __FUNCTION__);
+ } else {
+ s->meta.curr_timestamp = user_meta->timestamp;
+ log_verbose("%s: Starting burst @ %llu\n", __FUNCTION__,
+ (unsigned long long)s->meta.curr_timestamp);
+ }
+
+ if (user_meta->flags & BLADERF_META_FLAG_TX_UPDATE_TIMESTAMP) {
+ log_debug("UPDATE_TIMESTAMP ignored; BURST_START flag was used.\n");
+ }
+
+ } else if (user_meta->flags & BLADERF_META_FLAG_TX_NOW) {
+ log_debug("%s: TX_NOW was specified without BURST_START.\n",
+ __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ } else if (user_meta->flags & BLADERF_META_FLAG_TX_UPDATE_TIMESTAMP) {
+ if (timestamp_in_past(user_meta, s)) {
+ return BLADERF_ERR_TIME_PAST;
+ } else {
+ options->zero_pad = true;
+ }
+ }
+
+ if (user_meta->flags & BLADERF_META_FLAG_TX_BURST_END) {
+ if (s->meta.in_burst) {
+ options->flush = true;
+ } else {
+ log_debug("%s: BURST_END provided while not in a burst.\n",
+ __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ }
+ }
+
+ user_meta->status = 0;
+ }
+
+ return 0;
+}
+
+int sync_tx(struct bladerf_sync *s,
+ void const *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *user_meta,
+ unsigned int timeout_ms)
+{
+ struct buffer_mgmt *b = NULL;
+
+ int status = 0;
+ unsigned int samples_written = 0;
+ unsigned int samples_to_copy = 0;
+ unsigned int samples_per_buffer = 0;
+ uint8_t const *samples_src = (uint8_t const *)samples;
+ uint8_t *buf_dest = NULL;
+ struct tx_options op = {
+ FIELD_INIT(.flush, false), FIELD_INIT(.zero_pad, false),
+ };
+
+ log_verbose("%s: called for %u samples.\n", __FUNCTION__, num_samples);
+
+ if (s == NULL || samples == NULL || !s->initialized) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ MUTEX_LOCK(&s->lock);
+
+ status = handle_tx_parameters(user_meta, s, &op);
+ if (status != 0) {
+ goto out;
+ }
+
+ b = &s->buf_mgmt;
+ samples_per_buffer = s->stream_config.samples_per_buffer;
+
+ while (status == 0 && ((samples_written < num_samples) || op.flush)) {
+ switch (s->state) {
+ case SYNC_STATE_CHECK_WORKER: {
+ int stream_error;
+ sync_worker_state worker_state =
+ sync_worker_get_state(s->worker, &stream_error);
+
+ if (stream_error != 0) {
+ status = stream_error;
+ } else {
+ if (worker_state == SYNC_WORKER_STATE_IDLE) {
+ /* No need to reset any buffer management for TX since
+ * the TX stream does not submit an initial set of
+ * buffers. Therefore the RESET_BUF_MGMT state is
+ * skipped here. */
+ s->state = SYNC_STATE_START_WORKER;
+ } else {
+ /* Worker is running - continue onto checking for and
+ * potentially waiting for an available buffer */
+ s->state = SYNC_STATE_WAIT_FOR_BUFFER;
+ }
+ }
+ break;
+ }
+
+ case SYNC_STATE_RESET_BUF_MGMT:
+ assert(!"Bug");
+ break;
+
+ case SYNC_STATE_START_WORKER:
+ sync_worker_submit_request(s->worker, SYNC_WORKER_START);
+
+ status = sync_worker_wait_for_state(
+ s->worker, SYNC_WORKER_STATE_RUNNING,
+ SYNC_WORKER_START_TIMEOUT_MS);
+
+ if (status == 0) {
+ s->state = SYNC_STATE_WAIT_FOR_BUFFER;
+ log_debug("%s: Worker is now running.\n", __FUNCTION__);
+ }
+ break;
+
+ case SYNC_STATE_WAIT_FOR_BUFFER:
+ MUTEX_LOCK(&b->lock);
+
+ /* Check the buffer state, as the worker may have consumed one
+ * since we last queried the status */
+ if (b->status[b->prod_i] == SYNC_BUFFER_EMPTY) {
+ s->state = SYNC_STATE_BUFFER_READY;
+ } else {
+ status =
+ wait_for_buffer(b, timeout_ms, __FUNCTION__, b->prod_i);
+ }
+
+ MUTEX_UNLOCK(&b->lock);
+ break;
+
+ case SYNC_STATE_BUFFER_READY:
+ MUTEX_LOCK(&b->lock);
+ b->status[b->prod_i] = SYNC_BUFFER_PARTIAL;
+ b->partial_off = 0;
+
+ switch (s->stream_config.format) {
+ case BLADERF_FORMAT_SC16_Q11:
+ case BLADERF_FORMAT_SC8_Q7:
+ s->state = SYNC_STATE_USING_BUFFER;
+ break;
+
+ case BLADERF_FORMAT_SC16_Q11_META:
+ case BLADERF_FORMAT_SC8_Q7_META:
+ s->state = SYNC_STATE_USING_BUFFER_META;
+ s->meta.curr_msg_off = 0;
+ s->meta.msg_num = 0;
+ break;
+
+ case BLADERF_FORMAT_PACKET_META:
+ s->state = SYNC_STATE_USING_PACKET_META;
+ s->meta.curr_msg_off = 0;
+ s->meta.msg_num = 0;
+ break;
+
+ default:
+ assert(!"Invalid stream format");
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ MUTEX_UNLOCK(&b->lock);
+ break;
+
+
+ case SYNC_STATE_USING_BUFFER:
+ MUTEX_LOCK(&b->lock);
+
+ buf_dest = (uint8_t *)b->buffers[b->prod_i];
+ samples_to_copy = uint_min(num_samples - samples_written,
+ samples_per_buffer - b->partial_off);
+
+ memcpy(buf_dest + samples2bytes(s, b->partial_off),
+ samples_src + samples2bytes(s, samples_written),
+ samples2bytes(s, samples_to_copy));
+
+ b->partial_off += samples_to_copy;
+ samples_written += samples_to_copy;
+
+ log_verbose("%s: Buffered %u samples from caller\n",
+ __FUNCTION__, samples_to_copy);
+
+ if (b->partial_off >= samples_per_buffer) {
+ /* Check for symptom of out-of-bounds accesses */
+ assert(b->partial_off == samples_per_buffer);
+
+ /* Submit buffer and advance to the next one */
+ status = advance_tx_buffer(s, b);
+ }
+
+ MUTEX_UNLOCK(&b->lock);
+ break;
+
+ case SYNC_STATE_USING_BUFFER_META: /* SC16Q11 buffers w/ metadata */
+ MUTEX_LOCK(&b->lock);
+
+ switch (s->meta.state) {
+ case SYNC_META_STATE_HEADER:
+ buf_dest = (uint8_t *)b->buffers[b->prod_i];
+
+ s->meta.curr_msg =
+ buf_dest + s->meta.msg_size * s->meta.msg_num;
+
+ log_verbose("%s: Set curr_msg to: %p (buf @ %p)\n",
+ __FUNCTION__, s->meta.curr_msg, buf_dest);
+
+ s->meta.curr_msg_off = 0;
+
+ if (s->meta.now) {
+ metadata_set(s->meta.curr_msg, 0, 0);
+ } else {
+ metadata_set(s->meta.curr_msg,
+ s->meta.curr_timestamp, 0);
+ }
+
+ s->meta.state = SYNC_META_STATE_SAMPLES;
+
+ log_verbose("%s: Filled in header (t=%llu)\n",
+ __FUNCTION__,
+ (unsigned long long)s->meta.curr_timestamp);
+ break;
+
+ case SYNC_META_STATE_SAMPLES:
+ if (op.zero_pad) {
+ const uint64_t delta =
+ user_meta->timestamp - s->meta.curr_timestamp;
+
+ size_t to_zero;
+
+ log_verbose("%s: User requested zero padding to "
+ "t=%" PRIu64 " (%" PRIu64 " + %" PRIu64
+ ")\n",
+ __FUNCTION__, user_meta->timestamp,
+ s->meta.curr_timestamp, delta);
+
+ if (delta < left_in_msg(s)) {
+ to_zero = (size_t)delta;
+
+ log_verbose("%s: Padded subset of msg "
+ "(%" PRIu64 " samples)\n",
+ __FUNCTION__, (uint64_t)to_zero);
+ } else {
+ to_zero = left_in_msg(s);
+
+ log_verbose("%s: Padded remainder of msg "
+ "(%" PRIu64 " samples)\n",
+ __FUNCTION__, (uint64_t)to_zero);
+ }
+
+ memset(s->meta.curr_msg + METADATA_HEADER_SIZE +
+ samples2bytes(s, s->meta.curr_msg_off),
+ 0, samples2bytes(s, to_zero));
+
+ s->meta.curr_msg_off += to_zero;
+
+ /* If we're going to supply the FPGA with a
+ * discontinuity, it is required that the last three
+ * samples provided be zero in order to hold the
+ * DAC @ (0 + 0j).
+ *
+ * See "Figure 9: TX data interface" in the LMS6002D
+ * data sheet for the register stages that create
+ * this requirement.
+ *
+ * If we're ending a burst with < 3 zeros samples at
+ * the end of the message, we'll need to continue
+ * onto the next message. At this next message,
+ * we'll either encounter the requested timestamp or
+ * zero-fill the message to fulfil this "three zero
+ * sample" requirement, and set the timestamp
+ * appropriately at the following message.
+ */
+ if (to_zero < 3 && left_in_msg(s) == 0) {
+ s->meta.curr_timestamp += to_zero;
+ log_verbose("Ended msg with < 3 zero samples. "
+ "Padding into next message.\n");
+ } else {
+ s->meta.curr_timestamp = user_meta->timestamp;
+ op.zero_pad = false;
+ }
+ }
+
+ samples_to_copy = uint_min(
+ num_samples - samples_written, left_in_msg(s));
+
+ if (samples_to_copy != 0) {
+ /* We have user data to copy into the current
+ * message within the buffer */
+ memcpy(s->meta.curr_msg + METADATA_HEADER_SIZE +
+ samples2bytes(s, s->meta.curr_msg_off),
+ samples_src +
+ samples2bytes(s, samples_written),
+ samples2bytes(s, samples_to_copy));
+
+ s->meta.curr_msg_off += samples_to_copy;
+ if (s->stream_config.layout == BLADERF_TX_X2)
+ s->meta.curr_timestamp += samples_to_copy / 2;
+ else
+ s->meta.curr_timestamp += samples_to_copy;
+
+ samples_written += samples_to_copy;
+
+ log_verbose("%s: Copied %u samples. "
+ "Current message offset is now: %u\n",
+ __FUNCTION__, samples_to_copy,
+ s->meta.curr_msg_off);
+ }
+
+ if (left_in_msg(s) != 0 && op.flush) {
+ /* We're ending this buffer early and need to
+ * flush the remaining samples by setting all
+ * samples in the messages to (0 + 0j) */
+ const unsigned int to_zero = left_in_msg(s);
+
+ const size_t off =
+ METADATA_HEADER_SIZE +
+ samples2bytes(s, s->meta.curr_msg_off);
+
+ /* If we're here, we should have already copied
+ * all requested data to the buffer */
+ assert(num_samples == samples_written);
+
+ memset(s->meta.curr_msg + off, 0,
+ samples2bytes(s, to_zero));
+
+ log_verbose(
+ "%s: Flushed %u samples @ %u (0x%08x)\n",
+ __FUNCTION__, to_zero, s->meta.curr_msg_off,
+ off);
+
+ s->meta.curr_msg_off += to_zero;
+ s->meta.curr_timestamp += to_zero;
+ }
+
+ if (left_in_msg(s) == 0) {
+ s->meta.msg_num++;
+ s->meta.state = SYNC_META_STATE_HEADER;
+
+ log_verbose("%s: Advancing to next message (%u)\n",
+ __FUNCTION__, s->meta.msg_num);
+ }
+
+ if (s->meta.msg_num >= s->meta.msg_per_buf) {
+ assert(s->meta.msg_num == s->meta.msg_per_buf);
+
+ /* Submit buffer of samples for transmission */
+ status = advance_tx_buffer(s, b);
+
+ s->meta.msg_num = 0;
+ s->state = SYNC_STATE_WAIT_FOR_BUFFER;
+
+ /* We want to clear the flush flag if we've written
+ * all of our data, but keep it set if we have more
+ * data and need wrap around to another buffer */
+ op.flush =
+ op.flush && (samples_written != num_samples);
+ }
+
+ break;
+
+ default:
+ assert(!"Invalid state");
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ MUTEX_UNLOCK(&b->lock);
+ break;
+
+ case SYNC_STATE_USING_PACKET_META: /* Packet buffers w/ metadata */
+ MUTEX_LOCK(&b->lock);
+
+ buf_dest = (uint8_t *)b->buffers[b->prod_i];
+
+ memcpy(buf_dest + METADATA_HEADER_SIZE, samples_src, num_samples*4);
+
+ b->actual_lengths[b->prod_i] = samples2bytes(s, num_samples) + METADATA_HEADER_SIZE;
+
+ metadata_set_packet(buf_dest, 0, 0, num_samples, 0, 0);
+
+ samples_written = num_samples;
+
+ status = advance_tx_buffer(s, b);
+
+ s->meta.msg_num = 0;
+ s->state = SYNC_STATE_WAIT_FOR_BUFFER;
+
+ MUTEX_UNLOCK(&b->lock);
+ break;
+
+ }
+ }
+
+ if (status == 0 &&
+ s->stream_config.format == BLADERF_FORMAT_SC16_Q11_META &&
+ (user_meta->flags & BLADERF_META_FLAG_TX_BURST_END)) {
+ s->meta.in_burst = false;
+ s->meta.now = false;
+ }
+
+out:
+ MUTEX_UNLOCK(&s->lock);
+
+ return status;
+}
+
+unsigned int sync_buf2idx(struct buffer_mgmt *b, void *addr)
+{
+ unsigned int i;
+
+ for (i = 0; i < b->num_buffers; i++) {
+ if (b->buffers[i] == addr) {
+ return i;
+ }
+ }
+
+ assert(!"Bug: Buffer not found.");
+
+ /* Assertions are intended to always remain on. If someone turned them
+ * off, do the best we can...complain loudly and clobber a buffer */
+ log_critical("Bug: Buffer not found.");
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/src/streaming/sync.h b/Radio/HW/BladeRF/src/streaming/sync.h
new file mode 100644
index 0000000..8977791
--- /dev/null
+++ b/Radio/HW/BladeRF/src/streaming/sync.h
@@ -0,0 +1,193 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef STREAMING_SYNC_H_
+#define STREAMING_SYNC_H_
+
+#include <limits.h>
+#include <pthread.h>
+
+#include <libbladeRF.h>
+
+#include "thread.h"
+
+/* These parameters are only written during sync_init */
+struct stream_config {
+ bladerf_format format;
+ bladerf_channel_layout layout;
+
+ unsigned int samples_per_buffer;
+ unsigned int num_xfers;
+ unsigned int timeout_ms;
+
+ size_t bytes_per_sample;
+};
+
+typedef enum {
+ SYNC_BUFFER_EMPTY = 0, /**< Buffer contains no data */
+ SYNC_BUFFER_PARTIAL, /**< sync_rx/tx is currently emptying/filling */
+ SYNC_BUFFER_FULL, /**< Buffer is full of data */
+ SYNC_BUFFER_IN_FLIGHT, /**< Currently being transferred */
+} sync_buffer_status;
+
+typedef enum {
+ SYNC_META_STATE_HEADER, /**< Extract the metadata header */
+ SYNC_META_STATE_SAMPLES, /**< Process samples */
+} sync_meta_state;
+
+typedef enum {
+ /** Invalid selection */
+ SYNC_TX_SUBMITTER_INVALID = -1,
+
+ /** sync_tx() is repsonsible for submitting buffers for async transfer */
+ SYNC_TX_SUBMITTER_FN,
+
+ /** The TX worker callbacks should be returning buffers for submission */
+ SYNC_TX_SUBMITTER_CALLBACK
+} sync_tx_submitter;
+
+#define BUFFER_MGMT_INVALID_INDEX (UINT_MAX)
+
+struct buffer_mgmt {
+ sync_buffer_status *status;
+ size_t *actual_lengths;
+
+ void **buffers;
+ unsigned int num_buffers;
+
+ unsigned int prod_i; /**< Producer index - next buffer to fill */
+ unsigned int cons_i; /**< Consumer index - next buffer to empty */
+ unsigned int partial_off; /**< Current index into partial buffer */
+
+ /* In the event of a SW RX overrun, this count is used to determine
+ * how many more transfers should be considered invalid and require
+ * resubmission */
+ unsigned int resubmit_count;
+
+ /* Applicable to TX only. Denotes which context is responsible for
+ * submitting full buffers to the underlying async system */
+ sync_tx_submitter submitter;
+
+
+ MUTEX lock;
+ pthread_cond_t buf_ready; /**< Buffer produced by RX callback, or
+ * buffer emptied by TX callback */
+};
+
+/* State of API-side sync interface */
+typedef enum {
+ SYNC_STATE_CHECK_WORKER,
+ SYNC_STATE_RESET_BUF_MGMT,
+ SYNC_STATE_START_WORKER,
+ SYNC_STATE_WAIT_FOR_BUFFER,
+ SYNC_STATE_BUFFER_READY,
+ SYNC_STATE_USING_BUFFER,
+ SYNC_STATE_USING_PACKET_META,
+ SYNC_STATE_USING_BUFFER_META
+} sync_state;
+
+struct sync_meta {
+ sync_meta_state state; /* State of metadata processing */
+
+ uint8_t *curr_msg; /* Points to current message in the buffer */
+ size_t curr_msg_off; /* Offset into current message (samples),
+ * ignoring the 4-samples worth of metadata */
+ size_t msg_size; /* Size of data message */
+ unsigned int msg_per_buf; /* Number of data messages per buffer */
+ unsigned int msg_num; /* Which message within the buffer are we in?
+ * Range is: 0 to msg_per_buf */
+ unsigned int samples_per_msg; /* Number of samples within a message */
+ unsigned int samples_per_ts; /* Number of samples within a timestamp */
+
+ union {
+ /* Used only for RX */
+ struct {
+ uint64_t
+ msg_timestamp; /* Timestamp contained in the current message */
+ uint32_t msg_flags; /* Flags for the current message */
+ };
+
+ /* Used only for TX */
+ struct {
+ bool in_burst;
+ bool now;
+ };
+ };
+
+ uint64_t curr_timestamp; /* Timestamp at the sample we've
+ * consumed up to */
+};
+
+struct bladerf_sync {
+ MUTEX lock;
+ struct bladerf *dev;
+ bool initialized;
+ sync_state state;
+ struct buffer_mgmt buf_mgmt;
+ struct stream_config stream_config;
+ struct sync_worker *worker;
+ struct sync_meta meta;
+};
+
+/**
+ * Create and initialize as synchronous interface handle for the specified
+ * device and direction. If the synchronous handle is already initialized, this
+ * call will first deinitialize it.
+ *
+ * The associated stream will be started at the first RX or TX call
+ *
+ * @return 0 or BLADERF_ERR_* value on failure
+ */
+int sync_init(struct bladerf_sync *sync,
+ struct bladerf *dev,
+ bladerf_channel_layout layout,
+ bladerf_format format,
+ unsigned int num_buffers,
+ size_t buffer_size,
+ size_t msg_size,
+ unsigned int num_transfers,
+ unsigned int stream_timeout);
+
+/**
+ * Deinitialize the sync handle. This tears down and deallocates the underlying
+ * asynchronous stream.
+ *
+ * @param[inout] sync Handle to deinitialize.
+ */
+void sync_deinit(struct bladerf_sync *sync);
+
+int sync_rx(struct bladerf_sync *sync,
+ void *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms);
+
+int sync_tx(struct bladerf_sync *sync,
+ void const *samples,
+ unsigned int num_samples,
+ struct bladerf_metadata *metadata,
+ unsigned int timeout_ms);
+
+unsigned int sync_buf2idx(struct buffer_mgmt *b, void *addr);
+
+void *sync_idx2buf(struct buffer_mgmt *b, unsigned int idx);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/streaming/sync_worker.c b/Radio/HW/BladeRF/src/streaming/sync_worker.c
new file mode 100644
index 0000000..b2ec806
--- /dev/null
+++ b/Radio/HW/BladeRF/src/streaming/sync_worker.c
@@ -0,0 +1,532 @@
+/*
+ * Copyright (C) 2014-2015 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Only switch on the verbose debug prints in this file when we *really* want
+ * them. Otherwise, compile them out to avoid excessive log level checks
+ * in our data path */
+#include "log.h"
+#ifndef ENABLE_LIBBLADERF_SYNC_LOG_VERBOSE
+#undef log_verbose
+#define log_verbose(...)
+#endif
+#include "rel_assert.h"
+#include "conversions.h"
+#include "minmax.h"
+
+#include "async.h"
+#include "sync.h"
+#include "sync_worker.h"
+
+#include "board/board.h"
+#include "backend/usb/usb.h"
+
+#define worker2str(s) (direction2str(s->stream_config.layout & BLADERF_DIRECTION_MASK))
+
+void *sync_worker_task(void *arg);
+
+static void *rx_callback(struct bladerf *dev,
+ struct bladerf_stream *stream,
+ struct bladerf_metadata *meta,
+ void *samples,
+ size_t num_samples,
+ void *user_data)
+{
+ unsigned int requests; /* Pending requests */
+ unsigned int next_idx;
+ unsigned int samples_idx;
+ void *next_buf = NULL; /* Next buffer to submit for reception */
+
+ struct bladerf_sync *s = (struct bladerf_sync *)user_data;
+ struct sync_worker *w = s->worker;
+ struct buffer_mgmt *b = &s->buf_mgmt;
+
+ /* Check if the caller has requested us to shut down. We'll keep the
+ * SHUTDOWN bit set through our transition into the IDLE state so we
+ * can act on it there. */
+ MUTEX_LOCK(&w->request_lock);
+ requests = w->requests;
+ MUTEX_UNLOCK(&w->request_lock);
+
+ if (requests & SYNC_WORKER_STOP) {
+ log_verbose("%s worker: Got STOP request upon entering callback. "
+ "Ending stream.\n", worker2str(s));
+ return NULL;
+ }
+
+ MUTEX_LOCK(&b->lock);
+
+ /* Get the index of the buffer that was just filled */
+ samples_idx = sync_buf2idx(b, samples);
+
+ if (b->resubmit_count == 0) {
+ if (b->status[b->prod_i] == SYNC_BUFFER_EMPTY) {
+
+ /* This buffer is now ready for the consumer */
+ b->status[samples_idx] = SYNC_BUFFER_FULL;
+ b->actual_lengths[samples_idx] = num_samples;
+ pthread_cond_signal(&b->buf_ready);
+
+ /* Update the state of the buffer being submitted next */
+ next_idx = b->prod_i;
+ b->status[next_idx] = SYNC_BUFFER_IN_FLIGHT;
+ next_buf = b->buffers[next_idx];
+
+ /* Advance to the next buffer for the next callback */
+ b->prod_i = (next_idx + 1) % b->num_buffers;
+
+ log_verbose("%s worker: buf[%u] = full, buf[%u] = in_flight\n",
+ worker2str(s), samples_idx, next_idx);
+
+ } else {
+ /* TODO propagate back the RX Overrun to the sync_rx() caller */
+ log_debug("RX overrun @ buffer %u\r\n", samples_idx);
+
+ next_buf = samples;
+ b->resubmit_count = s->stream_config.num_xfers - 1;
+ }
+ } else {
+ /* We're still recovering from an overrun at this point. Just
+ * turn around and resubmit this buffer */
+ next_buf = samples;
+ b->resubmit_count--;
+ log_verbose("Resubmitting buffer %u (%u resubmissions left)\r\n",
+ samples_idx, b->resubmit_count);
+ }
+
+
+ MUTEX_UNLOCK(&b->lock);
+ return next_buf;
+}
+
+static void *tx_callback(struct bladerf *dev,
+ struct bladerf_stream *stream,
+ struct bladerf_metadata *meta,
+ void *samples,
+ size_t num_samples,
+ void *user_data)
+{
+ unsigned int requests; /* Pending requests */
+ unsigned int completed_idx; /* Index of completed buffer */
+
+ struct bladerf_sync *s = (struct bladerf_sync *)user_data;
+ struct sync_worker *w = s->worker;
+ struct buffer_mgmt *b = &s->buf_mgmt;
+
+ void *ret = BLADERF_STREAM_NO_DATA;
+
+ /* Check if the caller has requested us to shut down. We'll keep the
+ * SHUTDOWN bit set through our transition into the IDLE state so we
+ * can act on it there. */
+ MUTEX_LOCK(&w->request_lock);
+ requests = w->requests;
+ MUTEX_UNLOCK(&w->request_lock);
+
+ if (requests & SYNC_WORKER_STOP) {
+ log_verbose("%s worker: Got STOP request upon entering callback. "
+ "Ending stream.\r\n", worker2str(s));
+ return NULL;
+ }
+
+ /* The initial set of callbacks will do not provide us with any
+ * completed sample buffers */
+ if (samples != NULL) {
+ MUTEX_LOCK(&b->lock);
+
+ /* Mark the completed buffer as being empty */
+ completed_idx = sync_buf2idx(b, samples);
+ assert(b->status[completed_idx] == SYNC_BUFFER_IN_FLIGHT);
+ b->status[completed_idx] = SYNC_BUFFER_EMPTY;
+ pthread_cond_signal(&b->buf_ready);
+
+ /* If the callback is assigned to be the submitter, there are
+ * buffers pending submission */
+ if (b->submitter == SYNC_TX_SUBMITTER_CALLBACK) {
+ assert(b->cons_i != BUFFER_MGMT_INVALID_INDEX);
+ if (b->status[b->cons_i] == SYNC_BUFFER_FULL) {
+ /* This buffer is ready to ship out ("consume") */
+ log_verbose("%s: Submitting deferred buf[%u]\n",
+ __FUNCTION__, b->cons_i);
+
+ ret = b->buffers[b->cons_i];
+ /* This is actually # of 32bit DWORDs for PACKET_META */
+ meta->actual_count = b->actual_lengths[b->cons_i];
+ b->status[b->cons_i] = SYNC_BUFFER_IN_FLIGHT;
+ b->cons_i = (b->cons_i + 1) % b->num_buffers;
+ } else {
+ log_verbose("%s: No deferred buffer available. "
+ "Assigning submitter=FN\n", __FUNCTION__);
+
+ b->submitter = SYNC_TX_SUBMITTER_FN;
+ b->cons_i = BUFFER_MGMT_INVALID_INDEX;
+ }
+ }
+
+ MUTEX_UNLOCK(&b->lock);
+
+ log_verbose("%s worker: Buffer %u emptied.\r\n",
+ worker2str(s), completed_idx);
+ }
+
+ return ret;
+}
+
+int sync_worker_init(struct bladerf_sync *s)
+{
+ int status = 0;
+ s->worker = (struct sync_worker *)calloc(1, sizeof(*s->worker));
+
+ if (s->worker == NULL) {
+ status = BLADERF_ERR_MEM;
+ goto worker_init_out;
+ }
+
+ s->worker->state = SYNC_WORKER_STATE_STARTUP;
+ s->worker->err_code = 0;
+
+ s->worker->cb =
+ (s->stream_config.layout & BLADERF_DIRECTION_MASK) == BLADERF_RX
+ ? rx_callback
+ : tx_callback;
+
+ status = async_init_stream(
+ &s->worker->stream, s->dev, s->worker->cb, &s->buf_mgmt.buffers,
+ s->buf_mgmt.num_buffers, s->stream_config.format,
+ s->stream_config.samples_per_buffer, s->stream_config.num_xfers, s);
+
+ if (status != 0) {
+ log_debug("%s worker: Failed to init stream: %s\n", worker2str(s),
+ bladerf_strerror(status));
+ goto worker_init_out;
+ }
+
+ status = async_set_transfer_timeout(
+ s->worker->stream,
+ uint_max(s->stream_config.timeout_ms, BULK_TIMEOUT_MS));
+ if (status != 0) {
+ log_debug("%s worker: Failed to set transfer timeout: %s\n",
+ worker2str(s), bladerf_strerror(status));
+ goto worker_init_out;
+ }
+
+ MUTEX_INIT(&s->worker->state_lock);
+ MUTEX_INIT(&s->worker->request_lock);
+
+ status = pthread_cond_init(&s->worker->state_changed, NULL);
+ if (status != 0) {
+ log_debug("%s worker: pthread_cond_init(state_changed) failed: %d\n",
+ worker2str(s), status);
+ status = BLADERF_ERR_UNEXPECTED;
+ goto worker_init_out;
+ }
+
+ status = pthread_cond_init(&s->worker->requests_pending, NULL);
+ if (status != 0) {
+ log_debug("%s worker: pthread_cond_init(requests_pending) failed: %d\n",
+ worker2str(s), status);
+ status = BLADERF_ERR_UNEXPECTED;
+ goto worker_init_out;
+ }
+
+ status = pthread_create(&s->worker->thread, NULL, sync_worker_task, s);
+ if (status != 0) {
+ log_debug("%s worker: pthread_create failed: %d\n", worker2str(s),
+ status);
+ status = BLADERF_ERR_UNEXPECTED;
+ goto worker_init_out;
+ }
+
+ /* Wait until the worker thread has initialized and is ready to go */
+ status =
+ sync_worker_wait_for_state(s->worker, SYNC_WORKER_STATE_IDLE, 1000);
+ if (status != 0) {
+ log_debug("%s worker: sync_worker_wait_for_state failed: %d\n",
+ worker2str(s), status);
+ status = BLADERF_ERR_TIMEOUT;
+ goto worker_init_out;
+ }
+
+worker_init_out:
+ if (status != 0) {
+ free(s->worker);
+ s->worker = NULL;
+ }
+
+ return status;
+}
+
+void sync_worker_deinit(struct sync_worker *w,
+ pthread_mutex_t *lock, pthread_cond_t *cond)
+{
+ int status;
+
+ if (w == NULL) {
+ log_debug("%s called with NULL ptr\n", __FUNCTION__);
+ return;
+ }
+
+ log_verbose("%s: Requesting worker %p to stop...\n", __FUNCTION__, w);
+
+ sync_worker_submit_request(w, SYNC_WORKER_STOP);
+
+ if (lock != NULL && cond != NULL) {
+ MUTEX_LOCK(lock);
+ pthread_cond_signal(cond);
+ MUTEX_UNLOCK(lock);
+ }
+
+ status = sync_worker_wait_for_state(w, SYNC_WORKER_STATE_STOPPED, 3000);
+
+ if (status != 0) {
+ log_warning("Timed out while stopping worker. Canceling thread.\n");
+ pthread_cancel(w->thread);
+ }
+
+ pthread_join(w->thread, NULL);
+ log_verbose("%s: Worker joined.\n", __FUNCTION__);
+
+ async_deinit_stream(w->stream);
+
+ free(w);
+}
+
+void sync_worker_submit_request(struct sync_worker *w, unsigned int request)
+{
+ MUTEX_LOCK(&w->request_lock);
+ w->requests |= request;
+ pthread_cond_signal(&w->requests_pending);
+ MUTEX_UNLOCK(&w->request_lock);
+}
+
+int sync_worker_wait_for_state(struct sync_worker *w, sync_worker_state state,
+ unsigned int timeout_ms)
+{
+ int status = 0;
+ struct timespec timeout_abs;
+ const int nsec_per_sec = 1000 * 1000 * 1000;
+
+ if (timeout_ms != 0) {
+ const unsigned int timeout_sec = timeout_ms / 1000;
+
+ status = clock_gettime(CLOCK_REALTIME, &timeout_abs);
+ if (status != 0) {
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ timeout_abs.tv_sec += timeout_sec;
+ timeout_abs.tv_nsec += (timeout_ms % 1000) * 1000 * 1000;
+
+ if (timeout_abs.tv_nsec >= nsec_per_sec) {
+ timeout_abs.tv_sec += timeout_abs.tv_nsec / nsec_per_sec;
+ timeout_abs.tv_nsec %= nsec_per_sec;
+ }
+
+ MUTEX_LOCK(&w->state_lock);
+ status = 0;
+ while (w->state != state && status == 0) {
+ status = pthread_cond_timedwait(&w->state_changed,
+ &w->state_lock,
+ &timeout_abs);
+ }
+ MUTEX_UNLOCK(&w->state_lock);
+
+ } else {
+ MUTEX_LOCK(&w->state_lock);
+ while (w->state != state) {
+ log_verbose(": Waiting for state change, current = %d\n", w->state);
+ status = pthread_cond_wait(&w->state_changed,
+ &w->state_lock);
+ }
+ MUTEX_UNLOCK(&w->state_lock);
+ }
+
+ if (status != 0) {
+ log_debug("%s: Wait on state change failed: %s\n",
+ __FUNCTION__, strerror(status));
+
+ if (status == ETIMEDOUT) {
+ status = BLADERF_ERR_TIMEOUT;
+ } else {
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+ }
+
+ return status;
+}
+
+sync_worker_state sync_worker_get_state(struct sync_worker *w,
+ int *err_code)
+{
+ sync_worker_state ret;
+
+ MUTEX_LOCK(&w->state_lock);
+ ret = w->state;
+ if (err_code) {
+ *err_code = w->err_code;
+ w->err_code = 0;
+ }
+ MUTEX_UNLOCK(&w->state_lock);
+
+ return ret;
+}
+
+static void set_state(struct sync_worker *w, sync_worker_state state)
+{
+ MUTEX_LOCK(&w->state_lock);
+ w->state = state;
+ pthread_cond_signal(&w->state_changed);
+ MUTEX_UNLOCK(&w->state_lock);
+}
+
+
+static sync_worker_state exec_idle_state(struct bladerf_sync *s)
+{
+ sync_worker_state next_state = SYNC_WORKER_STATE_IDLE;
+ unsigned int requests;
+ unsigned int i;
+
+ MUTEX_LOCK(&s->worker->request_lock);
+
+ while (s->worker->requests == 0) {
+ log_verbose("%s worker: Waiting for pending requests\n", worker2str(s));
+
+ pthread_cond_wait(&s->worker->requests_pending,
+ &s->worker->request_lock);
+ }
+
+ requests = s->worker->requests;
+ s->worker->requests = 0;
+ MUTEX_UNLOCK(&s->worker->request_lock);
+
+ if (requests & SYNC_WORKER_STOP) {
+ log_verbose("%s worker: Got request to stop\n", worker2str(s));
+
+ next_state = SYNC_WORKER_STATE_SHUTTING_DOWN;
+
+ } else if (requests & SYNC_WORKER_START) {
+ log_verbose("%s worker: Got request to start\n", worker2str(s));
+ MUTEX_LOCK(&s->buf_mgmt.lock);
+
+ if ((s->stream_config.layout & BLADERF_DIRECTION_MASK) == BLADERF_TX) {
+ /* If we've previously timed out on a stream, we'll likely have some
+ * stale buffers marked "in-flight" that have since been cancelled. */
+ for (i = 0; i < s->buf_mgmt.num_buffers; i++) {
+ if (s->buf_mgmt.status[i] == SYNC_BUFFER_IN_FLIGHT) {
+ s->buf_mgmt.status[i] = SYNC_BUFFER_EMPTY;
+ }
+ }
+
+ pthread_cond_signal(&s->buf_mgmt.buf_ready);
+ } else {
+ s->buf_mgmt.prod_i = s->stream_config.num_xfers;
+
+ for (i = 0; i < s->buf_mgmt.num_buffers; i++) {
+ if (i < s->stream_config.num_xfers) {
+ s->buf_mgmt.status[i] = SYNC_BUFFER_IN_FLIGHT;
+ } else if (s->buf_mgmt.status[i] == SYNC_BUFFER_IN_FLIGHT) {
+ s->buf_mgmt.status[i] = SYNC_BUFFER_EMPTY;
+ }
+ }
+ }
+
+ MUTEX_UNLOCK(&s->buf_mgmt.lock);
+
+ next_state = SYNC_WORKER_STATE_RUNNING;
+ } else {
+ log_warning("Invalid request value encountered: 0x%08X\n",
+ s->worker->requests);
+ }
+
+ return next_state;
+}
+
+static void exec_running_state(struct bladerf_sync *s)
+{
+ int status;
+
+ status = async_run_stream(s->worker->stream, s->stream_config.layout);
+
+ log_verbose("%s worker: stream ended with: %s\n",
+ worker2str(s), bladerf_strerror(status));
+
+ /* Save off the result of running the stream so we can report what
+ * happened to the API caller */
+ MUTEX_LOCK(&s->worker->state_lock);
+ s->worker->err_code = status;
+ MUTEX_UNLOCK(&s->worker->state_lock);
+
+ /* Wake the API-side if an error occurred, so that it can propagate
+ * the stream error code back to the API caller */
+ if (status != 0) {
+ MUTEX_LOCK(&s->buf_mgmt.lock);
+ pthread_cond_signal(&s->buf_mgmt.buf_ready);
+ MUTEX_UNLOCK(&s->buf_mgmt.lock);
+ }
+}
+
+void *sync_worker_task(void *arg)
+{
+ sync_worker_state state = SYNC_WORKER_STATE_IDLE;
+ struct bladerf_sync *s = (struct bladerf_sync *)arg;
+
+ log_verbose("%s worker: task started\n", worker2str(s));
+ set_state(s->worker, state);
+ log_verbose("%s worker: task state set\n", worker2str(s));
+
+ while (state != SYNC_WORKER_STATE_STOPPED) {
+
+ switch (state) {
+ case SYNC_WORKER_STATE_STARTUP:
+ assert(!"Worker in unexpected state, shutting down. (STARTUP)");
+ set_state(s->worker, SYNC_WORKER_STATE_SHUTTING_DOWN);
+ break;
+
+ case SYNC_WORKER_STATE_IDLE:
+ state = exec_idle_state(s);
+ set_state(s->worker, state);
+ break;
+
+ case SYNC_WORKER_STATE_RUNNING:
+ exec_running_state(s);
+ state = SYNC_WORKER_STATE_IDLE;
+ set_state(s->worker, state);
+ break;
+
+ case SYNC_WORKER_STATE_SHUTTING_DOWN:
+ log_verbose("%s worker: Shutting down...\n", worker2str(s));
+
+ state = SYNC_WORKER_STATE_STOPPED;
+ set_state(s->worker, state);
+ break;
+
+ case SYNC_WORKER_STATE_STOPPED:
+ assert(!"Worker in unexpected state: STOPPED");
+ break;
+
+ default:
+ assert(!"Worker in unexpected state, shutting down. (UNKNOWN)");
+ set_state(s->worker, SYNC_WORKER_STATE_SHUTTING_DOWN);
+ break;
+ }
+ }
+
+ return NULL;
+}
diff --git a/Radio/HW/BladeRF/src/streaming/sync_worker.h b/Radio/HW/BladeRF/src/streaming/sync_worker.h
new file mode 100644
index 0000000..35d5075
--- /dev/null
+++ b/Radio/HW/BladeRF/src/streaming/sync_worker.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef STREAMING_SYNC_WORKER_H_
+#define STREAMING_SYNC_WORKER_H_
+
+#include "host_config.h"
+#include "sync.h"
+#include <libbladeRF.h>
+#include <pthread.h>
+
+#if BLADERF_OS_WINDOWS || BLADERF_OS_OSX
+#include "clock_gettime.h"
+#else
+#include <time.h>
+#endif
+
+/* Worker lifetime:
+ *
+ * STARTUP --+--> IDLE --> RUNNING --+--> SHUTTING_DOWN --> STOPPED
+ * ^----------------------/
+ */
+
+/* Request flags */
+#define SYNC_WORKER_START (1 << 0)
+#define SYNC_WORKER_STOP (1 << 1)
+
+typedef enum {
+ SYNC_WORKER_STATE_STARTUP,
+ SYNC_WORKER_STATE_IDLE,
+ SYNC_WORKER_STATE_RUNNING,
+ SYNC_WORKER_STATE_SHUTTING_DOWN,
+ SYNC_WORKER_STATE_STOPPED
+} sync_worker_state;
+
+struct sync_worker {
+ pthread_t thread;
+
+ struct bladerf_stream *stream;
+ bladerf_stream_cb cb;
+
+ /* These items should be accessed while holding state_lock */
+ sync_worker_state state;
+ int err_code;
+ MUTEX state_lock;
+ pthread_cond_t state_changed; /* Worker thread uses this to inform a
+ * waiting main thread about a state
+ * change */
+
+ /* The requests lock should always be acquired AFTER
+ * the sync->buf_mgmt.lock
+ */
+ unsigned int requests;
+ pthread_cond_t requests_pending;
+ MUTEX request_lock;
+};
+
+/**
+ * Create a launch a worker thread. It will enter the IDLE state upon
+ * executing.
+ *
+ * @param s Sync handle containing worker to initialize
+ *
+ * @return 0 on success, BLADERF_ERR_* on failure
+ */
+int sync_worker_init(struct bladerf_sync *s);
+
+/**
+ * Shutdown and deinitialize
+ *
+ * @param w Worker to deinitialize
+ * @param[in] lock Acquired to signal `cond` if non-NULL
+ * @param[in] cond If non-NULL, this is signaled after requesting the
+ * worker to shut down, waking a potentially blocked
+ * workers.
+ */
+void sync_worker_deinit(struct sync_worker *w,
+ pthread_mutex_t *lock,
+ pthread_cond_t *cond);
+
+/**
+ * Wait for state change with optional timeout
+ *
+ * @param w Worker to wait for
+ * @param[in] state State to wait for
+ * @param[in] timeout_ms Timeout in ms. 0 implies "wait forever"
+ *
+ * @return 0 on success, BLADERF_ERR_TIMEOUT on timeout, BLADERF_ERR_UNKNOWN on
+ * other errors
+ */
+int sync_worker_wait_for_state(struct sync_worker *w,
+ sync_worker_state state,
+ unsigned int timeout_ms);
+
+/**
+ * Get the worker's current state.
+ *
+ * @param w Worker to query
+ * @param[out] err_code Stream error code (libbladeRF error code value).
+ * Querying this value will reset the interal error
+ * code value.
+ *
+ * @return Worker's current state
+ */
+sync_worker_state sync_worker_get_state(struct sync_worker *w, int *err_code);
+
+/**
+ * Submit a request to the worker task
+ *
+ * @param w Worker to send request to
+ * @param[in] request Bitmask of requests to submit
+ */
+void sync_worker_submit_request(struct sync_worker *w, unsigned int request);
+
+#endif
diff --git a/Radio/HW/BladeRF/src/test.swift b/Radio/HW/BladeRF/src/test.swift
new file mode 100644
index 0000000..29717de
--- /dev/null
+++ b/Radio/HW/BladeRF/src/test.swift
@@ -0,0 +1,8 @@
+//
+// test.swift
+// PrySDR
+//
+// Created by Jacky Jack on 01/11/2024.
+//
+import libbladerf
+//var version:bladerf_version
diff --git a/Radio/HW/BladeRF/src/version.h b/Radio/HW/BladeRF/src/version.h
new file mode 100644
index 0000000..999d860
--- /dev/null
+++ b/Radio/HW/BladeRF/src/version.h
@@ -0,0 +1,12 @@
+#ifndef VERSION_H_
+#define VERSION_H_
+
+#define LIBBLADERF_VERSION "2.5.1-git-fe3304d7-dirty"
+
+// clang-format off
+#define LIBBLADERF_VERSION_MAJOR 2
+#define LIBBLADERF_VERSION_MINOR 5
+#define LIBBLADERF_VERSION_PATCH 1
+// clang-format on
+
+#endif
diff --git a/Radio/HW/BladeRF/src/version.h.in b/Radio/HW/BladeRF/src/version.h.in
new file mode 100644
index 0000000..7e66202
--- /dev/null
+++ b/Radio/HW/BladeRF/src/version.h.in
@@ -0,0 +1,12 @@
+#ifndef VERSION_H_
+#define VERSION_H_
+
+#define LIBBLADERF_VERSION "@VERSION@"
+
+// clang-format off
+#define LIBBLADERF_VERSION_MAJOR @VERSION_INFO_MAJOR@
+#define LIBBLADERF_VERSION_MINOR @VERSION_INFO_MINOR@
+#define LIBBLADERF_VERSION_PATCH @VERSION_INFO_PATCH@
+// clang-format on
+
+#endif
diff --git a/Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/adc_core.c b/Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/adc_core.c
new file mode 100644
index 0000000..9b7742d
--- /dev/null
+++ b/Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/adc_core.c
@@ -0,0 +1,261 @@
+/***************************************************************************//**
+ * @file adc_core.c
+ * @brief Implementation of ADC Core Driver.
+ * @author DBogdan (dragos.bogdan@analog.com)
+********************************************************************************
+ * Copyright 2013(c) Analog Devices, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * - Neither the name of Analog Devices, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * - The use of this software may or may not infringe the patent rights
+ * of one or more patent holders. This license does not release you
+ * from the requirement that you obtain separate licenses from these
+ * patent holders to use this software.
+ * - Use of the software either in source or binary form, must be run
+ * on or directly connected to an Analog Devices Inc. component.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*******************************************************************************/
+
+/******************************************************************************/
+/***************************** Include Files **********************************/
+/******************************************************************************/
+
+#include <stdint.h>
+
+#include "adc_core.h"
+#include "platform.h"
+#include "util.h"
+
+/***************************************************************************//**
+ * @brief adc_read
+*******************************************************************************/
+int adc_read(struct ad9361_rf_phy *phy, uint32_t regAddr, uint32_t *data)
+{
+ return axiadc_read(phy->adc_state, regAddr, data);
+}
+
+/***************************************************************************//**
+ * @brief adc_write
+*******************************************************************************/
+int adc_write(struct ad9361_rf_phy *phy, uint32_t regAddr, uint32_t data)
+{
+ return axiadc_write(phy->adc_state, regAddr, data);
+}
+
+/***************************************************************************//**
+ * @brief adc_init
+*******************************************************************************/
+int adc_init(struct ad9361_rf_phy *phy)
+{
+ int ret;
+
+ ret = adc_write(phy, ADC_REG_RSTN, 0);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = adc_write(phy, ADC_REG_RSTN, ADC_RSTN);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = adc_write(phy, ADC_REG_CHAN_CNTRL(0),
+ ADC_IQCOR_ENB | ADC_FORMAT_SIGNEXT | ADC_FORMAT_ENABLE | ADC_ENABLE);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = adc_write(phy, ADC_REG_CHAN_CNTRL(1),
+ ADC_IQCOR_ENB | ADC_FORMAT_SIGNEXT | ADC_FORMAT_ENABLE | ADC_ENABLE);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (phy->pdata->rx2tx2)
+ {
+ ret = adc_write(phy, ADC_REG_CHAN_CNTRL(2),
+ ADC_IQCOR_ENB | ADC_FORMAT_SIGNEXT | ADC_FORMAT_ENABLE | ADC_ENABLE);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = adc_write(phy, ADC_REG_CHAN_CNTRL(3),
+ ADC_IQCOR_ENB | ADC_FORMAT_SIGNEXT | ADC_FORMAT_ENABLE | ADC_ENABLE);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief adc_set_calib_scale_phase
+*******************************************************************************/
+int32_t adc_set_calib_scale_phase(struct ad9361_rf_phy *phy,
+ uint32_t phase,
+ uint32_t chan,
+ int32_t val,
+ int32_t val2)
+{
+ int ret;
+ uint32_t fract;
+ uint64_t llval;
+ uint32_t tmp;
+
+ switch (val) {
+ case 1:
+ fract = 0x4000;
+ break;
+ case -1:
+ fract = 0xC000;
+ break;
+ case 0:
+ fract = 0;
+ if (val2 < 0) {
+ fract = 0x8000;
+ val2 *= -1;
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ llval = (uint64_t)val2 * 0x4000UL + (1000000UL / 2);
+ do_div(&llval, 1000000UL);
+ fract |= llval;
+
+ ret = adc_read(phy, ADC_REG_CHAN_CNTRL_2(chan), &tmp);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (!((chan + phase) % 2)) {
+ tmp &= ~ADC_IQCOR_COEFF_1(~0);
+ tmp |= ADC_IQCOR_COEFF_1(fract);
+ } else {
+ tmp &= ~ADC_IQCOR_COEFF_2(~0);
+ tmp |= ADC_IQCOR_COEFF_2(fract);
+ }
+
+ ret = adc_write(phy, ADC_REG_CHAN_CNTRL_2(chan), tmp);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief adc_get_calib_scale_phase
+*******************************************************************************/
+int32_t adc_get_calib_scale_phase(struct ad9361_rf_phy *phy,
+ uint32_t phase,
+ uint32_t chan,
+ int32_t *val,
+ int32_t *val2)
+{
+ int ret;
+ uint32_t tmp;
+ int32_t sign;
+ uint64_t llval;
+
+ ret = adc_read(phy, ADC_REG_CHAN_CNTRL_2(chan), &tmp);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* format is 1.1.14 (sign, integer and fractional bits) */
+
+ if (!((phase + chan) % 2)) {
+ tmp = ADC_TO_IQCOR_COEFF_1(tmp);
+ } else {
+ tmp = ADC_TO_IQCOR_COEFF_2(tmp);
+ }
+
+ if (tmp & 0x8000)
+ sign = -1;
+ else
+ sign = 1;
+
+ if (tmp & 0x4000)
+ *val = 1 * sign;
+ else
+ *val = 0;
+
+ tmp &= ~0xC000;
+
+ llval = tmp * 1000000ULL + (0x4000 / 2);
+ do_div(&llval, 0x4000);
+ if (*val == 0)
+ *val2 = (int32_t)llval * sign;
+ else
+ *val2 = (int32_t)llval;
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief adc_set_calib_scale
+*******************************************************************************/
+int32_t adc_set_calib_scale(struct ad9361_rf_phy *phy,
+ uint32_t chan,
+ int32_t val,
+ int32_t val2)
+{
+ return adc_set_calib_scale_phase(phy, 0, chan, val, val2);
+}
+
+/***************************************************************************//**
+ * @brief adc_get_calib_scale
+*******************************************************************************/
+int32_t adc_get_calib_scale(struct ad9361_rf_phy *phy,
+ uint32_t chan,
+ int32_t *val,
+ int32_t *val2)
+{
+ return adc_get_calib_scale_phase(phy, 0, chan, val, val2);
+}
+
+/***************************************************************************//**
+ * @brief adc_set_calib_phase
+*******************************************************************************/
+int32_t adc_set_calib_phase(struct ad9361_rf_phy *phy,
+ uint32_t chan,
+ int32_t val,
+ int32_t val2)
+{
+ return adc_set_calib_scale_phase(phy, 1, chan, val, val2);
+}
+
+/***************************************************************************//**
+ * @brief adc_get_calib_phase
+*******************************************************************************/
+int32_t adc_get_calib_phase(struct ad9361_rf_phy *phy,
+ uint32_t chan,
+ int32_t *val,
+ int32_t *val2)
+{
+ return adc_get_calib_scale_phase(phy, 1, chan, val, val2);
+}
diff --git a/Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/adc_core.h b/Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/adc_core.h
new file mode 100644
index 0000000..f03d2b8
--- /dev/null
+++ b/Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/adc_core.h
@@ -0,0 +1,169 @@
+/***************************************************************************//**
+ * @file adc_core.h
+ * @brief Header file of ADC Core Driver.
+ * @author DBogdan (dragos.bogdan@analog.com)
+********************************************************************************
+ * Copyright 2013(c) Analog Devices, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * - Neither the name of Analog Devices, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * - The use of this software may or may not infringe the patent rights
+ * of one or more patent holders. This license does not release you
+ * from the requirement that you obtain separate licenses from these
+ * patent holders to use this software.
+ * - Use of the software either in source or binary form, must be run
+ * on or directly connected to an Analog Devices Inc. component.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*******************************************************************************/
+#ifndef ADC_CORE_API_H_
+#define ADC_CORE_API_H_
+
+/******************************************************************************/
+/***************************** Include Files **********************************/
+/******************************************************************************/
+#include "ad9361.h"
+
+/******************************************************************************/
+/********************** Macros and Constants Definitions **********************/
+/******************************************************************************/
+/* ADC COMMON */
+#define ADC_REG_RSTN 0x0040
+#define ADC_RSTN (1 << 0)
+#define ADC_MMCM_RSTN (1 << 1)
+
+#define ADC_REG_CNTRL 0x0044
+#define ADC_R1_MODE (1 << 2)
+#define ADC_DDR_EDGESEL (1 << 1)
+#define ADC_PIN_MODE (1 << 0)
+
+#define ADC_REG_STATUS 0x005C
+#define ADC_MUX_PN_ERR (1 << 3)
+#define ADC_MUX_PN_OOS (1 << 2)
+#define ADC_MUX_OVER_RANGE (1 << 1)
+#define ADC_STATUS (1 << 0)
+
+#define ADC_REG_DMA_CNTRL 0x0080
+#define ADC_DMA_STREAM (1 << 1)
+#define ADC_DMA_START (1 << 0)
+
+#define ADC_REG_DMA_COUNT 0x0084
+#define ADC_DMA_COUNT(x) (((x) & 0xFFFFFFFF) << 0)
+#define ADC_TO_DMA_COUNT(x) (((x) >> 0) & 0xFFFFFFFF)
+
+#define ADC_REG_DMA_STATUS 0x0088
+#define ADC_DMA_OVF (1 << 2)
+#define ADC_DMA_UNF (1 << 1)
+#define ADC_DMA_STATUS (1 << 0)
+
+#define ADC_REG_DMA_BUSWIDTH 0x008C
+#define ADC_DMA_BUSWIDTH(x) (((x) & 0xFFFFFFFF) << 0)
+#define ADC_TO_DMA_BUSWIDTH(x) (((x) >> 0) & 0xFFFFFFFF)
+
+/* ADC CHANNEL */
+#define ADC_REG_CHAN_CNTRL(c) (0x0400 + (c) * 0x40)
+#define ADC_LB_EN (1 << 11)
+#define ADC_PN_SEL (1 << 10)
+#define ADC_IQCOR_ENB (1 << 9)
+#define ADC_DCFILT_ENB (1 << 8)
+#define ADC_FORMAT_SIGNEXT (1 << 6)
+#define ADC_FORMAT_TYPE (1 << 5)
+#define ADC_FORMAT_ENABLE (1 << 4)
+#define ADC_PN23_TYPE (1 << 1)
+#define ADC_ENABLE (1 << 0)
+
+#define ADC_REG_CHAN_STATUS(c) (0x0404 + (c) * 0x40)
+#define ADC_PN_ERR (1 << 2)
+#define ADC_PN_OOS (1 << 1)
+#define ADC_OVER_RANGE (1 << 0)
+
+#define ADC_REG_CHAN_CNTRL_1(c) (0x0410 + (c) * 0x40)
+#define ADC_DCFILT_OFFSET(x) (((x) & 0xFFFF) << 16)
+#define ADC_TO_DCFILT_OFFSET(x) (((x) >> 16) & 0xFFFF)
+#define ADC_DCFILT_COEFF(x) (((x) & 0xFFFF) << 0)
+#define ADC_TO_DCFILT_COEFF(x) (((x) >> 0) & 0xFFFF)
+
+#define ADC_REG_CHAN_CNTRL_2(c) (0x0414 + (c) * 0x40)
+#define ADC_IQCOR_COEFF_1(x) (((x) & 0xFFFF) << 16)
+#define ADC_TO_IQCOR_COEFF_1(x) (((x) >> 16) & 0xFFFF)
+#define ADC_IQCOR_COEFF_2(x) (((x) & 0xFFFF) << 0)
+#define ADC_TO_IQCOR_COEFF_2(x) (((x) >> 0) & 0xFFFF)
+
+#define ADC_REG_CHAN_CNTRL_3(c) (0x0418 + (c) * 0x40) /* v8.0 */
+#define ADC_ADC_PN_SEL(x) (((x) & 0xF) << 16)
+#define ADC_TO_ADC_PN_SEL(x) (((x) >> 16) & 0xF)
+#define ADC_ADC_DATA_SEL(x) (((x) & 0xF) << 0)
+#define ADC_TO_ADC_DATA_SEL(x) (((x) >> 0) & 0xF)
+
+#define AXI_DMAC_REG_IRQ_MASK 0x80
+#define AXI_DMAC_REG_IRQ_PENDING 0x84
+#define AXI_DMAC_REG_IRQ_SOURCE 0x88
+
+#define AXI_DMAC_REG_CTRL 0x400
+#define AXI_DMAC_REG_TRANSFER_ID 0x404
+#define AXI_DMAC_REG_START_TRANSFER 0x408
+#define AXI_DMAC_REG_FLAGS 0x40c
+#define AXI_DMAC_REG_DEST_ADDRESS 0x410
+#define AXI_DMAC_REG_SRC_ADDRESS 0x414
+#define AXI_DMAC_REG_X_LENGTH 0x418
+#define AXI_DMAC_REG_Y_LENGTH 0x41c
+#define AXI_DMAC_REG_DEST_STRIDE 0x420
+#define AXI_DMAC_REG_SRC_STRIDE 0x424
+#define AXI_DMAC_REG_TRANSFER_DONE 0x428
+#define AXI_DMAC_REG_ACTIVE_TRANSFER_ID 0x42c
+#define AXI_DMAC_REG_STATUS 0x430
+#define AXI_DMAC_REG_CURRENT_DEST_ADDR 0x434
+#define AXI_DMAC_REG_CURRENT_SRC_ADDR 0x438
+#define AXI_DMAC_REG_DBG0 0x43c
+#define AXI_DMAC_REG_DBG1 0x440
+
+#define AXI_DMAC_CTRL_ENABLE (1 << 0)
+#define AXI_DMAC_CTRL_PAUSE (1 << 1)
+
+#define AXI_DMAC_IRQ_SOT (1 << 0)
+#define AXI_DMAC_IRQ_EOT (1 << 1)
+
+/******************************************************************************/
+/************************ Functions Declarations ******************************/
+/******************************************************************************/
+
+int adc_init(struct ad9361_rf_phy *phy);
+int adc_read(struct ad9361_rf_phy *phy, uint32_t regAddr, uint32_t *data);
+int adc_write(struct ad9361_rf_phy *phy, uint32_t regAddr, uint32_t data);
+int32_t adc_set_calib_scale(struct ad9361_rf_phy *phy,
+ uint32_t chan,
+ int32_t val,
+ int32_t val2);
+int32_t adc_get_calib_scale(struct ad9361_rf_phy *phy,
+ uint32_t chan,
+ int32_t *val,
+ int32_t *val2);
+int32_t adc_set_calib_phase(struct ad9361_rf_phy *phy,
+ uint32_t chan,
+ int32_t val,
+ int32_t val2);
+int32_t adc_get_calib_phase(struct ad9361_rf_phy *phy,
+ uint32_t chan,
+ int32_t *val,
+ int32_t *val2);
+#endif
diff --git a/Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/config.h b/Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/config.h
new file mode 100644
index 0000000..179780d
--- /dev/null
+++ b/Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/config.h
@@ -0,0 +1,67 @@
+/***************************************************************************//**
+ * @file config.h
+ * @brief Config file of AD9361/API Driver.
+ * @author DBogdan (dragos.bogdan@analog.com)
+********************************************************************************
+ * Copyright 2015(c) Analog Devices, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * - Neither the name of Analog Devices, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * - The use of this software may or may not infringe the patent rights
+ * of one or more patent holders. This license does not release you
+ * from the requirement that you obtain separate licenses from these
+ * patent holders to use this software.
+ * - Use of the software either in source or binary form, must be run
+ * on or directly connected to an Analog Devices Inc. component.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*******************************************************************************/
+#ifndef ADI_NOOS_CONFIG_H_
+#define ADI_NOOS_CONFIG_H_
+
+#define HAVE_VERBOSE_MESSAGES /* Recommended during development prints errors and warnings */
+//#define HAVE_DEBUG_MESSAGES /* For Debug purposes only */
+
+/*
+ * In case memory footprint is a concern these options allow
+ * to disable unused functionality which may free up a few kb
+ */
+
+#define HAVE_SPLIT_GAIN_TABLE 1 /* only set to 0 in case split_gain_table_mode_enable = 0*/
+#define HAVE_TDD_SYNTH_TABLE 1 /* only set to 0 in case split_gain_table_mode_enable = 0*/
+
+#define AD9361_DEVICE 1 /* set it 1 if AD9361 device is used, 0 otherwise */
+#define AD9364_DEVICE 0 /* set it 1 if AD9364 device is used, 0 otherwise */
+#define AD9363A_DEVICE 0 /* set it 1 if AD9363A device is used, 0 otherwise */
+
+//#define CONSOLE_COMMANDS
+//#define XILINX_PLATFORM
+#define ALTERA_PLATFORM
+//#define FMCOMMS5
+//#define PICOZED_SDR
+//#define PICOZED_SDR_CMOS
+//#define CAPTURE_SCRIPT
+//#define AXI_ADC_NOT_PRESENT
+//#define TDD_SWITCH_STATE_EXAMPLE
+
+#endif
diff --git a/Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/dac_core.c b/Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/dac_core.c
new file mode 100644
index 0000000..a103ffe
--- /dev/null
+++ b/Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/dac_core.c
@@ -0,0 +1,717 @@
+/***************************************************************************//**
+ * @file dac_core.c
+ * @brief Implementation of DAC Core Driver.
+ * @author DBogdan (dragos.bogdan@analog.com)
+********************************************************************************
+ * Copyright 2013(c) Analog Devices, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * - Neither the name of Analog Devices, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * - The use of this software may or may not infringe the patent rights
+ * of one or more patent holders. This license does not release you
+ * from the requirement that you obtain separate licenses from these
+ * patent holders to use this software.
+ * - Use of the software either in source or binary form, must be run
+ * on or directly connected to an Analog Devices Inc. component.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*******************************************************************************/
+
+/******************************************************************************/
+/***************************** Include Files **********************************/
+/******************************************************************************/
+
+#include <stdint.h>
+
+#include "dac_core.h"
+#include "platform.h"
+#include "util.h"
+
+/******************************************************************************/
+/********************** Macros and Constants Definitions **********************/
+/******************************************************************************/
+const uint16_t sine_lut[32] = {
+ 0x000, 0x031, 0x061, 0x08D, 0x0B4, 0x0D4, 0x0EC, 0x0FA,
+ 0x0FF, 0x0FA, 0x0EC, 0x0D4, 0x0B4, 0x08D, 0x061, 0x031,
+ 0x000, 0xFCE, 0xF9E, 0xF72, 0xF4B, 0xF2B, 0xF13, 0xF05,
+ 0xF00, 0xF05, 0xF13, 0xF2B, 0xF4B, 0xF72, 0xF9E, 0xFCE
+};
+
+/***************************************************************************//**
+ * @brief dac_read
+*******************************************************************************/
+int dac_read(struct ad9361_rf_phy *phy, uint32_t regAddr, uint32_t *data)
+{
+ return axiadc_read(phy->adc_state, regAddr + 0x4000, data);
+}
+
+/***************************************************************************//**
+ * @brief dac_write
+*******************************************************************************/
+int dac_write(struct ad9361_rf_phy *phy, uint32_t regAddr, uint32_t data)
+{
+ return axiadc_write(phy->adc_state, regAddr + 0x4000, data);
+}
+
+/***************************************************************************//**
+ * @brief dds_default_setup
+*******************************************************************************/
+static int dds_default_setup(struct ad9361_rf_phy *phy,
+ uint32_t chan, uint32_t phase,
+ uint32_t freq, int32_t scale)
+{
+ struct dds_state *dds_st = &phy->adc_state->dac_dds_state;
+ int ret;
+
+ ret = dds_set_phase(phy, chan, phase);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = dds_set_frequency(phy, chan, freq);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = dds_set_scale(phy, chan, scale);
+ if (ret < 0) {
+ return ret;
+ }
+
+ dds_st->cached_freq[chan] = freq;
+ dds_st->cached_phase[chan] = phase;
+ dds_st->cached_scale[chan] = scale;
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief dac_stop
+*******************************************************************************/
+int dac_stop(struct ad9361_rf_phy *phy)
+{
+ struct dds_state *dds_st = &phy->adc_state->dac_dds_state;
+
+ if (PCORE_VERSION_MAJOR(dds_st->pcore_version) < 8)
+ {
+ return dac_write(phy, DAC_REG_CNTRL_1, 0);
+ }
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief dac_start_sync
+*******************************************************************************/
+int dac_start_sync(struct ad9361_rf_phy *phy, bool force_on)
+{
+ struct dds_state *dds_st = &phy->adc_state->dac_dds_state;
+
+ if (PCORE_VERSION_MAJOR(dds_st->pcore_version) < 8)
+ {
+ return dac_write(phy, DAC_REG_CNTRL_1, (dds_st->enable || force_on) ? DAC_ENABLE : 0);
+ }
+ else
+ {
+ return dac_write(phy, DAC_REG_CNTRL_1, DAC_SYNC);
+ }
+}
+
+/***************************************************************************//**
+ * @brief dac_init
+*******************************************************************************/
+int dac_init(struct ad9361_rf_phy *phy, uint8_t data_sel, uint8_t config_dma)
+{
+ struct dds_state *dds_st;
+ int ret;
+ uint32_t reg_ctrl_2;
+
+ dds_st = &phy->adc_state->dac_dds_state;
+
+ ret = dac_write(phy, DAC_REG_RSTN, 0x0);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = dac_write(phy, DAC_REG_RSTN, DAC_RSTN);
+ if (ret < 0) {
+ return ret;
+ }
+
+ dds_st->dac_clk = &phy->clks[TX_SAMPL_CLK]->rate;
+ dds_st->rx2tx2 = phy->pdata->rx2tx2;
+
+ ret = dac_read(phy, DAC_REG_CNTRL_2, &reg_ctrl_2);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if(dds_st->rx2tx2)
+ {
+ dds_st->num_dds_channels = 8;
+
+ if(phy->pdata->port_ctrl.pp_conf[2] & LVDS_MODE)
+ ret = dac_write(phy, DAC_REG_RATECNTRL, DAC_RATE(3));
+ else
+ ret = dac_write(phy, DAC_REG_RATECNTRL, DAC_RATE(1));
+
+ if (ret < 0) {
+ return ret;
+ }
+
+ reg_ctrl_2 &= ~DAC_R1_MODE;
+ }
+ else
+ {
+ dds_st->num_dds_channels = 4;
+
+ if(phy->pdata->port_ctrl.pp_conf[2] & LVDS_MODE)
+ ret = dac_write(phy, DAC_REG_RATECNTRL, DAC_RATE(1));
+ else
+ ret = dac_write(phy, DAC_REG_RATECNTRL, DAC_RATE(0));
+
+ if (ret < 0) {
+ return ret;
+ }
+
+ reg_ctrl_2 |= DAC_R1_MODE;
+ }
+
+ ret = dac_write(phy, DAC_REG_CNTRL_2, reg_ctrl_2);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = dac_read(phy, DAC_REG_VERSION, &dds_st->pcore_version);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = dac_write(phy, DAC_REG_CNTRL_1, 0);
+ if (ret < 0) {
+ return ret;
+ }
+
+ switch (data_sel) {
+ case DATA_SEL_DDS:
+ ret = dds_default_setup(phy, DDS_CHAN_TX1_I_F1, 90000, 1000000, 250000);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = dds_default_setup(phy, DDS_CHAN_TX1_I_F2, 90000, 1000000, 250000);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = dds_default_setup(phy, DDS_CHAN_TX1_Q_F1, 0, 1000000, 250000);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = dds_default_setup(phy, DDS_CHAN_TX1_Q_F2, 0, 1000000, 250000);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if(dds_st->rx2tx2)
+ {
+ ret = dds_default_setup(phy, DDS_CHAN_TX2_I_F1, 90000, 1000000, 250000);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = dds_default_setup(phy, DDS_CHAN_TX2_I_F2, 90000, 1000000, 250000);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = dds_default_setup(phy, DDS_CHAN_TX2_Q_F1, 0, 1000000, 250000);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = dds_default_setup(phy, DDS_CHAN_TX2_Q_F2, 0, 1000000, 250000);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ ret = dac_datasel(phy, -1, DATA_SEL_DDS);
+ if (ret < 0) {
+ return ret;
+ }
+
+ break;
+ case DATA_SEL_DMA:
+ ret = dac_datasel(phy, -1, DATA_SEL_DMA);
+ if (ret < 0) {
+ return ret;
+ }
+ break;
+ default:
+ break;
+ }
+
+ dds_st->enable = true;
+
+ ret = dac_start_sync(phy, 0);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief dds_set_frequency
+*******************************************************************************/
+int dds_set_frequency(struct ad9361_rf_phy *phy, uint32_t chan, uint32_t freq)
+{
+ struct dds_state *dds_st = &phy->adc_state->dac_dds_state;
+ int ret;
+ uint64_t val64;
+ uint32_t reg;
+
+ dds_st->cached_freq[chan] = freq;
+
+ ret = dac_stop(phy);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = dac_read(phy, DAC_REG_CHAN_CNTRL_2_IIOCHAN(chan), &reg);
+ if (ret < 0) {
+ return ret;
+ }
+
+ reg &= ~DAC_DDS_INCR(~0);
+ val64 = (uint64_t) freq * 0xFFFFULL;
+ do_div(&val64, *dds_st->dac_clk);
+ reg |= DAC_DDS_INCR(val64) | 1;
+
+ ret = dac_write(phy, DAC_REG_CHAN_CNTRL_2_IIOCHAN(chan), reg);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = dac_start_sync(phy, 0);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief dds_set_phase
+*******************************************************************************/
+int dds_set_phase(struct ad9361_rf_phy *phy, uint32_t chan, uint32_t phase)
+{
+ struct dds_state *dds_st = &phy->adc_state->dac_dds_state;
+ int ret;
+ uint64_t val64;
+ uint32_t reg;
+
+ dds_st->cached_phase[chan] = phase;
+
+ ret = dac_stop(phy);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = dac_read(phy, DAC_REG_CHAN_CNTRL_2_IIOCHAN(chan), &reg);
+ if (ret < 0) {
+ return ret;
+ }
+
+ reg &= ~DAC_DDS_INIT(~0);
+ val64 = (uint64_t) phase * 0x10000ULL + (360000 / 2);
+ do_div(&val64, 360000);
+ reg |= DAC_DDS_INIT(val64);
+
+ ret = dac_write(phy, DAC_REG_CHAN_CNTRL_2_IIOCHAN(chan), reg);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = dac_start_sync(phy, 0);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief dds_set_phase
+*******************************************************************************/
+int dds_set_scale(struct ad9361_rf_phy *phy, uint32_t chan, int32_t scale_micro_units)
+{
+ struct dds_state *dds_st = &phy->adc_state->dac_dds_state;
+ int ret;
+ uint32_t scale_reg;
+ uint32_t sign_part;
+ uint32_t int_part;
+ uint32_t fract_part;
+
+ if (PCORE_VERSION_MAJOR(dds_st->pcore_version) > 6)
+ {
+ if(scale_micro_units >= 1000000)
+ {
+ sign_part = 0;
+ int_part = 1;
+ fract_part = 0;
+ dds_st->cached_scale[chan] = 1000000;
+ goto set_scale_reg;
+ }
+ if(scale_micro_units <= -1000000)
+ {
+ sign_part = 1;
+ int_part = 1;
+ fract_part = 0;
+ dds_st->cached_scale[chan] = -1000000;
+ goto set_scale_reg;
+ }
+ dds_st->cached_scale[chan] = scale_micro_units;
+ if(scale_micro_units < 0)
+ {
+ sign_part = 1;
+ int_part = 0;
+ scale_micro_units *= -1;
+ }
+ else
+ {
+ sign_part = 0;
+ int_part = 0;
+ }
+ fract_part = (uint32_t)(((uint64_t)scale_micro_units * 0x4000) / 1000000);
+ set_scale_reg:
+ scale_reg = (sign_part << 15) | (int_part << 14) | fract_part;
+ }
+ else
+ {
+ if(scale_micro_units >= 1000000)
+ {
+ scale_reg = 0;
+ scale_micro_units = 1000000;
+ }
+ if(scale_micro_units <= 0)
+ {
+ scale_reg = 0;
+ scale_micro_units = 0;
+ }
+ dds_st->cached_scale[chan] = scale_micro_units;
+ fract_part = (uint32_t)(scale_micro_units);
+ scale_reg = 500000 / fract_part;
+ }
+
+ ret = dac_stop(phy);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = dac_write(phy, DAC_REG_CHAN_CNTRL_1_IIOCHAN(chan), DAC_DDS_SCALE(scale_reg));
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = dac_start_sync(phy, 0);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief dds_update
+*******************************************************************************/
+int dds_update(struct ad9361_rf_phy *phy)
+{
+ struct dds_state *dds_st = &phy->adc_state->dac_dds_state;
+ int ret;
+ uint32_t chan;
+
+ for(chan = DDS_CHAN_TX1_I_F1; chan <= DDS_CHAN_TX2_Q_F2; chan++)
+ {
+ ret = dds_set_frequency(phy, chan, dds_st->cached_freq[chan]);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = dds_set_phase(phy, chan, dds_st->cached_phase[chan]);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = dds_set_scale(phy, chan, dds_st->cached_scale[chan]);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief dac_datasel
+*******************************************************************************/
+int dac_datasel(struct ad9361_rf_phy *phy, int32_t chan, enum dds_data_select sel)
+{
+ struct dds_state *dds_st = &phy->adc_state->dac_dds_state;
+ int ret;
+
+ if (PCORE_VERSION_MAJOR(dds_st->pcore_version) > 7) {
+ if (chan < 0) { /* ALL */
+ uint32_t i;
+ for (i = 0; i < dds_st->num_dds_channels; i++) {
+ ret = dac_write(phy, DAC_REG_CHAN_CNTRL_7(i), sel);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ } else {
+ ret = dac_write(phy, DAC_REG_CHAN_CNTRL_7(chan), sel);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ } else {
+ uint32_t reg;
+
+ switch(sel) {
+ case DATA_SEL_DDS:
+ case DATA_SEL_SED:
+ case DATA_SEL_DMA:
+ ret = dac_read(phy, DAC_REG_CNTRL_2, &reg);
+ if (ret < 0) {
+ return ret;
+ }
+
+ reg &= ~DAC_DATA_SEL(~0);
+ reg |= DAC_DATA_SEL(sel);
+
+ ret = dac_write(phy, DAC_REG_CNTRL_2, reg);
+ if (ret < 0) {
+ return ret;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief dds_to_signed_mag_fmt
+*******************************************************************************/
+uint32_t dds_to_signed_mag_fmt(int32_t val, int32_t val2)
+{
+ uint32_t i;
+ uint64_t val64;
+
+ /* format is 1.1.14 (sign, integer and fractional bits) */
+
+ switch (val) {
+ case 1:
+ i = 0x4000;
+ break;
+ case -1:
+ i = 0xC000;
+ break;
+ case 0:
+ i = 0;
+ if (val2 < 0) {
+ i = 0x8000;
+ val2 *= -1;
+ }
+ break;
+ default:
+ /* Invalid Value */
+ i = 0;
+ }
+
+ val64 = (uint64_t)val2 * 0x4000UL + (1000000UL / 2);
+ do_div(&val64, 1000000UL);
+
+ return i | (uint32_t)val64;
+}
+
+/***************************************************************************//**
+ * @brief dds_from_signed_mag_fmt
+*******************************************************************************/
+void dds_from_signed_mag_fmt(uint32_t val,
+ int32_t *r_val,
+ int32_t *r_val2)
+{
+ uint64_t val64;
+ int32_t sign;
+
+ if (val & 0x8000)
+ sign = -1;
+ else
+ sign = 1;
+
+ if (val & 0x4000)
+ *r_val = 1 * sign;
+ else
+ *r_val = 0;
+
+ val &= ~0xC000;
+
+ val64 = val * 1000000ULL + (0x4000 / 2);
+ do_div(&val64, 0x4000);
+
+ if (*r_val == 0)
+ *r_val2 = (uint32_t)val64 * sign;
+ else
+ *r_val2 = (uint32_t)val64;
+}
+
+/***************************************************************************//**
+ * @brief dds_set_calib_scale_phase
+*******************************************************************************/
+int32_t dds_set_calib_scale_phase(struct ad9361_rf_phy *phy,
+ uint32_t phase,
+ uint32_t chan,
+ int32_t val,
+ int32_t val2)
+{
+ struct dds_state *dds_st = &phy->adc_state->dac_dds_state;
+ int ret;
+ uint32_t reg;
+ uint32_t i;
+
+ if (PCORE_VERSION_MAJOR(dds_st->pcore_version) < 8) {
+ return -1;
+ }
+
+ i = dds_to_signed_mag_fmt(val, val2);
+
+ ret = dac_read(phy, DAC_REG_CHAN_CNTRL_8(chan), &reg);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (!((chan + phase) % 2)) {
+ reg &= ~DAC_IQCOR_COEFF_1(~0);
+ reg |= DAC_IQCOR_COEFF_1(i);
+ } else {
+ reg &= ~DAC_IQCOR_COEFF_2(~0);
+ reg |= DAC_IQCOR_COEFF_2(i);
+ }
+
+ ret = dac_write(phy, DAC_REG_CHAN_CNTRL_8(chan), reg);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = dac_write(phy, DAC_REG_CHAN_CNTRL_6(chan), DAC_IQCOR_ENB);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief dds_get_calib_scale_phase
+*******************************************************************************/
+int32_t dds_get_calib_scale_phase(struct ad9361_rf_phy *phy,
+ uint32_t phase,
+ uint32_t chan,
+ int32_t *val,
+ int32_t *val2)
+{
+ struct dds_state *dds_st = &phy->adc_state->dac_dds_state;
+ int ret;
+ uint32_t reg;
+
+ if (PCORE_VERSION_MAJOR(dds_st->pcore_version) < 8) {
+ return -1;
+ }
+
+ ret = dac_read(phy, DAC_REG_CHAN_CNTRL_8(chan), &reg);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* format is 1.1.14 (sign, integer and fractional bits) */
+
+ if (!((phase + chan) % 2)) {
+ reg = DAC_TO_IQCOR_COEFF_1(reg);
+ } else {
+ reg = DAC_TO_IQCOR_COEFF_2(reg);
+ }
+
+ dds_from_signed_mag_fmt(reg, val, val2);
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief dds_set_calib_scale
+*******************************************************************************/
+int32_t dds_set_calib_scale(struct ad9361_rf_phy *phy,
+ uint32_t chan,
+ int32_t val,
+ int32_t val2)
+{
+ return dds_set_calib_scale_phase(phy, 0, chan, val, val2);
+}
+
+/***************************************************************************//**
+ * @brief dds_get_calib_scale
+*******************************************************************************/
+int32_t dds_get_calib_scale(struct ad9361_rf_phy *phy,
+ uint32_t chan,
+ int32_t *val,
+ int32_t *val2)
+{
+ return dds_get_calib_scale_phase(phy, 0, chan, val, val2);
+}
+
+/***************************************************************************//**
+ * @brief dds_set_calib_phase
+*******************************************************************************/
+int32_t dds_set_calib_phase(struct ad9361_rf_phy *phy,
+ uint32_t chan,
+ int32_t val,
+ int32_t val2)
+{
+ return dds_set_calib_scale_phase(phy, 1, chan, val, val2);
+}
+
+/***************************************************************************//**
+ * @brief dds_get_calib_phase
+*******************************************************************************/
+int32_t dds_get_calib_phase(struct ad9361_rf_phy *phy,
+ uint32_t chan,
+ int32_t *val,
+ int32_t *val2)
+{
+ return dds_get_calib_scale_phase(phy, 1, chan, val, val2);
+}
diff --git a/Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/dac_core.h b/Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/dac_core.h
new file mode 100644
index 0000000..46b516a
--- /dev/null
+++ b/Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/dac_core.h
@@ -0,0 +1,199 @@
+/***************************************************************************//**
+ * @file dac_core.h
+ * @brief Header file of DAC Core Driver.
+ * @author DBogdan (dragos.bogdan@analog.com)
+********************************************************************************
+ * Copyright 2013(c) Analog Devices, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * - Neither the name of Analog Devices, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * - The use of this software may or may not infringe the patent rights
+ * of one or more patent holders. This license does not release you
+ * from the requirement that you obtain separate licenses from these
+ * patent holders to use this software.
+ * - Use of the software either in source or binary form, must be run
+ * on or directly connected to an Analog Devices Inc. component.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*******************************************************************************/
+#ifndef DAC_CORE_API_H_
+#define DAC_CORE_API_H_
+
+/******************************************************************************/
+/***************************** Include Files **********************************/
+/******************************************************************************/
+#include "ad9361.h"
+
+/******************************************************************************/
+/********************** Macros and Constants Definitions **********************/
+/******************************************************************************/
+#define DAC_REG_VERSION 0x0000
+#define DAC_VERSION(x) (((x) & 0xffffffff) << 0)
+#define VERSION_IS(x,y,z) ((x) << 16 | (y) << 8 | (z))
+#define DAC_REG_ID 0x0004
+#define DAC_ID(x) (((x) & 0xffffffff) << 0)
+#define DAC_REG_SCRATCH 0x0008
+#define DAC_SCRATCH(x) (((x) & 0xffffffff) << 0)
+
+#define PCORE_VERSION_MAJOR(version) (version >> 16)
+
+#define DAC_REG_RSTN 0x0040
+#define DAC_RSTN (1 << 0)
+#define DAC_MMCM_RSTN (1 << 1)
+
+#define DAC_REG_RATECNTRL 0x004C
+#define DAC_RATE(x) (((x) & 0xFF) << 0)
+#define DAC_TO_RATE(x) (((x) >> 0) & 0xFF)
+
+#define DAC_REG_CNTRL_1 0x0044
+#define DAC_ENABLE (1 << 0) /* v7.0 */
+#define DAC_SYNC (1 << 0) /* v8.0 */
+
+#define DAC_REG_CNTRL_2 0x0048
+#define DAC_PAR_TYPE (1 << 7)
+#define DAC_PAR_ENB (1 << 6)
+#define DAC_R1_MODE (1 << 5)
+#define DAC_DATA_FORMAT (1 << 4)
+#define DAC_DATA_SEL(x) (((x) & 0xF) << 0) /* v7.0 */
+#define DAC_TO_DATA_SEL(x) (((x) >> 0) & 0xF) /* v7.0 */
+
+#define DAC_REG_VDMA_FRMCNT 0x0084
+#define DAC_VDMA_FRMCNT(x) (((x) & 0xFFFFFFFF) << 0)
+#define DAC_TO_VDMA_FRMCNT(x) (((x) >> 0) & 0xFFFFFFFF)
+
+#define DAC_REG_VDMA_STATUS 0x0088
+#define DAC_VDMA_OVF (1 << 1)
+#define DAC_VDMA_UNF (1 << 0)
+
+enum dds_data_select {
+ DATA_SEL_DDS,
+ DATA_SEL_SED,
+ DATA_SEL_DMA,
+ DATA_SEL_ZERO, /* OUTPUT 0 */
+ DATA_SEL_PN7,
+ DATA_SEL_PN15,
+ DATA_SEL_PN23,
+ DATA_SEL_PN31,
+ DATA_SEL_LB, /* loopback data (ADC) */
+ DATA_SEL_PNXX, /* (Device specific) */
+};
+
+#define DAC_REG_CHAN_CNTRL_1_IIOCHAN(x) (0x0400 + ((x) >> 1) * 0x40 + ((x) & 1) * 0x8)
+#define DAC_DDS_SCALE(x) (((x) & 0xFFFF) << 0)
+#define DAC_TO_DDS_SCALE(x) (((x) >> 0) & 0xFFFF)
+
+#define DAC_REG_CHAN_CNTRL_2_IIOCHAN(x) (0x0404 + ((x) >> 1) * 0x40 + ((x) & 1) * 0x8)
+#define DAC_DDS_INIT(x) (((x) & 0xFFFF) << 16)
+#define DAC_TO_DDS_INIT(x) (((x) >> 16) & 0xFFFF)
+#define DAC_DDS_INCR(x) (((x) & 0xFFFF) << 0)
+#define DAC_TO_DDS_INCR(x) (((x) >> 0) & 0xFFFF)
+
+#define DDS_CHAN_TX1_I_F1 0
+#define DDS_CHAN_TX1_I_F2 1
+#define DDS_CHAN_TX1_Q_F1 2
+#define DDS_CHAN_TX1_Q_F2 3
+#define DDS_CHAN_TX2_I_F1 4
+#define DDS_CHAN_TX2_I_F2 5
+#define DDS_CHAN_TX2_Q_F1 6
+#define DDS_CHAN_TX2_Q_F2 7
+
+#define AXI_DMAC_REG_IRQ_MASK 0x80
+#define AXI_DMAC_REG_IRQ_PENDING 0x84
+#define AXI_DMAC_REG_IRQ_SOURCE 0x88
+
+#define AXI_DMAC_REG_CTRL 0x400
+#define AXI_DMAC_REG_TRANSFER_ID 0x404
+#define AXI_DMAC_REG_START_TRANSFER 0x408
+#define AXI_DMAC_REG_FLAGS 0x40c
+#define AXI_DMAC_REG_DEST_ADDRESS 0x410
+#define AXI_DMAC_REG_SRC_ADDRESS 0x414
+#define AXI_DMAC_REG_X_LENGTH 0x418
+#define AXI_DMAC_REG_Y_LENGTH 0x41c
+#define AXI_DMAC_REG_DEST_STRIDE 0x420
+#define AXI_DMAC_REG_SRC_STRIDE 0x424
+#define AXI_DMAC_REG_TRANSFER_DONE 0x428
+#define AXI_DMAC_REG_ACTIVE_TRANSFER_ID 0x42c
+#define AXI_DMAC_REG_STATUS 0x430
+#define AXI_DMAC_REG_CURRENT_DEST_ADDR 0x434
+#define AXI_DMAC_REG_CURRENT_SRC_ADDR 0x438
+#define AXI_DMAC_REG_DBG0 0x43c
+#define AXI_DMAC_REG_DBG1 0x440
+
+#define AXI_DMAC_CTRL_ENABLE (1 << 0)
+#define AXI_DMAC_CTRL_PAUSE (1 << 1)
+
+#define AXI_DMAC_IRQ_SOT (1 << 0)
+#define AXI_DMAC_IRQ_EOT (1 << 1)
+
+struct dds_state
+{
+ uint32_t cached_freq[8];
+ uint32_t cached_phase[8];
+ int32_t cached_scale[8];
+ uint32_t *dac_clk;
+ uint32_t pcore_version;
+ uint32_t num_dds_channels;
+ bool enable;
+ bool rx2tx2;
+};
+
+#define DAC_REG_CHAN_CNTRL_6(c) (0x0414 + (c) * 0x40)
+#define DAC_IQCOR_ENB (1 << 2) /* v8.0 */
+
+#define DAC_REG_CHAN_CNTRL_7(c) (0x0418 + (c) * 0x40) /* v8.0 */
+#define DAC_DAC_DDS_SEL(x) (((x) & 0xF) << 0)
+#define DAC_TO_DAC_DDS_SEL(x) (((x) >> 0) & 0xF)
+
+#define DAC_REG_CHAN_CNTRL_8(c) (0x041C + (c) * 0x40) /* v8.0 */
+#define DAC_IQCOR_COEFF_1(x) (((x) & 0xFFFF) << 16)
+#define DAC_TO_IQCOR_COEFF_1(x) (((x) >> 16) & 0xFFFF)
+#define DAC_IQCOR_COEFF_2(x) (((x) & 0xFFFF) << 0)
+#define DAC_TO_IQCOR_COEFF_2(x) (((x) >> 0) & 0xFFFF)
+
+/******************************************************************************/
+/************************ Functions Declarations ******************************/
+/******************************************************************************/
+
+int dac_init(struct ad9361_rf_phy *phy, uint8_t data_sel, uint8_t config_dma);
+int dds_set_frequency(struct ad9361_rf_phy *phy, uint32_t chan, uint32_t freq);
+int dds_set_phase(struct ad9361_rf_phy *phy, uint32_t chan, uint32_t phase);
+int dds_set_scale(struct ad9361_rf_phy *phy, uint32_t chan, int32_t scale_micro_units);
+int dds_update(struct ad9361_rf_phy *phy);
+int dac_datasel(struct ad9361_rf_phy *phy, int32_t chan, enum dds_data_select sel);
+int32_t dds_set_calib_scale(struct ad9361_rf_phy *phy,
+ uint32_t chan,
+ int32_t val,
+ int32_t val2);
+int32_t dds_get_calib_scale(struct ad9361_rf_phy *phy,
+ uint32_t chan,
+ int32_t *val,
+ int32_t *val2);
+int32_t dds_set_calib_phase(struct ad9361_rf_phy *phy,
+ uint32_t chan,
+ int32_t val,
+ int32_t val2);
+int32_t dds_get_calib_phase(struct ad9361_rf_phy *phy,
+ uint32_t chan,
+ int32_t *val,
+ int32_t *val2);
+#endif
diff --git a/Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/platform.c b/Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/platform.c
new file mode 100644
index 0000000..3d09c9f
--- /dev/null
+++ b/Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/platform.c
@@ -0,0 +1,295 @@
+#include <stdint.h>
+
+#include "board/board.h"
+
+#include "platform.h"
+
+#include "adc_core.h"
+#include "dac_core.h"
+
+/***************************************************************************//**
+ * @brief spi_init
+*******************************************************************************/
+
+int spi_init(struct ad9361_rf_phy *phy, void *userdata)
+{
+ phy->spi->userdata = userdata;
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief spi_write
+*******************************************************************************/
+
+int spi_write(struct spi_device *spi, uint16_t cmd, const uint8_t *buf,
+ unsigned int len)
+{
+ struct bladerf *dev = spi->userdata;
+ int status;
+ uint64_t data;
+ unsigned int i;
+
+ /* Copy buf to data */
+ data = 0;
+ for (i = 0; i < len; i++) {
+ data |= (((uint64_t)buf[i]) << 8*(7-i));
+ }
+
+ /* SPI transaction */
+ status = dev->backend->ad9361_spi_write(dev, cmd, data);
+ if (status < 0) {
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief spi_read
+*******************************************************************************/
+
+#include <inttypes.h>
+
+int spi_read(struct spi_device *spi, uint16_t cmd, uint8_t *buf,
+ unsigned int len)
+{
+ struct bladerf *dev = spi->userdata;
+ int status;
+ uint64_t data = 0;
+ unsigned int i;
+
+ /* SPI transaction */
+ status = dev->backend->ad9361_spi_read(dev, cmd, &data);
+ if (status < 0) {
+ return -EIO;
+ }
+
+ /* Copy data to buf */
+ for (i = 0; i < len; i++) {
+ buf[i] = (data >> 8*(7-i)) & 0xff;
+ }
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief gpio_init
+*******************************************************************************/
+
+int gpio_init(struct ad9361_rf_phy *phy, void *userdata)
+{
+ phy->gpio->userdata = userdata;
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief gpio_is_valid
+*******************************************************************************/
+
+bool gpio_is_valid(struct gpio_device *gpio, int32_t number)
+{
+ if (number == RFFE_CONTROL_RESET_N) {
+ return true;
+ } else if (number == RFFE_CONTROL_SYNC_IN) {
+ return true;
+ }
+
+ return false;
+}
+
+/***************************************************************************//**
+ * @brief gpio_set_value
+*******************************************************************************/
+
+int gpio_set_value(struct gpio_device *gpio, int32_t number, bool value)
+{
+ struct bladerf *dev = gpio->userdata;
+ int status;
+ uint32_t reg;
+
+ /* Read */
+ status = dev->backend->rffe_control_read(dev, &reg);
+ if (status < 0) {
+ return -EIO;
+ }
+
+ /* Modify */
+ if (value) {
+ reg |= (1 << number);
+ } else {
+ reg &= ~(1 << number);
+ }
+
+ /* Write */
+ status = dev->backend->rffe_control_write(dev, reg);
+ if (status < 0) {
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief udelay
+*******************************************************************************/
+
+void udelay(unsigned long usecs)
+{
+ usleep(usecs);
+}
+
+/***************************************************************************//**
+ * @brief mdelay
+*******************************************************************************/
+
+void mdelay(unsigned long msecs)
+{
+ usleep(msecs * 1000);
+}
+
+/***************************************************************************//**
+ * @brief msleep_interruptible
+*******************************************************************************/
+
+unsigned long msleep_interruptible(unsigned int msecs)
+{
+ usleep(msecs * 1000);
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief axiadc_init
+*******************************************************************************/
+
+int axiadc_init(struct ad9361_rf_phy *phy, void *userdata)
+{
+ int status;
+
+ phy->adc_state->userdata = userdata;
+
+ status = adc_init(phy);
+ if (status < 0) {
+ return status;
+ }
+
+ status = dac_init(phy, DATA_SEL_DMA, 0);
+ if (status < 0) {
+ return status;
+ }
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief axiadc_post_setup
+*******************************************************************************/
+
+int axiadc_post_setup(struct ad9361_rf_phy *phy)
+{
+ return ad9361_post_setup(phy);
+}
+
+/***************************************************************************//**
+ * @brief axiadc_read
+*******************************************************************************/
+
+int axiadc_read(struct axiadc_state *st, uint32_t addr, uint32_t *data)
+{
+ struct bladerf *dev = st->userdata;
+ int status;
+
+ /* Read */
+ status = dev->backend->adi_axi_read(dev, addr, data);
+ if (status < 0) {
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief axiadc_write
+*******************************************************************************/
+
+int axiadc_write(struct axiadc_state *st, uint32_t addr, uint32_t data)
+{
+ struct bladerf *dev = st->userdata;
+ int status;
+
+ /* Write */
+ status = dev->backend->adi_axi_write(dev, addr, data);
+ if (status < 0) {
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief axiadc_set_pnsel
+*******************************************************************************/
+
+int axiadc_set_pnsel(struct axiadc_state *st, unsigned int channel, enum adc_pn_sel sel)
+{
+ int status;
+ uint32_t reg;
+
+ if (PCORE_VERSION_MAJOR(st->pcore_version) > 7) {
+ status = axiadc_read(st, ADI_REG_CHAN_CNTRL_3(channel), &reg);
+ if (status != 0)
+ return status;
+
+ reg &= ~ADI_ADC_PN_SEL(~0);
+ reg |= ADI_ADC_PN_SEL(sel);
+
+ status = axiadc_write(st, ADI_REG_CHAN_CNTRL_3(channel), reg);
+ if (status != 0)
+ return status;
+ } else {
+ status = axiadc_read(st, ADI_REG_CHAN_CNTRL(channel), &reg);
+ if (status != 0)
+ return status;
+
+ if (sel == ADC_PN_CUSTOM) {
+ reg |= ADI_PN_SEL;
+ } else if (sel == ADC_PN9) {
+ reg &= ~ADI_PN23_TYPE;
+ reg &= ~ADI_PN_SEL;
+ } else {
+ reg |= ADI_PN23_TYPE;
+ reg &= ~ADI_PN_SEL;
+ }
+
+ status = axiadc_write(st, ADI_REG_CHAN_CNTRL(channel), reg);
+ if (status != 0)
+ return status;
+ }
+
+ return 0;
+}
+
+/***************************************************************************//**
+ * @brief axiadc_idelay_set
+*******************************************************************************/
+
+int axiadc_idelay_set(struct axiadc_state *st, unsigned int lane, unsigned int val)
+{
+ int status;
+
+ if (PCORE_VERSION_MAJOR(st->pcore_version) > 8) {
+ status = axiadc_write(st, ADI_REG_DELAY(lane), val);
+ if (status != 0)
+ return status;
+ } else {
+ status = axiadc_write(st, ADI_REG_DELAY_CNTRL, 0);
+ if (status != 0)
+ return status;
+
+ status = axiadc_write(st, ADI_REG_DELAY_CNTRL,
+ ADI_DELAY_ADDRESS(lane) | ADI_DELAY_WDATA(val) | ADI_DELAY_SEL);
+ if (status != 0)
+ return status;
+ }
+
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/platform.h b/Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/platform.h
new file mode 100644
index 0000000..badbc65
--- /dev/null
+++ b/Radio/HW/BladeRF/thirdparty/analogdevicesinc/no-OS_local/platform_bladerf2/platform.h
@@ -0,0 +1,169 @@
+/***************************************************************************//**
+ * @file platform.h
+ * @brief Header file of Platform driver.
+ * @author DBogdan (dragos.bogdan@analog.com)
+********************************************************************************
+ * Copyright 2014(c) Analog Devices, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * - Neither the name of Analog Devices, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * - The use of this software may or may not infringe the patent rights
+ * of one or more patent holders. This license does not release you
+ * from the requirement that you obtain separate licenses from these
+ * patent holders to use this software.
+ * - Use of the software either in source or binary form, must be run
+ * on or directly connected to an Analog Devices Inc. component.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*******************************************************************************/
+#ifndef PLATFORM_H_
+#define PLATFORM_H_
+
+/******************************************************************************/
+/***************************** Include Files **********************************/
+/******************************************************************************/
+
+#include "stdint.h"
+#include "util.h"
+#include "config.h"
+
+/******************************************************************************/
+/********************** Macros and Constants Definitions **********************/
+/******************************************************************************/
+#define ADI_REG_VERSION 0x0000
+
+#define ADI_REG_ID 0x0004
+
+#define ADI_REG_RSTN 0x0040
+#define ADI_RSTN (1 << 0)
+#define ADI_MMCM_RSTN (1 << 1)
+
+#define ADI_REG_CNTRL 0x0044
+#define ADI_R1_MODE (1 << 2)
+#define ADI_DDR_EDGESEL (1 << 1)
+#define ADI_PIN_MODE (1 << 0)
+
+#define ADI_REG_STATUS 0x005C
+#define ADI_MUX_PN_ERR (1 << 3)
+#define ADI_MUX_PN_OOS (1 << 2)
+#define ADI_MUX_OVER_RANGE (1 << 1)
+#define ADI_STATUS (1 << 0)
+
+#define ADI_REG_DELAY_CNTRL 0x0060 /* <= v8.0 */
+#define ADI_DELAY_SEL (1 << 17)
+#define ADI_DELAY_RWN (1 << 16)
+#define ADI_DELAY_ADDRESS(x) (((x) & 0xFF) << 8)
+#define ADI_TO_DELAY_ADDRESS(x) (((x) >> 8) & 0xFF)
+#define ADI_DELAY_WDATA(x) (((x) & 0x1F) << 0)
+#define ADI_TO_DELAY_WDATA(x) (((x) >> 0) & 0x1F)
+
+#define ADI_REG_CHAN_CNTRL(c) (0x0400 + (c) * 0x40)
+#define ADI_PN_SEL (1 << 10) /* !v8.0 */
+#define ADI_IQCOR_ENB (1 << 9)
+#define ADI_DCFILT_ENB (1 << 8)
+#define ADI_FORMAT_SIGNEXT (1 << 6)
+#define ADI_FORMAT_TYPE (1 << 5)
+#define ADI_FORMAT_ENABLE (1 << 4)
+#define ADI_PN23_TYPE (1 << 1) /* !v8.0 */
+#define ADI_ENABLE (1 << 0)
+
+#define ADI_REG_CHAN_STATUS(c) (0x0404 + (c) * 0x40)
+#define ADI_PN_ERR (1 << 2)
+#define ADI_PN_OOS (1 << 1)
+#define ADI_OVER_RANGE (1 << 0)
+
+#define ADI_REG_CHAN_CNTRL_1(c) (0x0410 + (c) * 0x40)
+#define ADI_DCFILT_OFFSET(x) (((x) & 0xFFFF) << 16)
+#define ADI_TO_DCFILT_OFFSET(x) (((x) >> 16) & 0xFFFF)
+#define ADI_DCFILT_COEFF(x) (((x) & 0xFFFF) << 0)
+#define ADI_TO_DCFILT_COEFF(x) (((x) >> 0) & 0xFFFF)
+
+#define ADI_REG_CHAN_CNTRL_2(c) (0x0414 + (c) * 0x40)
+#define ADI_IQCOR_COEFF_1(x) (((x) & 0xFFFF) << 16)
+#define ADI_TO_IQCOR_COEFF_1(x) (((x) >> 16) & 0xFFFF)
+#define ADI_IQCOR_COEFF_2(x) (((x) & 0xFFFF) << 0)
+#define ADI_TO_IQCOR_COEFF_2(x) (((x) >> 0) & 0xFFFF)
+
+#define PCORE_VERSION(major, minor, letter) ((major << 16) | (minor << 8) | letter)
+#define PCORE_VERSION_MAJOR(version) (version >> 16)
+#define PCORE_VERSION_MINOR(version) ((version >> 8) & 0xff)
+#define PCORE_VERSION_LETTER(version) (version & 0xff)
+
+#define ADI_REG_CHAN_CNTRL_3(c) (0x0418 + (c) * 0x40) /* v8.0 */
+#define ADI_ADC_PN_SEL(x) (((x) & 0xF) << 16)
+#define ADI_TO_ADC_PN_SEL(x) (((x) >> 16) & 0xF)
+#define ADI_ADC_DATA_SEL(x) (((x) & 0xF) << 0)
+#define ADI_TO_ADC_DATA_SEL(x) (((x) >> 0) & 0xF)
+
+/* PCORE Version > 8.00 */
+#define ADI_REG_DELAY(l) (0x0800 + (l) * 0x4)
+
+enum adc_pn_sel {
+ ADC_PN9 = 0,
+ ADC_PN23A = 1,
+ ADC_PN7 = 4,
+ ADC_PN15 = 5,
+ ADC_PN23 = 6,
+ ADC_PN31 = 7,
+ ADC_PN_CUSTOM = 9,
+ ADC_PN_END = 10,
+};
+
+enum adc_data_sel {
+ ADC_DATA_SEL_NORM,
+ ADC_DATA_SEL_LB, /* DAC loopback */
+ ADC_DATA_SEL_RAMP, /* TBD */
+};
+
+/* Bitwise positions of GPOs in RFFE control register */
+#define RFFE_CONTROL_RESET_N 0
+#define RFFE_CONTROL_SYNC_IN 4
+
+/******************************************************************************/
+/************************ Functions Declarations ******************************/
+/******************************************************************************/
+
+int spi_init(struct ad9361_rf_phy *phy, void *userdata);
+int spi_write(struct spi_device *spi, uint16_t cmd, const uint8_t *buf,
+ unsigned int len);
+int spi_read(struct spi_device *spi, uint16_t cmd, uint8_t *buf,
+ unsigned int len);
+
+int gpio_init(struct ad9361_rf_phy *phy, void *userdata);
+bool gpio_is_valid(struct gpio_device *gpio, int32_t number);
+int gpio_set_value(struct gpio_device *gpio, int32_t number, bool value);
+
+void udelay(unsigned long usecs);
+void mdelay(unsigned long msecs);
+unsigned long msleep_interruptible(unsigned int msecs);
+
+#ifndef AXI_ADC_NOT_PRESENT
+int axiadc_init(struct ad9361_rf_phy *phy, void *userdata);
+int axiadc_post_setup(struct ad9361_rf_phy *phy);
+int axiadc_read(struct axiadc_state *st, uint32_t addr, uint32_t *data);
+int axiadc_write(struct axiadc_state *st, uint32_t addr, uint32_t data);
+int axiadc_set_pnsel(struct axiadc_state *st, unsigned int channel, enum adc_pn_sel sel);
+int axiadc_idelay_set(struct axiadc_state *st, unsigned int lane, unsigned int val);
+#endif
+
+#endif