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