aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/common
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp/common')
-rw-r--r--host/lib/usrp/common/CMakeLists.txt38
-rw-r--r--host/lib/usrp/common/ad9361_ctrl.cpp173
-rw-r--r--host/lib/usrp/common/ad9361_ctrl.hpp128
-rw-r--r--host/lib/usrp/common/ad9361_transaction.h102
-rw-r--r--host/lib/usrp/common/adf4001_ctrl.cpp151
-rw-r--r--host/lib/usrp/common/adf4001_ctrl.hpp142
-rw-r--r--host/lib/usrp/common/apply_corrections.cpp192
-rw-r--r--host/lib/usrp/common/apply_corrections.hpp41
-rw-r--r--host/lib/usrp/common/async_packet_handler.hpp71
-rw-r--r--host/lib/usrp/common/fifo_ctrl_excelsior.cpp293
-rw-r--r--host/lib/usrp/common/fifo_ctrl_excelsior.hpp63
-rw-r--r--host/lib/usrp/common/fx2_ctrl.cpp469
-rw-r--r--host/lib/usrp/common/fx2_ctrl.hpp146
-rw-r--r--host/lib/usrp/common/recv_packet_demuxer.cpp115
-rw-r--r--host/lib/usrp/common/recv_packet_demuxer.hpp41
-rw-r--r--host/lib/usrp/common/recv_packet_demuxer_3000.hpp127
-rw-r--r--host/lib/usrp/common/validate_subdev_spec.cpp73
-rw-r--r--host/lib/usrp/common/validate_subdev_spec.hpp38
18 files changed, 2403 insertions, 0 deletions
diff --git a/host/lib/usrp/common/CMakeLists.txt b/host/lib/usrp/common/CMakeLists.txt
new file mode 100644
index 000000000..1728b63f9
--- /dev/null
+++ b/host/lib/usrp/common/CMakeLists.txt
@@ -0,0 +1,38 @@
+#
+# Copyright 2011-2013 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+IF(ENABLE_USB)
+ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/../firmware/fx2/common)
+
+ LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/fx2_ctrl.cpp
+ )
+ENDIF(ENABLE_USB)
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+
+LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/adf4001_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/ad9361_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/apply_corrections.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/validate_subdev_spec.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/recv_packet_demuxer.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/fifo_ctrl_excelsior.cpp
+)
diff --git a/host/lib/usrp/common/ad9361_ctrl.cpp b/host/lib/usrp/common/ad9361_ctrl.cpp
new file mode 100644
index 000000000..1afa2fbb7
--- /dev/null
+++ b/host/lib/usrp/common/ad9361_ctrl.cpp
@@ -0,0 +1,173 @@
+//
+// Copyright 2012-2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "ad9361_ctrl.hpp"
+#include "ad9361_transaction.h"
+#include <uhd/exception.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/utils/msg.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/format.hpp>
+#include <cstring>
+
+//! compat strnlen for platforms that dont have it
+static size_t my_strnlen(const char *str, size_t max)
+{
+ const char *end = (const char *)std::memchr((const void *)str, 0, max);
+ if (end == NULL) return max;
+ return (size_t)(end - str);
+}
+
+using namespace uhd;
+
+struct ad9361_ctrl_impl : public ad9361_ctrl
+{
+ ad9361_ctrl_impl(ad9361_ctrl_iface_sptr iface):
+ _iface(iface), _seq(0)
+ {
+ ad9361_transaction_t request;
+
+ request.action = AD9361_ACTION_ECHO;
+ this->do_transaction(request);
+
+ request.action = AD9361_ACTION_INIT;
+ this->do_transaction(request);
+ }
+
+ double set_gain(const std::string &which, const double value)
+ {
+ ad9361_transaction_t request;
+
+ if (which == "RX1") request.action = AD9361_ACTION_SET_RX1_GAIN;
+ if (which == "RX2") request.action = AD9361_ACTION_SET_RX2_GAIN;
+ if (which == "TX1") request.action = AD9361_ACTION_SET_TX1_GAIN;
+ if (which == "TX2") request.action = AD9361_ACTION_SET_TX2_GAIN;
+
+ ad9361_double_pack(value, request.value.gain);
+ const ad9361_transaction_t reply = this->do_transaction(request);
+ return ad9361_double_unpack(reply.value.gain);
+ }
+
+ //! set a new clock rate, return the exact value
+ double set_clock_rate(const double rate)
+ {
+ //warning for known trouble rates
+ if (rate > 56e6) UHD_MSG(warning) << boost::format(
+ "The requested clock rate %f MHz may cause slow configuration.\n"
+ "The driver recommends a master clock rate less than %f MHz.\n"
+ ) % (rate/1e6) % 56.0 << std::endl;
+
+ //clip to known bounds
+ const meta_range_t clock_rate_range = ad9361_ctrl::get_clock_rate_range();
+ const double clipped_rate = clock_rate_range.clip(rate);
+
+ ad9361_transaction_t request;
+ request.action = AD9361_ACTION_SET_CLOCK_RATE;
+ ad9361_double_pack(clipped_rate, request.value.rate);
+ const ad9361_transaction_t reply = this->do_transaction(request);
+ return ad9361_double_unpack(reply.value.rate);
+ }
+
+ //! set which RX and TX chains/antennas are active
+ void set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2)
+ {
+ boost::uint32_t mask = 0;
+ if (tx1) mask |= (1 << 0);
+ if (tx2) mask |= (1 << 1);
+ if (rx1) mask |= (1 << 2);
+ if (rx2) mask |= (1 << 3);
+
+ ad9361_transaction_t request;
+ request.action = AD9361_ACTION_SET_ACTIVE_CHAINS;
+ request.value.enable_mask = mask;
+ this->do_transaction(request);
+ }
+
+ //! tune the given frontend, return the exact value
+ double tune(const std::string &which, const double freq)
+ {
+ //clip to known bounds
+ const meta_range_t freq_range = ad9361_ctrl::get_rf_freq_range();
+ const double clipped_freq = freq_range.clip(freq);
+
+ ad9361_transaction_t request;
+
+ if (which[0] == 'R') request.action = AD9361_ACTION_SET_RX_FREQ;
+ if (which[0] == 'T') request.action = AD9361_ACTION_SET_TX_FREQ;
+
+ const double value = ad9361_ctrl::get_rf_freq_range().clip(clipped_freq);
+ ad9361_double_pack(value, request.value.freq);
+ const ad9361_transaction_t reply = this->do_transaction(request);
+ return ad9361_double_unpack(reply.value.freq);
+ }
+
+ //! turn on/off Catalina's data port loopback
+ void data_port_loopback(const bool on)
+ {
+ ad9361_transaction_t request;
+ request.action = AD9361_ACTION_SET_CODEC_LOOP;
+ request.value.codec_loop = on? 1 : 0;
+ this->do_transaction(request);
+ }
+
+ ad9361_transaction_t do_transaction(const ad9361_transaction_t &request)
+ {
+ boost::mutex::scoped_lock lock(_mutex);
+
+ //declare in/out buffers
+ unsigned char in_buff[64] = {};
+ unsigned char out_buff[64] = {};
+
+ //copy the input transaction
+ std::memcpy(in_buff, &request, sizeof(request));
+
+ //fill in other goodies
+ ad9361_transaction_t *in = (ad9361_transaction_t *)in_buff;
+ in->version = AD9361_TRANSACTION_VERSION;
+ in->sequence = _seq++;
+
+ //transact
+ _iface->ad9361_transact(in_buff, out_buff);
+ ad9361_transaction_t *out = (ad9361_transaction_t *)out_buff;
+
+ //sanity checks
+ UHD_ASSERT_THROW(out->version == in->version);
+ UHD_ASSERT_THROW(out->sequence == in->sequence);
+
+ //handle errors
+ const size_t len = my_strnlen(out->error_msg, AD9361_TRANSACTION_MAX_ERROR_MSG);
+ const std::string error_msg(out->error_msg, len);
+ if (not error_msg.empty()) throw uhd::runtime_error("ad9361 do transaction: " + error_msg);
+
+ //return result done!
+ return *out;
+ }
+
+ ad9361_ctrl_iface_sptr _iface;
+ size_t _seq;
+ boost::mutex _mutex;
+
+};
+
+
+/***********************************************************************
+ * Make an instance of the implementation
+ **********************************************************************/
+ad9361_ctrl::sptr ad9361_ctrl::make(ad9361_ctrl_iface_sptr iface)
+{
+ return sptr(new ad9361_ctrl_impl(iface));
+}
diff --git a/host/lib/usrp/common/ad9361_ctrl.hpp b/host/lib/usrp/common/ad9361_ctrl.hpp
new file mode 100644
index 000000000..fd8012764
--- /dev/null
+++ b/host/lib/usrp/common/ad9361_ctrl.hpp
@@ -0,0 +1,128 @@
+//
+// Copyright 2012-2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_AD9361_CTRL_HPP
+#define INCLUDED_AD9361_CTRL_HPP
+
+#include <uhd/transport/zero_copy.hpp>
+#include <uhd/types/serial.hpp>
+#include <uhd/types/ranges.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include <boost/function.hpp>
+#include <vector>
+#include <string>
+
+
+struct ad9361_ctrl_iface_type
+{
+ virtual void ad9361_transact(const unsigned char in_buff[64], unsigned char out_buff[64]) = 0;
+};
+typedef boost::shared_ptr<ad9361_ctrl_iface_type> ad9361_ctrl_iface_sptr;
+
+
+struct ad9361_ctrl_over_zc : ad9361_ctrl_iface_type
+{
+ ad9361_ctrl_over_zc(uhd::transport::zero_copy_if::sptr xport)
+ {
+ _xport = xport;
+ }
+
+ void ad9361_transact(const unsigned char in_buff[64], unsigned char out_buff[64])
+ {
+ {
+ uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0);
+ if (not buff or buff->size() < 64) throw std::runtime_error("ad9361_ctrl_over_zc send timeout");
+ std::memcpy(buff->cast<void *>(), in_buff, 64);
+ buff->commit(64);
+ }
+ {
+ uhd::transport::managed_recv_buffer::sptr buff = _xport->get_recv_buff(10.0);
+ if (not buff or buff->size() < 64) throw std::runtime_error("ad9361_ctrl_over_zc recv timeout");
+ std::memcpy(out_buff, buff->cast<const void *>(), 64);
+ }
+ }
+
+ uhd::transport::zero_copy_if::sptr _xport;
+};
+
+
+class ad9361_ctrl : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<ad9361_ctrl> sptr;
+
+ //! make a new codec control object
+ static sptr make(ad9361_ctrl_iface_sptr iface);
+
+ //! Get a list of gain names for RX or TX
+ static std::vector<std::string> get_gain_names(const std::string &/*which*/)
+ {
+ return std::vector<std::string>(1, "PGA");
+ }
+
+ //! get the gain range for a particular gain element
+ static uhd::meta_range_t get_gain_range(const std::string &which)
+ {
+ if(which[0] == 'R') {
+ return uhd::meta_range_t(0.0, 73.0, 1.0);
+ } else {
+ return uhd::meta_range_t(0.0, 89.75, 0.25);
+ }
+ }
+
+ //! get the freq range for the frontend which
+ static uhd::meta_range_t get_rf_freq_range(void)
+ {
+ return uhd::meta_range_t(50e6, 6e9);
+ }
+
+ //! get the filter range for the frontend which
+ static uhd::meta_range_t get_bw_filter_range(const std::string &/*which*/)
+ {
+ return uhd::meta_range_t(200e3, 56e6);
+ }
+
+ //! get the clock rate range for the frontend
+ static uhd::meta_range_t get_clock_rate_range(void)
+ {
+ //return uhd::meta_range_t(220e3, 61.44e6);
+ return uhd::meta_range_t(5e6, 61.44e6); //5 MHz DCM low end
+ }
+
+ //! set the filter bandwidth for the frontend
+ double set_bw_filter(const std::string &/*which*/, const double /*bw*/)
+ {
+ return 56e6; //TODO
+ }
+
+ //! set the gain for a particular gain element
+ virtual double set_gain(const std::string &which, const double value) = 0;
+
+ //! set a new clock rate, return the exact value
+ virtual double set_clock_rate(const double rate) = 0;
+
+ //! set which RX and TX chains/antennas are active
+ virtual void set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2) = 0;
+
+ //! tune the given frontend, return the exact value
+ virtual double tune(const std::string &which, const double value) = 0;
+
+ //! turn on/off Catalina's data port loopback
+ virtual void data_port_loopback(const bool on) = 0;
+};
+
+#endif /* INCLUDED_AD9361_CTRL_HPP */
diff --git a/host/lib/usrp/common/ad9361_transaction.h b/host/lib/usrp/common/ad9361_transaction.h
new file mode 100644
index 000000000..7b41b811f
--- /dev/null
+++ b/host/lib/usrp/common/ad9361_transaction.h
@@ -0,0 +1,102 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_AD9361_TRANSACTION_H
+#define INCLUDED_AD9361_TRANSACTION_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//various constants
+#define AD9361_TRANSACTION_VERSION 0x4
+#define AD9361_TRANSACTION_MAX_ERROR_MSG 40
+
+//action types
+#define AD9361_ACTION_ECHO 0
+#define AD9361_ACTION_INIT 1
+#define AD9361_ACTION_SET_RX1_GAIN 2
+#define AD9361_ACTION_SET_TX1_GAIN 3
+#define AD9361_ACTION_SET_RX2_GAIN 4
+#define AD9361_ACTION_SET_TX2_GAIN 5
+#define AD9361_ACTION_SET_RX_FREQ 6
+#define AD9361_ACTION_SET_TX_FREQ 7
+#define AD9361_ACTION_SET_CODEC_LOOP 8
+#define AD9361_ACTION_SET_CLOCK_RATE 9
+#define AD9361_ACTION_SET_ACTIVE_CHAINS 10
+
+static inline void ad9361_double_pack(const double input, uint32_t output[2])
+{
+ const uint32_t *p = (const uint32_t *)&input;
+ output[0] = p[0];
+ output[1] = p[1];
+}
+
+static inline double ad9361_double_unpack(const uint32_t input[2])
+{
+ double output = 0.0;
+ uint32_t *p = (uint32_t *)&output;
+ p[0] = input[0];
+ p[1] = input[1];
+ return output;
+}
+
+typedef struct
+{
+ //version is expected to be AD9361_TRANSACTION_VERSION
+ //check otherwise for compatibility
+ uint32_t version;
+
+ //sequence number - increment every call for sanity
+ uint32_t sequence;
+
+ //action tells us what to do, see AD9361_ACTION_*
+ uint32_t action;
+
+ union
+ {
+ //enable mask for chains
+ uint32_t enable_mask;
+
+ //true to enable codec internal loopback
+ uint32_t codec_loop;
+
+ //freq holds request LO freq and result from tune
+ uint32_t freq[2];
+
+ //gain holds request gain and result from action
+ uint32_t gain[2];
+
+ //rate holds request clock rate and result from action
+ uint32_t rate[2];
+
+ } value;
+
+ //error message comes back as a reply -
+ //set to null string for no error \0
+ char error_msg[];
+
+} ad9361_transaction_t;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* INCLUDED_AD9361_TRANSACTION_H */
diff --git a/host/lib/usrp/common/adf4001_ctrl.cpp b/host/lib/usrp/common/adf4001_ctrl.cpp
new file mode 100644
index 000000000..46171c7ce
--- /dev/null
+++ b/host/lib/usrp/common/adf4001_ctrl.cpp
@@ -0,0 +1,151 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// Original ADF4001 driver written by: bistromath
+// Mar 1, 2013
+//
+// Re-used and re-licensed with permission.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "adf4001_ctrl.hpp"
+
+#include <uhd/utils/msg.hpp>
+#include <iostream>
+#include <iomanip>
+
+using namespace uhd;
+using namespace uhd::usrp;
+
+adf4001_regs_t::adf4001_regs_t(void) {
+ ref_counter = 0;
+ n = 0;
+ charge_pump_current_1 = 0;
+ charge_pump_current_2 = 0;
+ anti_backlash_width = ANTI_BACKLASH_WIDTH_2_9NS;
+ lock_detect_precision = LOCK_DETECT_PRECISION_3CYC;
+ charge_pump_gain = CHARGE_PUMP_GAIN_1;
+ counter_reset = COUNTER_RESET_NORMAL;
+ power_down = POWER_DOWN_NORMAL;
+ muxout = MUXOUT_TRISTATE_OUT;
+ phase_detector_polarity = PHASE_DETECTOR_POLARITY_NEGATIVE;
+ charge_pump_mode = CHARGE_PUMP_TRISTATE;
+ fastlock_mode = FASTLOCK_MODE_DISABLED;
+ timer_counter_control = TIMEOUT_3CYC;
+}
+
+
+boost::uint32_t adf4001_regs_t::get_reg(boost::uint8_t addr) {
+ boost::uint32_t reg = 0;
+ switch (addr) {
+ case 0:
+ reg |= (boost::uint32_t(ref_counter) & 0x003FFF) << 2;
+ reg |= (boost::uint32_t(anti_backlash_width) & 0x000003) << 16;
+ reg |= (boost::uint32_t(lock_detect_precision) & 0x000001) << 20;
+ break;
+ case 1:
+ reg |= (boost::uint32_t(n) & 0x001FFF) << 8;
+ reg |= (boost::uint32_t(charge_pump_gain) & 0x000001) << 21;
+ break;
+ case 2:
+ reg |= (boost::uint32_t(counter_reset) & 0x000001) << 2;
+ reg |= (boost::uint32_t(power_down) & 0x000001) << 3;
+ reg |= (boost::uint32_t(muxout) & 0x000007) << 4;
+ reg |= (boost::uint32_t(phase_detector_polarity) & 0x000001) << 7;
+ reg |= (boost::uint32_t(charge_pump_mode) & 0x000001) << 8;
+ reg |= (boost::uint32_t(fastlock_mode) & 0x000003) << 9;
+ reg |= (boost::uint32_t(timer_counter_control) & 0x00000F) << 11;
+ reg |= (boost::uint32_t(charge_pump_current_1) & 0x000007) << 15;
+ reg |= (boost::uint32_t(charge_pump_current_2) & 0x000007) << 18;
+ reg |= (boost::uint32_t(power_down) & 0x000002) << 21;
+ break;
+ case 3:
+ reg |= (boost::uint32_t(counter_reset) & 0x000001) << 2;
+ reg |= (boost::uint32_t(power_down) & 0x000001) << 3;
+ reg |= (boost::uint32_t(muxout) & 0x000007) << 4;
+ reg |= (boost::uint32_t(phase_detector_polarity) & 0x000001) << 7;
+ reg |= (boost::uint32_t(charge_pump_mode) & 0x000001) << 8;
+ reg |= (boost::uint32_t(fastlock_mode) & 0x000003) << 9;
+ reg |= (boost::uint32_t(timer_counter_control) & 0x00000F) << 11;
+ reg |= (boost::uint32_t(charge_pump_current_1) & 0x000007) << 15;
+ reg |= (boost::uint32_t(charge_pump_current_2) & 0x000007) << 18;
+ reg |= (boost::uint32_t(power_down) & 0x000002) << 21;
+ break;
+ default:
+ break;
+ }
+
+ reg |= (boost::uint32_t(addr) & 0x03);
+
+ return reg;
+}
+
+
+adf4001_ctrl::adf4001_ctrl(spi_core_3000::sptr _spi, int slaveno):
+ spi_iface(_spi),
+ slaveno(slaveno)
+ {
+
+ spi_config.mosi_edge = spi_config_t::EDGE_RISE;
+
+ //set defaults
+ adf4001_regs.ref_counter = 1;
+ adf4001_regs.n = 4;
+ adf4001_regs.charge_pump_current_1 = 7;
+ adf4001_regs.charge_pump_current_2 = 7;
+ adf4001_regs.muxout = adf4001_regs_t::MUXOUT_DLD;
+ adf4001_regs.counter_reset = adf4001_regs_t::COUNTER_RESET_NORMAL;
+ adf4001_regs.phase_detector_polarity = adf4001_regs_t::PHASE_DETECTOR_POLARITY_POSITIVE;
+ adf4001_regs.charge_pump_mode = adf4001_regs_t::CHARGE_PUMP_TRISTATE;
+
+ //everything else should be defaults
+
+ program_regs();
+}
+
+void adf4001_ctrl::set_lock_to_ext_ref(bool external) {
+ if(external) {
+ adf4001_regs.charge_pump_mode = adf4001_regs_t::CHARGE_PUMP_NORMAL;
+ } else {
+ adf4001_regs.charge_pump_mode = adf4001_regs_t::CHARGE_PUMP_TRISTATE;
+ }
+
+ program_regs();
+}
+
+void adf4001_ctrl::program_regs(void) {
+ //no control over CE, only LE, therefore we use the initialization latch method
+ write_reg(3);
+ boost::this_thread::sleep(boost::posix_time::microseconds(1));
+
+ //write R counter latch (0)
+ write_reg(0);
+ boost::this_thread::sleep(boost::posix_time::microseconds(1));
+
+ //write N counter latch (1)
+ write_reg(1);
+ boost::this_thread::sleep(boost::posix_time::microseconds(1));
+}
+
+
+void adf4001_ctrl::write_reg(boost::uint8_t addr) {
+ boost::uint32_t reg = adf4001_regs.get_reg(addr); //load the reg data
+
+ spi_iface->transact_spi(slaveno,
+ spi_config,
+ reg,
+ 24,
+ false);
+}
diff --git a/host/lib/usrp/common/adf4001_ctrl.hpp b/host/lib/usrp/common/adf4001_ctrl.hpp
new file mode 100644
index 000000000..a16cff3fa
--- /dev/null
+++ b/host/lib/usrp/common/adf4001_ctrl.hpp
@@ -0,0 +1,142 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// Original ADF4001 driver written by: bistromath
+// Mar 1, 2013
+//
+// Re-used and re-licensed with permission.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_COMMON_ADF4001_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_ADF4001_HPP
+
+#include "spi_core_3000.hpp"
+#include <uhd/types/serial.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/thread/thread.hpp>
+
+namespace uhd { namespace usrp {
+
+class adf4001_regs_t {
+public:
+
+ /* Function prototypes */
+ boost::uint32_t get_reg(boost::uint8_t addr);
+ adf4001_regs_t(void);
+
+ /* Register values / addresses */
+ boost::uint16_t ref_counter; //14 bits
+ boost::uint16_t n; //13 bits
+ boost::uint8_t charge_pump_current_1; //3 bits
+ boost::uint8_t charge_pump_current_2; //3 bits
+
+ enum anti_backlash_width_t {
+ ANTI_BACKLASH_WIDTH_2_9NS = 0,
+ ANTI_BACKLASH_WIDTH_1_3NS = 1,
+ ANTI_BACKLASH_WIDTH_6_0NS = 2,
+ ANTI_BACKLASH_WIDTH_2_9NS_WAT = 3
+ };
+ anti_backlash_width_t anti_backlash_width;
+
+ enum lock_detect_precision_t {
+ LOCK_DETECT_PRECISION_3CYC = 0,
+ LOCK_DETECT_PRECISION_5CYC = 1
+ };
+ lock_detect_precision_t lock_detect_precision;
+ enum charge_pump_gain_t {
+ CHARGE_PUMP_GAIN_1 = 0,
+ CHARGE_PUMP_GAIN_2 = 1
+ };
+ charge_pump_gain_t charge_pump_gain;
+ enum counter_reset_t {
+ COUNTER_RESET_NORMAL = 0,
+ COUNTER_RESET_RESET = 1
+ };
+ counter_reset_t counter_reset;
+ enum power_down_t {
+ POWER_DOWN_NORMAL = 0,
+ POWER_DOWN_ASYNC = 1,
+ POWER_DOWN_SYNC = 3
+ };
+ power_down_t power_down;
+ enum muxout_t {
+ MUXOUT_TRISTATE_OUT = 0,
+ MUXOUT_DLD = 1,
+ MUXOUT_NDIV = 2,
+ MUXOUT_AVDD = 3,
+ MUXOUT_RDIV = 4,
+ MUXOUT_NCH_OD_ALD = 5,
+ MUXOUT_SDO = 6,
+ MUXOUT_GND = 7
+ };
+ muxout_t muxout;
+ enum phase_detector_polarity_t {
+ PHASE_DETECTOR_POLARITY_NEGATIVE = 0,
+ PHASE_DETECTOR_POLARITY_POSITIVE = 1
+ };
+ phase_detector_polarity_t phase_detector_polarity;
+ enum charge_pump_mode_t {
+ CHARGE_PUMP_NORMAL = 0,
+ CHARGE_PUMP_TRISTATE = 1
+ };
+ charge_pump_mode_t charge_pump_mode;
+ enum fastlock_mode_t {
+ FASTLOCK_MODE_DISABLED = 0,
+ FASTLOCK_MODE_1 = 1,
+ FASTLOCK_MODE_2 = 2
+ };
+ fastlock_mode_t fastlock_mode;
+ enum timer_counter_control_t {
+ TIMEOUT_3CYC = 0,
+ TIMEOUT_7CYC = 1,
+ TIMEOUT_11CYC = 2,
+ TIMEOUT_15CYC = 3,
+ TIMEOUT_19CYC = 4,
+ TIMEOUT_23CYC = 5,
+ TIMEOUT_27CYC = 6,
+ TIMEOUT_31CYC = 7,
+ TIMEOUT_35CYC = 8,
+ TIMEOUT_39CYC = 9,
+ TIMEOUT_43CYC = 10,
+ TIMEOUT_47CYC = 11,
+ TIMEOUT_51CYC = 12,
+ TIMEOUT_55CYC = 13,
+ TIMEOUT_59CYC = 14,
+ TIMEOUT_63CYC = 15,
+ };
+ timer_counter_control_t timer_counter_control;
+};
+
+
+class adf4001_ctrl {
+public:
+
+ adf4001_ctrl(spi_core_3000::sptr _spi, int slaveno);
+ void set_lock_to_ext_ref(bool external);
+
+private:
+ spi_core_3000::sptr spi_iface;
+ int slaveno;
+ spi_config_t spi_config;
+ adf4001_regs_t adf4001_regs;
+
+ void program_regs(void);
+ void write_reg(boost::uint8_t addr);
+};
+
+}}
+
+#endif
diff --git a/host/lib/usrp/common/apply_corrections.cpp b/host/lib/usrp/common/apply_corrections.cpp
new file mode 100644
index 000000000..3d33e7d11
--- /dev/null
+++ b/host/lib/usrp/common/apply_corrections.cpp
@@ -0,0 +1,192 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "apply_corrections.hpp"
+#include <uhd/usrp/dboard_eeprom.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/csv.hpp>
+#include <uhd/types/dict.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/foreach.hpp>
+#include <boost/thread/mutex.hpp>
+#include <cstdio>
+#include <complex>
+#include <fstream>
+
+namespace fs = boost::filesystem;
+
+boost::mutex corrections_mutex;
+
+/***********************************************************************
+ * Helper routines
+ **********************************************************************/
+static double linear_interp(double x, double x0, double y0, double x1, double y1){
+ return y0 + (x - x0)*(y1 - y0)/(x1 - x0);
+}
+
+/***********************************************************************
+ * FE apply corrections implementation
+ **********************************************************************/
+struct fe_cal_t{
+ double lo_freq;
+ double iq_corr_real;
+ double iq_corr_imag;
+};
+
+static bool fe_cal_comp(fe_cal_t a, fe_cal_t b){
+ return (a.lo_freq < b.lo_freq);
+}
+
+static uhd::dict<std::string, std::vector<fe_cal_t> > fe_cal_cache;
+
+static bool is_same_freq(const double f1, const double f2)
+{
+ const double epsilon = 0.1;
+ return ((f1 - epsilon) < f2 and (f1 + epsilon) > f2);
+}
+
+static std::complex<double> get_fe_correction(
+ const std::string &key, const double lo_freq
+){
+ const std::vector<fe_cal_t> &datas = fe_cal_cache[key];
+ if (datas.empty()) throw uhd::runtime_error("empty calibration table " + key);
+
+ //search for lo freq
+ size_t lo_index = 0;
+ size_t hi_index = datas.size()-1;
+ for (size_t i = 0; i < datas.size(); i++){
+ if (is_same_freq(datas[i].lo_freq, lo_freq))
+ {
+ hi_index = i;
+ lo_index = i;
+ break;
+ }
+ if (datas[i].lo_freq > lo_freq){
+ hi_index = i;
+ break;
+ }
+ lo_index = i;
+ }
+
+ if (lo_index == 0) return std::complex<double>(datas[lo_index].iq_corr_real, datas[lo_index].iq_corr_imag);
+ if (hi_index == lo_index) return std::complex<double>(datas[hi_index].iq_corr_real, datas[hi_index].iq_corr_imag);
+
+ //interpolation time
+ return std::complex<double>(
+ linear_interp(lo_freq, datas[lo_index].lo_freq, datas[lo_index].iq_corr_real, datas[hi_index].lo_freq, datas[hi_index].iq_corr_real),
+ linear_interp(lo_freq, datas[lo_index].lo_freq, datas[lo_index].iq_corr_imag, datas[hi_index].lo_freq, datas[hi_index].iq_corr_imag)
+ );
+}
+
+static void apply_fe_corrections(
+ uhd::property_tree::sptr sub_tree,
+ const uhd::fs_path &db_path,
+ const uhd::fs_path &fe_path,
+ const std::string &file_prefix,
+ const double lo_freq
+){
+ //extract eeprom serial
+ const uhd::usrp::dboard_eeprom_t db_eeprom = sub_tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get();
+
+ //make the calibration file path
+ const fs::path cal_data_path = fs::path(uhd::get_app_path()) / ".uhd" / "cal" / (file_prefix + db_eeprom.serial + ".csv");
+ if (not fs::exists(cal_data_path)) return;
+
+ //parse csv file or get from cache
+ if (not fe_cal_cache.has_key(cal_data_path.string())){
+ std::ifstream cal_data(cal_data_path.string().c_str());
+ const uhd::csv::rows_type rows = uhd::csv::to_rows(cal_data);
+
+ bool read_data = false, skip_next = false;;
+ std::vector<fe_cal_t> datas;
+ BOOST_FOREACH(const uhd::csv::row_type &row, rows){
+ if (not read_data and not row.empty() and row[0] == "DATA STARTS HERE"){
+ read_data = true;
+ skip_next = true;
+ continue;
+ }
+ if (not read_data) continue;
+ if (skip_next){
+ skip_next = false;
+ continue;
+ }
+ fe_cal_t data;
+ std::sscanf(row[0].c_str(), "%lf" , &data.lo_freq);
+ std::sscanf(row[1].c_str(), "%lf" , &data.iq_corr_real);
+ std::sscanf(row[2].c_str(), "%lf" , &data.iq_corr_imag);
+ datas.push_back(data);
+ }
+ std::sort(datas.begin(), datas.end(), fe_cal_comp);
+ fe_cal_cache[cal_data_path.string()] = datas;
+ UHD_MSG(status) << "Loaded " << cal_data_path.string() << std::endl;
+
+ }
+
+ sub_tree->access<std::complex<double> >(fe_path)
+ .set(get_fe_correction(cal_data_path.string(), lo_freq));
+}
+
+/***********************************************************************
+ * Wrapper routines with nice try/catch + print
+ **********************************************************************/
+void uhd::usrp::apply_tx_fe_corrections(
+ property_tree::sptr sub_tree, //starts at mboards/x
+ const std::string &slot, //name of dboard slot
+ const double lo_freq //actual lo freq
+){
+ boost::mutex::scoped_lock l(corrections_mutex);
+ try{
+ apply_fe_corrections(
+ sub_tree,
+ "dboards/" + slot + "/tx_eeprom",
+ "tx_frontends/" + slot + "/iq_balance/value",
+ "tx_iq_cal_v0.2_",
+ lo_freq
+ );
+ apply_fe_corrections(
+ sub_tree,
+ "dboards/" + slot + "/tx_eeprom",
+ "tx_frontends/" + slot + "/dc_offset/value",
+ "tx_dc_cal_v0.2_",
+ lo_freq
+ );
+ }
+ catch(const std::exception &e){
+ UHD_MSG(error) << "Failure in apply_tx_fe_corrections: " << e.what() << std::endl;
+ }
+}
+
+void uhd::usrp::apply_rx_fe_corrections(
+ property_tree::sptr sub_tree, //starts at mboards/x
+ const std::string &slot, //name of dboard slot
+ const double lo_freq //actual lo freq
+){
+ boost::mutex::scoped_lock l(corrections_mutex);
+ try{
+ apply_fe_corrections(
+ sub_tree,
+ "dboards/" + slot + "/rx_eeprom",
+ "rx_frontends/" + slot + "/iq_balance/value",
+ "rx_iq_cal_v0.2_",
+ lo_freq
+ );
+ }
+ catch(const std::exception &e){
+ UHD_MSG(error) << "Failure in apply_rx_fe_corrections: " << e.what() << std::endl;
+ }
+}
diff --git a/host/lib/usrp/common/apply_corrections.hpp b/host/lib/usrp/common/apply_corrections.hpp
new file mode 100644
index 000000000..c516862d1
--- /dev/null
+++ b/host/lib/usrp/common/apply_corrections.hpp
@@ -0,0 +1,41 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_COMMON_APPLY_CORRECTIONS_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_APPLY_CORRECTIONS_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/property_tree.hpp>
+#include <string>
+
+namespace uhd{ namespace usrp{
+
+ void apply_tx_fe_corrections(
+ property_tree::sptr sub_tree, //starts at mboards/x
+ const std::string &slot, //name of dboard slot
+ const double tx_lo_freq //actual lo freq
+ );
+
+ void apply_rx_fe_corrections(
+ property_tree::sptr sub_tree, //starts at mboards/x
+ const std::string &slot, //name of dboard slot
+ const double rx_lo_freq //actual lo freq
+ );
+
+}} //namespace uhd::usrp
+
+#endif /* INCLUDED_LIBUHD_USRP_COMMON_APPLY_CORRECTIONS_HPP */
diff --git a/host/lib/usrp/common/async_packet_handler.hpp b/host/lib/usrp/common/async_packet_handler.hpp
new file mode 100644
index 000000000..fef03483f
--- /dev/null
+++ b/host/lib/usrp/common/async_packet_handler.hpp
@@ -0,0 +1,71 @@
+//
+// Copyright 2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_COMMON_ASYNC_PACKET_HANDLER_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_ASYNC_PACKET_HANDLER_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/transport/vrt_if_packet.hpp>
+#include <uhd/types/metadata.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/msg.hpp>
+
+namespace uhd{ namespace usrp{
+
+ template <typename to_host_type>
+ void load_metadata_from_buff(
+ const to_host_type &to_host,
+ async_metadata_t &metadata,
+ const transport::vrt::if_packet_info_t &if_packet_info,
+ const boost::uint32_t *vrt_hdr,
+ const double tick_rate,
+ const size_t channel = 0
+ ){
+ const boost::uint32_t *payload = vrt_hdr + if_packet_info.num_header_words32;
+
+ //load into metadata
+ metadata.channel = channel;
+ metadata.has_time_spec = if_packet_info.has_tsf;
+ metadata.time_spec = time_spec_t::from_ticks(if_packet_info.tsf, tick_rate);
+ metadata.event_code = async_metadata_t::event_code_t(to_host(payload[0]) & 0xff);
+
+ //load user payload
+ for (size_t i = 1; i < if_packet_info.num_payload_words32; i++){
+ if (i-1 == 4) break; //limit of 4 words32
+ metadata.user_payload[i-1] = to_host(payload[i]);
+ }
+ }
+
+ UHD_INLINE void standard_async_msg_prints(const async_metadata_t &metadata)
+ {
+ if (metadata.event_code &
+ ( async_metadata_t::EVENT_CODE_UNDERFLOW
+ | async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET)
+ ) UHD_MSG(fastpath) << "U";
+ else if (metadata.event_code &
+ ( async_metadata_t::EVENT_CODE_SEQ_ERROR
+ | async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST)
+ ) UHD_MSG(fastpath) << "S";
+ else if (metadata.event_code &
+ async_metadata_t::EVENT_CODE_TIME_ERROR
+ ) UHD_MSG(fastpath) << "L";
+ }
+
+
+}} //namespace uhd::usrp
+
+#endif /* INCLUDED_LIBUHD_USRP_COMMON_ASYNC_PACKET_HANDLER_HPP */
diff --git a/host/lib/usrp/common/fifo_ctrl_excelsior.cpp b/host/lib/usrp/common/fifo_ctrl_excelsior.cpp
new file mode 100644
index 000000000..c0e2c5ea0
--- /dev/null
+++ b/host/lib/usrp/common/fifo_ctrl_excelsior.cpp
@@ -0,0 +1,293 @@
+//
+// Copyright 2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "fifo_ctrl_excelsior.hpp"
+#include "async_packet_handler.hpp"
+#include <uhd/exception.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/tasks.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <uhd/utils/thread_priority.hpp>
+#include <uhd/transport/vrt_if_packet.hpp>
+#include <uhd/transport/bounded_buffer.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/format.hpp>
+#include <boost/bind.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+static const size_t POKE32_CMD = (1 << 8);
+static const size_t PEEK32_CMD = 0;
+static const double ACK_TIMEOUT = 0.5;
+static const double MASSIVE_TIMEOUT = 10.0; //for when we wait on a timed command
+static const boost::uint32_t MAX_SEQS_OUT = 15;
+
+#define SPI_DIV _config.spi_base + 0
+#define SPI_CTRL _config.spi_base + 4
+#define SPI_DATA _config.spi_base + 8
+#define SPI_DIVIDER 4
+
+struct ctrl_result_t{
+ boost::uint32_t msg[2];
+};
+
+class fifo_ctrl_excelsior_impl : public fifo_ctrl_excelsior{
+public:
+
+ fifo_ctrl_excelsior_impl(zero_copy_if::sptr xport, const fifo_ctrl_excelsior_config &config):
+ _xport(xport),
+ _config(config),
+ _seq_out(0),
+ _seq_ack(0),
+ _timeout(ACK_TIMEOUT),
+ _async_fifo(1000),
+ _ctrl_fifo(MAX_SEQS_OUT+1)
+ {
+ while (_xport->get_recv_buff(0.0)){} //flush
+ this->set_time(uhd::time_spec_t(0.0));
+ this->set_tick_rate(1.0); //something possible but bogus
+ _msg_task = task::make(boost::bind(&fifo_ctrl_excelsior_impl::handle_msg, this));
+ this->init_spi();
+ }
+
+ ~fifo_ctrl_excelsior_impl(void){
+ _timeout = ACK_TIMEOUT; //reset timeout to something small
+ UHD_SAFE_CALL(
+ this->peek32(0); //dummy peek with the purpose of ack'ing all packets
+ )
+ }
+
+ bool pop_async_msg(async_metadata_t &async_metadata, double timeout){
+ boost::this_thread::disable_interruption di; //disable because the wait can throw
+ return _async_fifo.pop_with_timed_wait(async_metadata, timeout);
+ }
+
+ void handle_msg(void){
+ set_thread_priority_safe();
+ while (not boost::this_thread::interruption_requested()){
+ this->handle_msg1();
+ }
+ }
+
+ void handle_msg1(void){
+ managed_recv_buffer::sptr buff = _xport->get_recv_buff();
+ if (not buff) return;
+ const boost::uint32_t *pkt = buff->cast<const boost::uint32_t *>();
+ vrt::if_packet_info_t packet_info;
+ packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t);
+ try{
+ vrt::if_hdr_unpack_le(pkt, packet_info);
+ }
+ catch(const std::exception &ex){
+ UHD_MSG(error) << "FIFO ctrl bad VITA packet: " << ex.what() << std::endl;
+ }
+ if (packet_info.has_sid and packet_info.sid == _config.ctrl_sid_base){
+ ctrl_result_t res = ctrl_result_t();
+ res.msg[0] = uhd::wtohx(pkt[packet_info.num_header_words32+0]);
+ res.msg[1] = uhd::wtohx(pkt[packet_info.num_header_words32+1]);
+ _ctrl_fifo.push_with_haste(res);
+ }
+ else if (packet_info.has_sid and packet_info.sid >= _config.async_sid_base and packet_info.sid <= _config.async_sid_base + _config.num_async_chan){
+ async_metadata_t metadata;
+ load_metadata_from_buff(uhd::wtohx<boost::uint32_t>, metadata, packet_info, pkt, _tick_rate, packet_info.sid - _config.async_sid_base);
+ _async_fifo.push_with_pop_on_full(metadata);
+ standard_async_msg_prints(metadata);
+ }
+ else{
+ UHD_MSG(error) << "FIFO ctrl got unknown SID: " << packet_info.sid << std::endl;
+ }
+ }
+
+ /*******************************************************************
+ * Peek and poke 32 bit implementation
+ ******************************************************************/
+ void poke32(wb_addr_type addr, boost::uint32_t data){
+ boost::mutex::scoped_lock lock(_mutex);
+
+ this->send_pkt(addr, data, POKE32_CMD);
+
+ this->wait_for_ack(_seq_out-MAX_SEQS_OUT);
+ }
+
+ boost::uint32_t peek32(wb_addr_type addr){
+ boost::mutex::scoped_lock lock(_mutex);
+
+ this->send_pkt(addr, 0, PEEK32_CMD);
+
+ return this->wait_for_ack(_seq_out);
+ }
+
+ /*******************************************************************
+ * Peek and poke 16 bit not implemented
+ ******************************************************************/
+ void poke16(wb_addr_type, boost::uint16_t){
+ throw uhd::not_implemented_error("poke16 not implemented in fifo ctrl module");
+ }
+
+ boost::uint16_t peek16(wb_addr_type){
+ throw uhd::not_implemented_error("peek16 not implemented in fifo ctrl module");
+ }
+
+ /*******************************************************************
+ * FIFO controlled SPI implementation
+ ******************************************************************/
+ void init_spi(void){
+ boost::mutex::scoped_lock lock(_mutex);
+
+ this->send_pkt(SPI_DIV, SPI_DIVIDER, POKE32_CMD);
+ this->wait_for_ack(_seq_out-MAX_SEQS_OUT);
+
+ _ctrl_word_cache = 0; // force update first time around
+ }
+
+ boost::uint32_t transact_spi(
+ int which_slave,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits,
+ bool readback
+ ){
+ boost::mutex::scoped_lock lock(_mutex);
+
+ //load control word
+ boost::uint32_t ctrl_word = 0;
+ ctrl_word |= ((which_slave & 0xffffff) << 0);
+ ctrl_word |= ((num_bits & 0x3ff) << 24);
+ if (config.mosi_edge == spi_config_t::EDGE_FALL) ctrl_word |= (1 << 31);
+ if (config.miso_edge == spi_config_t::EDGE_RISE) ctrl_word |= (1 << 30);
+
+ //load data word (must be in upper bits)
+ const boost::uint32_t data_out = data << (32 - num_bits);
+
+ //conditionally send control word
+ if (_ctrl_word_cache != ctrl_word){
+ this->send_pkt(SPI_CTRL, ctrl_word, POKE32_CMD);
+ this->wait_for_ack(_seq_out-MAX_SEQS_OUT);
+ _ctrl_word_cache = ctrl_word;
+ }
+
+ //send data word
+ this->send_pkt(SPI_DATA, data_out, POKE32_CMD);
+ this->wait_for_ack(_seq_out-MAX_SEQS_OUT);
+
+ //conditional readback
+ if (readback){
+ this->send_pkt(_config.spi_rb, 0, PEEK32_CMD);
+ return this->wait_for_ack(_seq_out);
+ }
+
+ return 0;
+ }
+
+ /*******************************************************************
+ * Update methods for time
+ ******************************************************************/
+ void set_time(const uhd::time_spec_t &time){
+ boost::mutex::scoped_lock lock(_mutex);
+ _time = time;
+ _use_time = _time != uhd::time_spec_t(0.0);
+ if (_use_time) _timeout = MASSIVE_TIMEOUT; //permanently sets larger timeout
+ }
+
+ void set_tick_rate(const double rate){
+ boost::mutex::scoped_lock lock(_mutex);
+ _tick_rate = rate;
+ }
+
+private:
+
+ /*******************************************************************
+ * Primary control and interaction private methods
+ ******************************************************************/
+ UHD_INLINE void send_pkt(wb_addr_type addr, boost::uint32_t data, int cmd){
+ managed_send_buffer::sptr buff = _xport->get_send_buff();
+ if (not buff){
+ throw uhd::runtime_error("fifo ctrl timed out getting a send buffer");
+ }
+ boost::uint32_t *pkt = buff->cast<boost::uint32_t *>();
+
+ //load packet info
+ vrt::if_packet_info_t packet_info;
+ packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_CONTEXT;
+ packet_info.num_payload_words32 = 2;
+ packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t);
+ packet_info.packet_count = ++_seq_out;
+ packet_info.tsf = _time.to_ticks(_tick_rate);
+ packet_info.sob = false;
+ packet_info.eob = false;
+ packet_info.has_sid = false;
+ packet_info.has_cid = false;
+ packet_info.has_tsi = false;
+ packet_info.has_tsf = _use_time;
+ packet_info.has_tlr = false;
+
+ //load header
+ vrt::if_hdr_pack_le(pkt, packet_info);
+
+ //load payload
+ const boost::uint32_t ctrl_word = (addr/4 & 0xff) | cmd | (_seq_out << 16);
+ pkt[packet_info.num_header_words32+0] = uhd::htowx(ctrl_word);
+ pkt[packet_info.num_header_words32+1] = uhd::htowx(data);
+
+ //send the buffer over the interface
+ buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32));
+ }
+
+ UHD_INLINE bool wraparound_lt16(const boost::int16_t i0, const boost::int16_t i1){
+ if (((i0 ^ i1) & 0x8000) == 0) //same sign bits
+ return boost::uint16_t(i0) < boost::uint16_t(i1);
+ return boost::int16_t(i1 - i0) > 0;
+ }
+
+ UHD_INLINE boost::uint32_t wait_for_ack(const boost::uint16_t seq_to_ack){
+
+ while (wraparound_lt16(_seq_ack, seq_to_ack)){
+ ctrl_result_t res = ctrl_result_t();
+ if (not _ctrl_fifo.pop_with_timed_wait(res, _timeout)){
+ throw uhd::runtime_error("fifo ctrl timed out looking for acks");
+ }
+ _seq_ack = res.msg[0] >> 16;
+ if (_seq_ack == seq_to_ack) return res.msg[1];
+ }
+
+ return 0;
+ }
+
+ zero_copy_if::sptr _xport;
+ const fifo_ctrl_excelsior_config _config;
+ boost::mutex _mutex;
+ boost::uint16_t _seq_out;
+ boost::uint16_t _seq_ack;
+ uhd::time_spec_t _time;
+ bool _use_time;
+ double _tick_rate;
+ double _timeout;
+ boost::uint32_t _ctrl_word_cache;
+ bounded_buffer<async_metadata_t> _async_fifo;
+ bounded_buffer<ctrl_result_t> _ctrl_fifo;
+ task::sptr _msg_task;
+};
+
+
+fifo_ctrl_excelsior::sptr fifo_ctrl_excelsior::make(zero_copy_if::sptr xport, const fifo_ctrl_excelsior_config &config)
+{
+ return sptr(new fifo_ctrl_excelsior_impl(xport, config));
+}
diff --git a/host/lib/usrp/common/fifo_ctrl_excelsior.hpp b/host/lib/usrp/common/fifo_ctrl_excelsior.hpp
new file mode 100644
index 000000000..c3ef65a2c
--- /dev/null
+++ b/host/lib/usrp/common/fifo_ctrl_excelsior.hpp
@@ -0,0 +1,63 @@
+//
+// Copyright 2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_B200_CTRL_HPP
+#define INCLUDED_B200_CTRL_HPP
+
+#include <uhd/types/time_spec.hpp>
+#include <uhd/types/metadata.hpp>
+#include <uhd/types/serial.hpp>
+#include <uhd/transport/zero_copy.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include "wb_iface.hpp"
+#include <string>
+
+
+struct fifo_ctrl_excelsior_config
+{
+ size_t async_sid_base;
+ size_t num_async_chan;
+ size_t ctrl_sid_base;
+ size_t spi_base;
+ size_t spi_rb;
+};
+
+/*!
+ * Provide access to peek, poke, spi, and async messages.
+ */
+class fifo_ctrl_excelsior : public wb_iface, public uhd::spi_iface{
+public:
+ typedef boost::shared_ptr<fifo_ctrl_excelsior> sptr;
+
+ //! Make a new control object
+ static sptr make(
+ uhd::transport::zero_copy_if::sptr xport,
+ const fifo_ctrl_excelsior_config &config
+ );
+
+ //! Set the command time that will activate
+ virtual void set_time(const uhd::time_spec_t &time) = 0;
+
+ //! Set the tick rate (converting time into ticks)
+ virtual void set_tick_rate(const double rate) = 0;
+
+ //! Pop an async message from the queue or timeout
+ virtual bool pop_async_msg(uhd::async_metadata_t &async_metadata, double timeout) = 0;
+};
+
+#endif /* INCLUDED_B200_CTRL_HPP */
diff --git a/host/lib/usrp/common/fx2_ctrl.cpp b/host/lib/usrp/common/fx2_ctrl.cpp
new file mode 100644
index 000000000..6111efea9
--- /dev/null
+++ b/host/lib/usrp/common/fx2_ctrl.cpp
@@ -0,0 +1,469 @@
+//
+// Copyright 2010-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "fx2_ctrl.hpp"
+#include <uhd/utils/msg.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/transport/usb_control.hpp>
+#include <boost/functional/hash.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/cstdint.hpp>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <cstring>
+
+using namespace uhd;
+using namespace uhd::usrp;
+
+#define FX2_FIRMWARE_LOAD 0xa0
+
+static const bool load_img_msg = true;
+
+typedef boost::uint32_t hash_type;
+
+/***********************************************************************
+ * Helper Functions
+ **********************************************************************/
+/*!
+ * Create a file hash
+ * The hash will be used to identify the loaded firmware and fpga image
+ * \param filename file used to generate hash value
+ * \return hash value in a size_t type
+ */
+static hash_type generate_hash(const char *filename)
+{
+ std::ifstream file(filename);
+ if (not file){
+ throw uhd::io_error(std::string("cannot open input file ") + filename);
+ }
+
+ size_t hash = 0;
+
+ char ch;
+ while (file.get(ch)) {
+ boost::hash_combine(hash, ch);
+ }
+
+ if (not file.eof()){
+ throw uhd::io_error(std::string("file error ") + filename);
+ }
+
+ file.close();
+ return hash_type(hash);
+}
+
+
+/*!
+ * Verify checksum of a Intel HEX record
+ * \param record a line from an Intel HEX file
+ * \return true if record is valid, false otherwise
+ */
+static bool checksum(std::string *record)
+{
+
+ size_t len = record->length();
+ unsigned int i;
+ unsigned char sum = 0;
+ unsigned int val;
+
+ for (i = 1; i < len; i += 2) {
+ std::istringstream(record->substr(i, 2)) >> std::hex >> val;
+ sum += val;
+ }
+
+ if (sum == 0)
+ return true;
+ else
+ return false;
+}
+
+
+/*!
+ * Parse Intel HEX record
+ *
+ * \param record a line from an Intel HEX file
+ * \param len output length of record
+ * \param addr output address
+ * \param type output type
+ * \param data output data
+ * \return true if record is sucessfully read, false on error
+ */
+bool parse_record(std::string *record, unsigned int &len,
+ unsigned int &addr, unsigned int &type,
+ unsigned char* data)
+{
+ unsigned int i;
+ std::string _data;
+ unsigned int val;
+
+ if (record->substr(0, 1) != ":")
+ return false;
+
+ std::istringstream(record->substr(1, 2)) >> std::hex >> len;
+ std::istringstream(record->substr(3, 4)) >> std::hex >> addr;
+ std::istringstream(record->substr(7, 2)) >> std::hex >> type;
+
+ for (i = 0; i < len; i++) {
+ std::istringstream(record->substr(9 + 2 * i, 2)) >> std::hex >> val;
+ data[i] = (unsigned char) val;
+ }
+
+ return true;
+}
+
+
+/*!
+ * USRP control implementation for device discovery and configuration
+ */
+class fx2_ctrl_impl : public fx2_ctrl {
+public:
+ fx2_ctrl_impl(uhd::transport::usb_control::sptr ctrl_transport)
+ {
+ _ctrl_transport = ctrl_transport;
+ }
+
+ void usrp_fx2_reset(void){
+ unsigned char reset_y = 1;
+ unsigned char reset_n = 0;
+ usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0, &reset_y, 1);
+ usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0, &reset_n, 1);
+ //wait for things to settle
+ boost::this_thread::sleep(boost::posix_time::milliseconds(2000));
+ }
+
+ void usrp_load_firmware(std::string filestring, bool force)
+ {
+ const char *filename = filestring.c_str();
+
+ hash_type hash = generate_hash(filename);
+
+ hash_type loaded_hash; usrp_get_firmware_hash(loaded_hash);
+
+ if (not force and (hash == loaded_hash)) return;
+
+ //FIXME: verify types
+ unsigned int len;
+ unsigned int addr;
+ unsigned int type;
+ unsigned char data[512];
+
+ std::ifstream file;
+ file.open(filename, std::ifstream::in);
+
+ if (!file.good()) {
+ throw uhd::io_error("usrp_load_firmware: cannot open firmware input file");
+ }
+
+ unsigned char reset_y = 1;
+ unsigned char reset_n = 0;
+
+ //hit the reset line
+ if (load_img_msg) UHD_MSG(status) << "Loading firmware image: " << filestring << "..." << std::flush;
+ usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0, &reset_y, 1);
+
+ while (!file.eof()) {
+ std::string record;
+ file >> record;
+
+ //check for valid record
+ if (not checksum(&record) or not parse_record(&record, len, addr, type, data)) {
+ throw uhd::io_error("usrp_load_firmware: bad record checksum");
+ }
+
+ //type 0x00 is data
+ if (type == 0x00) {
+ int ret = usrp_control_write(FX2_FIRMWARE_LOAD, addr, 0, data, len);
+ if (ret < 0) throw uhd::io_error("usrp_load_firmware: usrp_control_write failed");
+ }
+ //type 0x01 is end
+ else if (type == 0x01) {
+ usrp_set_firmware_hash(hash); //set hash before reset
+ usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0, &reset_n, 1);
+ file.close();
+
+ //wait for things to settle
+ boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
+ if (load_img_msg) UHD_MSG(status) << " done" << std::endl;
+ return;
+ }
+ //type anything else is unhandled
+ else {
+ throw uhd::io_error("usrp_load_firmware: unsupported record");
+ }
+ }
+
+ //file did not end
+ throw uhd::io_error("usrp_load_firmware: bad record");
+ }
+
+ void usrp_init(void){
+ //disable
+ usrp_rx_enable(false);
+ usrp_tx_enable(false);
+
+ //toggle resets
+ usrp_rx_reset(true);
+ usrp_tx_reset(true);
+ usrp_rx_reset(false);
+ usrp_tx_reset(false);
+ }
+
+ void usrp_load_fpga(std::string filestring)
+ {
+ const char *filename = filestring.c_str();
+
+ hash_type hash = generate_hash(filename);
+
+ hash_type loaded_hash; usrp_get_fpga_hash(loaded_hash);
+
+ if (hash == loaded_hash) return;
+ const int ep0_size = 64;
+ unsigned char buf[ep0_size];
+
+ if (load_img_msg) UHD_MSG(status) << "Loading FPGA image: " << filestring << "..." << std::flush;
+ std::ifstream file;
+ file.open(filename, std::ios::in | std::ios::binary);
+ if (not file.good()) {
+ throw uhd::io_error("usrp_load_fpga: cannot open fpga input file");
+ }
+
+ usrp_fpga_reset(true); //holding the fpga in reset while loading
+
+ if (usrp_control_write_cmd(VRQ_FPGA_LOAD, 0, FL_BEGIN) < 0) {
+ throw uhd::io_error("usrp_load_fpga: fpga load error");
+ }
+
+ while (not file.eof()) {
+ file.read((char *)buf, sizeof(buf));
+ const std::streamsize n = file.gcount();
+ if(n == 0) continue;
+ int ret = usrp_control_write(VRQ_FPGA_LOAD, 0, FL_XFER, buf, boost::uint16_t(n));
+ if (ret < 0 or std::streamsize(ret) != n) {
+ throw uhd::io_error("usrp_load_fpga: fpga load error");
+ }
+ }
+
+ if (usrp_control_write_cmd(VRQ_FPGA_LOAD, 0, FL_END) < 0) {
+ throw uhd::io_error("usrp_load_fpga: fpga load error");
+ }
+
+ usrp_set_fpga_hash(hash);
+
+ usrp_fpga_reset(false); //done loading, take fpga out of reset
+
+ file.close();
+ if (load_img_msg) UHD_MSG(status) << " done" << std::endl;
+ }
+
+ void usrp_load_eeprom(std::string filestring)
+ {
+ if (load_img_msg) UHD_MSG(status) << "Loading EEPROM image: " << filestring << "..." << std::flush;
+ const char *filename = filestring.c_str();
+ const boost::uint16_t i2c_addr = 0x50;
+
+ unsigned int addr;
+ unsigned char data[256];
+ unsigned char sendbuf[17];
+
+ std::ifstream file;
+ file.open(filename, std::ifstream::in);
+
+ if (not file.good()) {
+ throw uhd::io_error("usrp_load_eeprom: cannot open EEPROM input file");
+ }
+
+ file.read((char *)data, 256);
+ std::streamsize len = file.gcount();
+
+ if(len == 256) {
+ throw uhd::io_error("usrp_load_eeprom: image size too large");
+ }
+
+ const int pagesize = 16;
+ addr = 0;
+ while(len > 0) {
+ sendbuf[0] = addr;
+ memcpy(sendbuf+1, &data[addr], len > pagesize ? pagesize : size_t(len));
+ int ret = usrp_i2c_write(i2c_addr, sendbuf, (len > pagesize ? pagesize : size_t(len))+1);
+ if (ret < 0) {
+ throw uhd::io_error("usrp_load_eeprom: usrp_i2c_write failed");
+ }
+ addr += pagesize;
+ len -= pagesize;
+ boost::this_thread::sleep(boost::posix_time::milliseconds(100));
+ }
+ file.close();
+ if (load_img_msg) UHD_MSG(status) << " done" << std::endl;
+ }
+
+
+ void usrp_set_led(int led_num, bool on)
+ {
+ UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_SET_LED, on, led_num) >= 0);
+ }
+
+
+ void usrp_get_firmware_hash(hash_type &hash)
+ {
+ UHD_ASSERT_THROW(usrp_control_read(0xa0, USRP_HASH_SLOT_0_ADDR, 0,
+ (unsigned char*) &hash, sizeof(hash)) >= 0);
+ }
+
+
+ void usrp_set_firmware_hash(hash_type hash)
+ {
+ UHD_ASSERT_THROW(usrp_control_write(0xa0, USRP_HASH_SLOT_0_ADDR, 0,
+ (unsigned char*) &hash, sizeof(hash)) >= 0);
+
+ }
+
+
+ void usrp_get_fpga_hash(hash_type &hash)
+ {
+ UHD_ASSERT_THROW(usrp_control_read(0xa0, USRP_HASH_SLOT_1_ADDR, 0,
+ (unsigned char*) &hash, sizeof(hash)) >= 0);
+ }
+
+
+ void usrp_set_fpga_hash(hash_type hash)
+ {
+ UHD_ASSERT_THROW(usrp_control_write(0xa0, USRP_HASH_SLOT_1_ADDR, 0,
+ (unsigned char*) &hash, sizeof(hash)) >= 0);
+ }
+
+ void usrp_tx_enable(bool on)
+ {
+ UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_FPGA_SET_TX_ENABLE, on, 0) >= 0);
+ }
+
+
+ void usrp_rx_enable(bool on)
+ {
+ UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_FPGA_SET_RX_ENABLE, on, 0) >= 0);
+ }
+
+
+ void usrp_tx_reset(bool on)
+ {
+ UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_FPGA_SET_TX_RESET, on, 0) >= 0);
+ }
+
+
+ void usrp_rx_reset(bool on)
+ {
+ UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_FPGA_SET_RX_RESET, on, 0) >= 0);
+ }
+
+ void usrp_fpga_reset(bool on)
+ {
+ UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_FPGA_SET_RESET, on, 0) >= 0);
+ }
+
+ int usrp_control_write(boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length)
+ {
+ return _ctrl_transport->submit(VRT_VENDOR_OUT, // bmReqeustType
+ request, // bRequest
+ value, // wValue
+ index, // wIndex
+ buff, // data
+ length); // wLength
+ }
+
+
+ int usrp_control_read(boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length)
+ {
+ return _ctrl_transport->submit(VRT_VENDOR_IN, // bmReqeustType
+ request, // bRequest
+ value, // wValue
+ index, // wIndex
+ buff, // data
+ length); // wLength
+ }
+
+
+ int usrp_control_write_cmd(boost::uint8_t request, boost::uint16_t value, boost::uint16_t index)
+ {
+ return usrp_control_write(request, value, index, 0, 0);
+ }
+
+ byte_vector_t read_eeprom(
+ boost::uint16_t addr,
+ boost::uint16_t offset,
+ size_t num_bytes
+ ){
+ this->write_i2c(addr, byte_vector_t(1, offset));
+ return this->read_i2c(addr, num_bytes);
+ }
+
+ int usrp_i2c_write(boost::uint16_t i2c_addr, unsigned char *buf, boost::uint16_t len)
+ {
+ return usrp_control_write(VRQ_I2C_WRITE, i2c_addr, 0, buf, len);
+ }
+
+ int usrp_i2c_read(boost::uint16_t i2c_addr, unsigned char *buf, boost::uint16_t len)
+ {
+ return usrp_control_read(VRQ_I2C_READ, i2c_addr, 0, buf, len);
+ }
+
+ static const bool iface_debug = false;
+ static const size_t max_i2c_data_bytes = 64;
+
+ void write_i2c(boost::uint16_t addr, const byte_vector_t &bytes)
+ {
+ UHD_ASSERT_THROW(bytes.size() < max_i2c_data_bytes);
+
+ int ret = this->usrp_i2c_write(addr, (unsigned char *)&bytes.front(), bytes.size());
+
+ if (iface_debug && (ret < 0))
+ uhd::runtime_error("USRP: failed i2c write");
+ }
+
+ byte_vector_t read_i2c(boost::uint16_t addr, size_t num_bytes)
+ {
+ UHD_ASSERT_THROW(num_bytes < max_i2c_data_bytes);
+
+ byte_vector_t bytes(num_bytes);
+ int ret = this->usrp_i2c_read(addr, (unsigned char *)&bytes.front(), num_bytes);
+
+ if (iface_debug && ((ret < 0) || (unsigned)ret < (num_bytes)))
+ uhd::runtime_error("USRP: failed i2c read");
+
+ return bytes;
+ }
+
+
+private:
+ uhd::transport::usb_control::sptr _ctrl_transport;
+};
+
+/***********************************************************************
+ * Public make function for fx2_ctrl interface
+ **********************************************************************/
+fx2_ctrl::sptr fx2_ctrl::make(uhd::transport::usb_control::sptr ctrl_transport){
+ return sptr(new fx2_ctrl_impl(ctrl_transport));
+}
+
diff --git a/host/lib/usrp/common/fx2_ctrl.hpp b/host/lib/usrp/common/fx2_ctrl.hpp
new file mode 100644
index 000000000..9f8cec296
--- /dev/null
+++ b/host/lib/usrp/common/fx2_ctrl.hpp
@@ -0,0 +1,146 @@
+//
+// Copyright 2010-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_COMMON_FX2_CTRL_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_FX2_CTRL_HPP
+
+#include <uhd/transport/usb_control.hpp>
+#include <uhd/types/serial.hpp> //i2c iface
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+
+#define FL_BEGIN 0
+#define FL_END 2
+#define FL_XFER 1
+#define USRP_HASH_SLOT_0_ADDR 0xe1e0
+#define USRP_HASH_SLOT_1_ADDR 0xe1f0
+#define VRQ_FPGA_LOAD 0x02
+#define VRQ_FPGA_SET_RESET 0x04
+#define VRQ_FPGA_SET_TX_ENABLE 0x05
+#define VRQ_FPGA_SET_RX_ENABLE 0x06
+#define VRQ_FPGA_SET_TX_RESET 0x0a
+#define VRQ_FPGA_SET_RX_RESET 0x0b
+#define VRQ_I2C_READ 0x81
+#define VRQ_I2C_WRITE 0x08
+#define VRQ_SET_LED 0x01
+#define VRT_VENDOR_IN 0xC0
+#define VRT_VENDOR_OUT 0x40
+
+namespace uhd{ namespace usrp{
+
+class fx2_ctrl : boost::noncopyable, public uhd::i2c_iface{
+public:
+ typedef boost::shared_ptr<fx2_ctrl> sptr;
+
+ /*!
+ * Make a usrp control object from a control transport
+ * \param ctrl_transport a USB control transport
+ * \return a new usrp control object
+ */
+ static sptr make(uhd::transport::usb_control::sptr ctrl_transport);
+
+ //! Call init after the fpga is loaded
+ virtual void usrp_init(void) = 0;
+
+ //! For emergency situations
+ virtual void usrp_fx2_reset(void) = 0;
+
+ /*!
+ * Load firmware in Intel HEX Format onto device
+ * \param filename name of firmware file
+ * \param force reload firmware if already loaded
+ */
+ virtual void usrp_load_firmware(std::string filename,
+ bool force = false) = 0;
+
+ /*!
+ * Load fpga file onto usrp
+ * \param filename name of fpga image
+ */
+ virtual void usrp_load_fpga(std::string filename) = 0;
+
+ /*!
+ * Load USB descriptor file in Intel HEX format into EEPROM
+ * \param filename name of EEPROM image
+ */
+ virtual void usrp_load_eeprom(std::string filestring) = 0;
+
+ /*!
+ * Submit an IN transfer
+ * \param request device specific request
+ * \param value device specific field
+ * \param index device specific field
+ * \param buff buffer to place data
+ * \return number of bytes read or error
+ */
+ virtual int usrp_control_read(boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length) = 0;
+
+ /*!
+ * Submit an OUT transfer
+ * \param request device specific request
+ * \param value device specific field
+ * \param index device specific field
+ * \param buff buffer of data to be sent
+ * \return number of bytes written or error
+ */
+ virtual int usrp_control_write(boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length) = 0;
+
+ /*!
+ * Perform an I2C write
+ * \param i2c_addr I2C device address
+ * \param buf data to be written
+ * \param len length of data in bytes
+ * \return number of bytes written or error
+ */
+
+ virtual int usrp_i2c_write(boost::uint16_t i2c_addr,
+ unsigned char *buf,
+ boost::uint16_t len) = 0;
+
+ /*!
+ * Perform an I2C read
+ * \param i2c_addr I2C device address
+ * \param buf data to be read
+ * \param len length of data in bytes
+ * \return number of bytes read or error
+ */
+
+ virtual int usrp_i2c_read(boost::uint16_t i2c_addr,
+ unsigned char *buf,
+ boost::uint16_t len) = 0;
+
+ //! enable/disable the rx path
+ virtual void usrp_rx_enable(bool on) = 0;
+
+ //! enable/disable the tx path
+ virtual void usrp_tx_enable(bool on) = 0;
+
+ //! reset the fpga
+ virtual void usrp_fpga_reset(bool on) = 0;
+};
+
+}} //namespace uhd::usrp
+
+#endif /* INCLUDED_LIBUHD_USRP_COMMON_FX2_CTRL_HPP */
diff --git a/host/lib/usrp/common/recv_packet_demuxer.cpp b/host/lib/usrp/common/recv_packet_demuxer.cpp
new file mode 100644
index 000000000..fe606213c
--- /dev/null
+++ b/host/lib/usrp/common/recv_packet_demuxer.cpp
@@ -0,0 +1,115 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "recv_packet_demuxer.hpp"
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <boost/thread/mutex.hpp>
+#include <uhd/transport/vrt_if_packet.hpp>
+#include <uhd/types/metadata.hpp>
+#include <queue>
+#include <deque>
+#include <vector>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+struct recv_pkt_demux_mrb : public managed_recv_buffer
+{
+public:
+ recv_pkt_demux_mrb(void){/*NOP*/}
+
+ void release(void)
+ {
+ delete this;
+ }
+
+ boost::uint32_t buff[10];
+};
+
+static UHD_INLINE boost::uint32_t extract_sid(managed_recv_buffer::sptr &buff){
+ //ASSUME that the data is in little endian format
+ return uhd::wtohx(buff->cast<const boost::uint32_t *>()[1]);
+}
+
+class recv_packet_demuxer_impl : public uhd::usrp::recv_packet_demuxer{
+public:
+ recv_packet_demuxer_impl(
+ transport::zero_copy_if::sptr transport,
+ const size_t size,
+ const boost::uint32_t sid_base
+ ):
+ _transport(transport), _sid_base(sid_base), _queues(size)
+ {
+ /* NOP */
+ }
+
+ managed_recv_buffer::sptr get_recv_buff(const size_t index, const double timeout){
+ boost::mutex::scoped_lock lock(_mutex);
+ managed_recv_buffer::sptr buff;
+
+ //there is already an entry in the queue, so pop that
+ if (not _queues[index].wrapper.empty()){
+ std::swap(buff, _queues[index].wrapper.front());
+ _queues[index].wrapper.pop();
+ return buff;
+ }
+
+ while (true){
+ //otherwise call into the transport
+ buff = _transport->get_recv_buff(timeout);
+ if (buff.get() == NULL) return buff; //timeout
+
+ //check the stream id to know which channel
+ const size_t rx_index = extract_sid(buff) - _sid_base;
+ if (rx_index == index) return buff; //got expected message
+
+ //otherwise queue and try again
+ if (rx_index < _queues.size()) _queues[rx_index].wrapper.push(buff);
+ else
+ {
+ UHD_MSG(error) << "Got a data packet with unknown SID " << extract_sid(buff) << std::endl;
+ recv_pkt_demux_mrb *mrb = new recv_pkt_demux_mrb();
+ vrt::if_packet_info_t info;
+ info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA;
+ info.num_payload_words32 = 1;
+ info.num_payload_bytes = info.num_payload_words32*sizeof(boost::uint32_t);
+ info.has_sid = true;
+ info.sid = _sid_base + index;
+ vrt::if_hdr_pack_le(mrb->buff, info);
+ mrb->buff[info.num_header_words32] = rx_metadata_t::ERROR_CODE_OVERFLOW;
+ return mrb->make(mrb, mrb->buff, info.num_packet_words32*sizeof(boost::uint32_t));
+ }
+ }
+ }
+
+private:
+ transport::zero_copy_if::sptr _transport;
+ const boost::uint32_t _sid_base;
+ boost::mutex _mutex;
+ struct channel_guts_type{
+ channel_guts_type(void): wrapper(container){}
+ std::deque<managed_recv_buffer::sptr> container;
+ std::queue<managed_recv_buffer::sptr> wrapper;
+ };
+ std::vector<channel_guts_type> _queues;
+};
+
+recv_packet_demuxer::sptr recv_packet_demuxer::make(transport::zero_copy_if::sptr transport, const size_t size, const boost::uint32_t sid_base){
+ return sptr(new recv_packet_demuxer_impl(transport, size, sid_base));
+}
diff --git a/host/lib/usrp/common/recv_packet_demuxer.hpp b/host/lib/usrp/common/recv_packet_demuxer.hpp
new file mode 100644
index 000000000..fde756d27
--- /dev/null
+++ b/host/lib/usrp/common/recv_packet_demuxer.hpp
@@ -0,0 +1,41 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/transport/zero_copy.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/cstdint.hpp>
+
+namespace uhd{ namespace usrp{
+
+ class recv_packet_demuxer{
+ public:
+ typedef boost::shared_ptr<recv_packet_demuxer> sptr;
+
+ //! Make a new demuxer from a transport and parameters
+ static sptr make(transport::zero_copy_if::sptr transport, const size_t size, const boost::uint32_t sid_base);
+
+ //! Get a buffer at the given index from the transport
+ virtual transport::managed_recv_buffer::sptr get_recv_buff(const size_t index, const double timeout) = 0;
+ };
+
+}} //namespace uhd::usrp
+
+#endif /* INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_HPP */
diff --git a/host/lib/usrp/common/recv_packet_demuxer_3000.hpp b/host/lib/usrp/common/recv_packet_demuxer_3000.hpp
new file mode 100644
index 000000000..4fb6c4604
--- /dev/null
+++ b/host/lib/usrp/common/recv_packet_demuxer_3000.hpp
@@ -0,0 +1,127 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_3000_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_3000_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/transport/zero_copy.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/thread.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/atomic.hpp>
+#include <uhd/types/time_spec.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <queue>
+#include <map>
+
+namespace uhd{ namespace usrp{
+
+ struct recv_packet_demuxer_3000
+ {
+ recv_packet_demuxer_3000(transport::zero_copy_if::sptr xport):
+ _xport(xport)
+ {/*NOP*/}
+
+ transport::managed_recv_buffer::sptr get_recv_buff(const boost::uint32_t sid, const double timeout)
+ {
+ const time_spec_t exit_time = time_spec_t(timeout) + time_spec_t::get_system_time();
+ transport::managed_recv_buffer::sptr buff;
+ buff = _internal_get_recv_buff(sid, timeout);
+ while (not buff) //loop until timeout
+ {
+ const time_spec_t delta = exit_time - time_spec_t::get_system_time();
+ const double new_timeout = delta.get_real_secs();
+ if (new_timeout < 0.0) break;
+ buff = _internal_get_recv_buff(sid, new_timeout);
+ }
+ return buff;
+ }
+
+ transport::managed_recv_buffer::sptr _internal_get_recv_buff(const boost::uint32_t sid, const double timeout)
+ {
+ transport::managed_recv_buffer::sptr buff;
+
+ //----------------------------------------------------------
+ //-- Check the queue to see if we already have a buffer
+ //----------------------------------------------------------
+ {
+ boost::mutex::scoped_lock l(mutex);
+ queue_type_t &queue = _queues[sid];
+ if (not queue.empty())
+ {
+ buff = queue.front();
+ queue.front().reset();
+ queue.pop();
+ return buff;
+ }
+ }
+
+ //----------------------------------------------------------
+ //-- Try to claim the transport or wait patiently
+ //----------------------------------------------------------
+ if (_claimed.cas(1, 0))
+ {
+ boost::mutex::scoped_lock l(mutex);
+ cond.timed_wait(l, boost::posix_time::microseconds(long(timeout*1e6)));
+ }
+
+ //----------------------------------------------------------
+ //-- Wait on the transport for input buffers
+ //----------------------------------------------------------
+ else
+ {
+ buff = _xport->get_recv_buff(timeout);
+ if (buff)
+ {
+ const boost::uint32_t new_sid = uhd::wtohx(buff->cast<const boost::uint32_t *>()[1]);
+ if (new_sid != sid)
+ {
+ boost::mutex::scoped_lock l(mutex);
+ if (_queues.count(new_sid) == 0) UHD_MSG(error)
+ << "recv packet demuxer unexpected sid 0x" << std::hex << new_sid << std::dec
+ << std::endl;
+ else _queues[new_sid].push(buff);
+ buff.reset();
+ }
+ }
+ _claimed.write(0);
+ cond.notify_all();
+ }
+ return buff;
+ }
+
+ void realloc_sid(const boost::uint32_t sid)
+ {
+ boost::mutex::scoped_lock l(mutex);
+ while(not _queues[sid].empty()) //allocated and clears if already allocated
+ {
+ _queues[sid].pop();
+ }
+ }
+
+ typedef std::queue<transport::managed_recv_buffer::sptr> queue_type_t;
+ std::map<boost::uint32_t, queue_type_t> _queues;
+ transport::zero_copy_if::sptr _xport;
+ uhd::atomic_uint32_t _claimed;
+ boost::condition_variable cond;
+ boost::mutex mutex;
+ };
+
+}} //namespace uhd::usrp
+
+#endif /* INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_3000_HPP */
diff --git a/host/lib/usrp/common/validate_subdev_spec.cpp b/host/lib/usrp/common/validate_subdev_spec.cpp
new file mode 100644
index 000000000..fab40b204
--- /dev/null
+++ b/host/lib/usrp/common/validate_subdev_spec.cpp
@@ -0,0 +1,73 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "validate_subdev_spec.hpp"
+#include <uhd/exception.hpp>
+#include <uhd/utils/assert_has.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+
+namespace uhd{ namespace usrp{
+
+ static std::ostream& operator<< (std::ostream &out, const subdev_spec_pair_t &pair){
+ out << pair.db_name << ":" << pair.sd_name;
+ return out;
+ }
+
+}}
+
+void uhd::usrp::validate_subdev_spec(
+ property_tree::sptr tree,
+ const subdev_spec_t &spec,
+ const std::string &type,
+ const std::string &mb
+){
+ const size_t num_dsps = tree->list(str(boost::format("/mboards/%s/%s_dsps") % mb % type)).size();
+
+ //sanity checking on the length
+ if (spec.size() == 0) throw uhd::value_error(str(boost::format(
+ "Empty %s subdevice specification is not supported.\n"
+ ) % type));
+ if (spec.size() > num_dsps) throw uhd::value_error(str(boost::format(
+ "The subdevice specification \"%s\" is too long.\n"
+ "The user specified %u channels, but there are only %u %s dsps on mboard %s.\n"
+ ) % spec.to_string() % spec.size() % num_dsps % type % mb));
+
+ //make a list of all possible specs
+ subdev_spec_t all_specs;
+ BOOST_FOREACH(const std::string &db, tree->list(str(boost::format("/mboards/%s/dboards") % mb))){
+ BOOST_FOREACH(const std::string &sd, tree->list(str(boost::format("/mboards/%s/dboards/%s/%s_frontends") % mb % db % type))){
+ all_specs.push_back(subdev_spec_pair_t(db, sd));
+ }
+ }
+
+ //validate that the spec is possible
+ BOOST_FOREACH(const subdev_spec_pair_t &pair, spec){
+ uhd::assert_has(all_specs, pair, str(boost::format("%s subdevice specification on mboard %s") % type % mb));
+ }
+
+ //enable selected frontends, disable others
+ BOOST_FOREACH(const subdev_spec_pair_t &pair, all_specs){
+ const bool enb = uhd::has(spec, pair);
+ tree->access<bool>(str(boost::format(
+ "/mboards/%s/dboards/%s/%s_frontends/%s/enabled"
+ ) % mb % pair.db_name % type % pair.sd_name)).set(enb);
+ }
+}
diff --git a/host/lib/usrp/common/validate_subdev_spec.hpp b/host/lib/usrp/common/validate_subdev_spec.hpp
new file mode 100644
index 000000000..7d9e2c309
--- /dev/null
+++ b/host/lib/usrp/common/validate_subdev_spec.hpp
@@ -0,0 +1,38 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_COMMON_VALIDATE_SUBDEV_SPEC_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_VALIDATE_SUBDEV_SPEC_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/usrp/subdev_spec.hpp>
+#include <uhd/property_tree.hpp>
+#include <string>
+
+namespace uhd{ namespace usrp{
+
+ //! Validate a subdev spec against a property tree
+ void validate_subdev_spec(
+ property_tree::sptr tree,
+ const subdev_spec_t &spec,
+ const std::string &type, //rx or tx
+ const std::string &mb = "0"
+ );
+
+}} //namespace uhd::usrp
+
+#endif /* INCLUDED_LIBUHD_USRP_COMMON_VALIDATE_SUBDEV_SPEC_HPP */