diff options
Diffstat (limited to 'host/lib/usrp/common')
-rw-r--r-- | host/lib/usrp/common/CMakeLists.txt | 1 | ||||
-rw-r--r-- | host/lib/usrp/common/ad9361_ctrl.hpp | 6 | ||||
-rw-r--r-- | host/lib/usrp/common/ad9361_driver/ad9361_device.cpp | 5 | ||||
-rw-r--r-- | host/lib/usrp/common/ad9361_driver/ad9361_device.h | 1 | ||||
-rw-r--r-- | host/lib/usrp/common/constrained_device_args.hpp | 283 | ||||
-rw-r--r-- | host/lib/usrp/common/fw_comm_protocol.h | 102 | ||||
-rw-r--r-- | host/lib/usrp/common/usrp3_fw_ctrl_iface.cpp | 246 | ||||
-rw-r--r-- | host/lib/usrp/common/usrp3_fw_ctrl_iface.hpp | 72 |
8 files changed, 712 insertions, 4 deletions
diff --git a/host/lib/usrp/common/CMakeLists.txt b/host/lib/usrp/common/CMakeLists.txt index 9dabc4e0b..270314dcc 100644 --- a/host/lib/usrp/common/CMakeLists.txt +++ b/host/lib/usrp/common/CMakeLists.txt @@ -37,4 +37,5 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/validate_subdev_spec.cpp ${CMAKE_CURRENT_SOURCE_DIR}/recv_packet_demuxer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/fifo_ctrl_excelsior.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/usrp3_fw_ctrl_iface.cpp ) diff --git a/host/lib/usrp/common/ad9361_ctrl.hpp b/host/lib/usrp/common/ad9361_ctrl.hpp index 5c438ee9c..8cd75d539 100644 --- a/host/lib/usrp/common/ad9361_ctrl.hpp +++ b/host/lib/usrp/common/ad9361_ctrl.hpp @@ -89,8 +89,10 @@ public: //! 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, ad9361_device_t::AD9361_MAX_CLOCK_RATE); //5 MHz DCM low end + return uhd::meta_range_t( + ad9361_device_t::AD9361_MIN_CLOCK_RATE, + ad9361_device_t::AD9361_MAX_CLOCK_RATE + ); } //! set the filter bandwidth for the frontend's analog low pass diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp index 0a8a61575..bb25379c0 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp +++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp @@ -91,6 +91,7 @@ int get_num_taps(int max_num_taps) { } const double ad9361_device_t::AD9361_MAX_GAIN = 89.75; +const double ad9361_device_t::AD9361_MIN_CLOCK_RATE = 220e3; const double ad9361_device_t::AD9361_MAX_CLOCK_RATE = 61.44e6; const double ad9361_device_t::AD9361_CAL_VALID_WINDOW = 100e6; // Max bandwdith is due to filter rolloff in analog filter stage @@ -770,7 +771,7 @@ void ad9361_device_t::_calibrate_rf_dc_offset() size_t count = 0; _io_iface->poke8(0x016, 0x02); while (_io_iface->peek8(0x016) & 0x02) { - if (count > 100) { + if (count > 200) { throw uhd::runtime_error("[ad9361_device_t] RF DC Offset Calibration Failure"); break; } @@ -821,7 +822,7 @@ void ad9361_device_t::_calibrate_rx_quadrature() size_t count = 0; _io_iface->poke8(0x016, 0x20); while (_io_iface->peek8(0x016) & 0x20) { - if (count > 100) { + if (count > 1000) { throw uhd::runtime_error("[ad9361_device_t] Rx Quadrature Calibration Failure"); break; } diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.h b/host/lib/usrp/common/ad9361_driver/ad9361_device.h index 66bc2e8b9..73b1d9a35 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_device.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.h @@ -157,6 +157,7 @@ public: //Constants static const double AD9361_MAX_GAIN; static const double AD9361_MAX_CLOCK_RATE; + static const double AD9361_MIN_CLOCK_RATE; static const double AD9361_CAL_VALID_WINDOW; static const double AD9361_RECOMMENDED_MAX_BANDWIDTH; static const double DEFAULT_RX_FREQ; diff --git a/host/lib/usrp/common/constrained_device_args.hpp b/host/lib/usrp/common/constrained_device_args.hpp new file mode 100644 index 000000000..1bfd1df00 --- /dev/null +++ b/host/lib/usrp/common/constrained_device_args.hpp @@ -0,0 +1,283 @@ +// +// Copyright 2014 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_CONSTRAINED_DEV_ARGS_HPP +#define INCLUDED_LIBUHD_USRP_COMMON_CONSTRAINED_DEV_ARGS_HPP + +#include <uhd/types/device_addr.hpp> +#include <uhd/exception.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/format.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/assign/list_of.hpp> +#include <vector> +#include <string> + +namespace uhd { +namespace usrp { + + /*! + * constrained_device_args_t provides a base and utilities to + * map key=value pairs passed in through the device creation + * args interface (device_addr_t). + * + * Inherit from this class to create typed device specific + * arguments and use the base class methods to handle parsing + * the device_addr or any key=value string to populate the args + * + * This file contains a library of different types of args the + * the user can pass in. The library can be extended to support + * non-intrinsic types by the client. + * + */ + class constrained_device_args_t { + public: //Types + + /*! + * Base argument type. All other arguments inherit from this. + */ + class generic_arg { + public: + generic_arg(const std::string& key): _key(key) {} + inline const std::string& key() const { return _key; } + inline virtual std::string to_string() const = 0; + private: + std::string _key; + }; + + /*! + * String argument type. Can be case sensitive or insensitive + */ + template<bool case_sensitive> + class str_arg : public generic_arg { + public: + str_arg(const std::string& name, const std::string& default_value) : + generic_arg(name) { set(default_value); } + + inline void set(const std::string& value) { + _value = case_sensitive ? value : boost::algorithm::to_lower_copy(value); + } + inline const std::string& get() const { + return _value; + } + inline void parse(const std::string& str_rep) { + set(str_rep); + } + inline virtual std::string to_string() const { + return key() + "=" + get(); + } + inline bool operator==(const std::string& rhs) const { + return get() == boost::algorithm::to_lower_copy(rhs); + } + private: + std::string _value; + }; + typedef str_arg<false> str_ci_arg; + typedef str_arg<true> str_cs_arg; + + /*! + * Numeric argument type. The template type data_t allows the + * client to constrain the type of the number. + */ + template<typename data_t> + class num_arg : public generic_arg { + public: + num_arg(const std::string& name, const data_t default_value) : + generic_arg(name) { set(default_value); } + + inline void set(const data_t value) { + _value = value; + } + inline const data_t get() const { + return _value; + } + inline void parse(const std::string& str_rep) { + try { + _value = boost::lexical_cast<data_t>(str_rep); + } catch (std::exception& ex) { + throw uhd::value_error(str(boost::format( + "Error parsing numeric parameter %s: %s.") % + key() % ex.what() + )); + } + } + inline virtual std::string to_string() const { + return key() + "=" + boost::lexical_cast<std::string>(get()); + } + private: + data_t _value; + }; + + /*! + * Enumeration argument type. The template type enum_t allows the + * client to use their own enum and specify a string mapping for + * the values of the enum + * + * NOTE: The constraint on enum_t is that the values must start with + * 0 and be sequential + */ + template<typename enum_t> + class enum_arg : public generic_arg { + public: + enum_arg( + const std::string& name, + const enum_t default_value, + const std::vector<std::string>& values) : + generic_arg(name), _str_values(values) + { set(default_value); } + + inline void set(const enum_t value) { + _value = value; + } + inline const enum_t get() const { + return _value; + } + inline void parse(const std::string& str_rep, bool assert_invalid = true) { + std::string valid_values_str; + for (size_t i = 0; i < _str_values.size(); i++) { + if (boost::algorithm::to_lower_copy(str_rep) == + boost::algorithm::to_lower_copy(_str_values[i])) + { + valid_values_str += ((i==0)?"":", ") + _str_values[i]; + set(static_cast<enum_t>(static_cast<int>(i))); + return; + } + } + //If we reach here then, the string enum value was invalid + if (assert_invalid) { + throw uhd::value_error(str(boost::format( + "Invalid device arg value: %s=%s (Valid: {%s})") % + key() % str_rep % valid_values_str + )); + } + } + inline virtual std::string to_string() const { + size_t index = static_cast<size_t>(static_cast<int>(_value)); + UHD_ASSERT_THROW(index < _str_values.size()); + return key() + "=" + _str_values[index]; + } + + private: + enum_t _value; + std::vector<std::string> _str_values; + }; + + /*! + * Boolean argument type. + */ + class bool_arg : public generic_arg { + public: + bool_arg(const std::string& name, const bool default_value) : + generic_arg(name) { set(default_value); } + + inline void set(const bool value) { + _value = value; + } + inline bool get() const { + return _value; + } + inline void parse(const std::string& str_rep) { + try { + _value = (boost::lexical_cast<int>(str_rep) != 0); + } catch (std::exception& ex) { + if (str_rep.empty()) { + //If str_rep is empty then the device_addr was set + //without a value which means that the user "set" the flag + _value = true; + } else if (boost::algorithm::to_lower_copy(str_rep) == "true" || + boost::algorithm::to_lower_copy(str_rep) == "yes" || + boost::algorithm::to_lower_copy(str_rep) == "y") { + _value = true; + } else if (boost::algorithm::to_lower_copy(str_rep) == "false" || + boost::algorithm::to_lower_copy(str_rep) == "no" || + boost::algorithm::to_lower_copy(str_rep) == "n") { + _value = false; + } else { + throw uhd::value_error(str(boost::format( + "Error parsing boolean parameter %s: %s.") % + key() % ex.what() + )); + } + } + } + inline virtual std::string to_string() const { + return key() + "=" + (get() ? "true" : "false"); + } + private: + bool _value; + }; + + public: //Methods + constrained_device_args_t() {} + virtual ~constrained_device_args_t() {} + + void parse(const std::string& str_args) { + device_addr_t dev_args(str_args); + _parse(dev_args); + } + + void parse(const device_addr_t& dev_args) { + _parse(dev_args); + } + + inline virtual std::string to_string() const = 0; + + protected: //Methods + //Override _parse to provide an implementation to parse all + //client specific device args + virtual void _parse(const device_addr_t& dev_args) = 0; + + /*! + * Utility: Ensure that the value of the device arg is between min and max + */ + template<typename num_data_t> + static inline void _enforce_range(const num_arg<num_data_t>& arg, const num_data_t& min, const num_data_t& max) { + if (arg.get() > max || arg.get() < min) { + throw uhd::value_error(str(boost::format( + "Invalid device arg value: %s (Minimum: %s, Maximum: %s)") % + arg.to_string() % + boost::lexical_cast<std::string>(min) % boost::lexical_cast<std::string>(max))); + } + } + + /*! + * Utility: Ensure that the value of the device arg is is contained in valid_values + */ + template<typename arg_t, typename data_t> + static inline void _enforce_discrete(const arg_t& arg, const std::vector<data_t>& valid_values) { + bool match = false; + BOOST_FOREACH(const data_t& val, valid_values) { + if (val == arg.get()) { + match = true; + break; + } + } + if (!match) { + std::string valid_values_str; + for (size_t i = 0; i < valid_values.size(); i++) { + valid_values_str += ((i==0)?"":", ") + boost::lexical_cast<std::string>(valid_values[i]); + throw uhd::value_error(str(boost::format( + "Invalid device arg value: %s (Valid: {%s})") % + arg.to_string() % valid_values_str + )); + } + } + } + }; +}} //namespaces + +#endif /* INCLUDED_LIBUHD_USRP_COMMON_CONSTRAINED_DEV_ARGS_HPP */ diff --git a/host/lib/usrp/common/fw_comm_protocol.h b/host/lib/usrp/common/fw_comm_protocol.h new file mode 100644 index 000000000..14adb33a9 --- /dev/null +++ b/host/lib/usrp/common/fw_comm_protocol.h @@ -0,0 +1,102 @@ +// +// Copyright 2014 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_FW_COMM_PROTOCOL +#define INCLUDED_FW_COMM_PROTOCOL + +#include <stdint.h> +#ifndef __cplusplus +#include <stdbool.h> +#endif + +/*! + * Structs and constants for communication between firmware and host. + * This header is shared by the firmware and host code. + * Therefore, this header may only contain valid C code. + */ +#ifdef __cplusplus +extern "C" { +#endif + +#define FW_COMM_PROTOCOL_SIGNATURE 0xACE3 +#define FW_COMM_PROTOCOL_VERSION 0 +#define FW_COMM_MAX_DATA_WORDS 16 +#define FW_COMM_PROTOCOL_MTU 256 + +#define FW_COMM_FLAGS_ACK 0x00000001 +#define FW_COMM_FLAGS_CMD_MASK 0x00000FF0 +#define FW_COMM_FLAGS_ERROR_MASK 0xFF000000 + +#define FW_COMM_CMD_ECHO 0x00000000 +#define FW_COMM_CMD_POKE32 0x00000010 +#define FW_COMM_CMD_PEEK32 0x00000020 +#define FW_COMM_CMD_BLOCK_POKE32 0x00000030 +#define FW_COMM_CMD_BLOCK_PEEK32 0x00000040 + +#define FW_COMM_ERR_PKT_ERROR 0x80000000 +#define FW_COMM_ERR_CMD_ERROR 0x40000000 +#define FW_COMM_ERR_SIZE_ERROR 0x20000000 + +#define FW_COMM_GENERATE_ID(prod) ((((uint32_t) FW_COMM_PROTOCOL_SIGNATURE) << 0) | \ + (((uint32_t) prod) << 16) | \ + (((uint32_t) FW_COMM_PROTOCOL_VERSION) << 24)) + +#define FW_COMM_GET_PROTOCOL_SIG(id) ((uint16_t)(id & 0xFFFF)) +#define FW_COMM_GET_PRODUCT_ID(id) ((uint8_t)(id >> 16)) +#define FW_COMM_GET_PROTOCOL_VER(id) ((uint8_t)(id >> 24)) + +typedef struct +{ + uint32_t id; //Protocol and device identifier + uint32_t flags; //Holds commands and ack messages + uint32_t sequence; //Sequence number (specific to FW communication transactions) + uint32_t data_words; //Number of data words in payload + uint32_t addr; //Address field for the command in flags + uint32_t data[FW_COMM_MAX_DATA_WORDS]; //Data field for the command in flags +} fw_comm_pkt_t; + +#ifdef __cplusplus +} //extern "C" +#endif + +// The following definitions are only useful in firmware. Exclude in host code. +#ifndef __cplusplus + +typedef void (*poke32_func)(const uint32_t addr, const uint32_t data); +typedef uint32_t (*peek32_func)(const uint32_t addr); + +/*! + * Process a firmware communication packet and compute a response. + * Args: + * - (in) request: Pointer to the request struct + * - (out) response: Pointer to the response struct + * - (in) product_id: The 8-bit usrp3 specific product ID (for request filtering) + * - (func) poke_callback, peek_callback: Callback functions for a single peek/poke + * - return value: Send a response packet + */ +bool process_fw_comm_protocol_pkt( + const fw_comm_pkt_t* request, + fw_comm_pkt_t* response, + uint8_t product_id, + uint32_t iface_id, + poke32_func poke_callback, + peek32_func peek_callback +); + +#endif //ifdef __cplusplus + +#endif /* INCLUDED_FW_COMM_PROTOCOL */ diff --git a/host/lib/usrp/common/usrp3_fw_ctrl_iface.cpp b/host/lib/usrp/common/usrp3_fw_ctrl_iface.cpp new file mode 100644 index 000000000..ef541e37f --- /dev/null +++ b/host/lib/usrp/common/usrp3_fw_ctrl_iface.cpp @@ -0,0 +1,246 @@ +// +// 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/>. +// + +#include "usrp3_fw_ctrl_iface.hpp" + +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/exception.hpp> +#include <boost/format.hpp> +#include <boost/asio.hpp> //used for htonl and ntohl +#include <boost/foreach.hpp> +#include "fw_comm_protocol.h" + +namespace uhd { namespace usrp { namespace usrp3 { + +//---------------------------------------------------------- +// Factory method +//---------------------------------------------------------- +uhd::wb_iface::sptr usrp3_fw_ctrl_iface::make( + uhd::transport::udp_simple::sptr udp_xport, + const boost::uint16_t product_id, + const bool verbose) +{ + return wb_iface::sptr(new usrp3_fw_ctrl_iface(udp_xport, product_id, verbose)); +} + +//---------------------------------------------------------- +// udp_fw_ctrl_iface +//---------------------------------------------------------- + +usrp3_fw_ctrl_iface::usrp3_fw_ctrl_iface( + uhd::transport::udp_simple::sptr udp_xport, + const boost::uint16_t product_id, + const bool verbose) : + _product_id(product_id), _verbose(verbose), _udp_xport(udp_xport), + _seq_num(0) +{ + flush(); + peek32(0); +} + +usrp3_fw_ctrl_iface::~usrp3_fw_ctrl_iface() +{ + flush(); +} + +void usrp3_fw_ctrl_iface::flush() +{ + boost::mutex::scoped_lock lock(_mutex); + _flush(); +} + +void usrp3_fw_ctrl_iface::poke32(const wb_addr_type addr, const boost::uint32_t data) +{ + boost::mutex::scoped_lock lock(_mutex); + + for (size_t i = 1; i <= NUM_RETRIES; i++) { + try { + _poke32(addr, data); + return; + } catch(const std::exception &ex) { + const std::string error_msg = str(boost::format( + "udp fw poke32 failure #%u\n%s") % i % ex.what()); + if (_verbose) UHD_MSG(warning) << error_msg << std::endl; + if (i == NUM_RETRIES) throw uhd::io_error(error_msg); + } + } +} + +boost::uint32_t usrp3_fw_ctrl_iface::peek32(const wb_addr_type addr) +{ + boost::mutex::scoped_lock lock(_mutex); + + for (size_t i = 1; i <= NUM_RETRIES; i++) { + try { + return _peek32(addr); + } catch(const std::exception &ex) { + const std::string error_msg = str(boost::format( + "udp fw peek32 failure #%u\n%s") % i % ex.what()); + if (_verbose) UHD_MSG(warning) << error_msg << std::endl; + if (i == NUM_RETRIES) throw uhd::io_error(error_msg); + } + } + return 0; +} + +void usrp3_fw_ctrl_iface::_poke32(const wb_addr_type addr, const boost::uint32_t data) +{ + //Load request struct + fw_comm_pkt_t request; + request.id = uhd::htonx<boost::uint32_t>(FW_COMM_GENERATE_ID(_product_id)); + request.flags = uhd::htonx<boost::uint32_t>(FW_COMM_FLAGS_ACK | FW_COMM_CMD_POKE32); + request.sequence = uhd::htonx<boost::uint32_t>(_seq_num++); + request.addr = uhd::htonx(addr); + request.data_words = 1; + request.data[0] = uhd::htonx(data); + + //Send request + _flush(); + _udp_xport->send(boost::asio::buffer(&request, sizeof(request))); + + //Recv reply + fw_comm_pkt_t reply; + const size_t nbytes = _udp_xport->recv(boost::asio::buffer(&reply, sizeof(reply)), 1.0); + if (nbytes == 0) throw uhd::io_error("udp fw poke32 - reply timed out"); + + //Sanity checks + const size_t flags = uhd::ntohx<boost::uint32_t>(reply.flags); + UHD_ASSERT_THROW(nbytes == sizeof(reply)); + UHD_ASSERT_THROW(not (flags & FW_COMM_FLAGS_ERROR_MASK)); + UHD_ASSERT_THROW(flags & FW_COMM_CMD_POKE32); + UHD_ASSERT_THROW(flags & FW_COMM_FLAGS_ACK); + UHD_ASSERT_THROW(reply.sequence == request.sequence); + UHD_ASSERT_THROW(reply.addr == request.addr); + UHD_ASSERT_THROW(reply.data[0] == request.data[0]); +} + +boost::uint32_t usrp3_fw_ctrl_iface::_peek32(const wb_addr_type addr) +{ + //Load request struct + fw_comm_pkt_t request; + request.id = uhd::htonx<boost::uint32_t>(FW_COMM_GENERATE_ID(_product_id)); + request.flags = uhd::htonx<boost::uint32_t>(FW_COMM_FLAGS_ACK | FW_COMM_CMD_PEEK32); + request.sequence = uhd::htonx<boost::uint32_t>(_seq_num++); + request.addr = uhd::htonx(addr); + request.data_words = 1; + request.data[0] = 0; + + //Send request + _flush(); + _udp_xport->send(boost::asio::buffer(&request, sizeof(request))); + + //Recv reply + fw_comm_pkt_t reply; + const size_t nbytes = _udp_xport->recv(boost::asio::buffer(&reply, sizeof(reply)), 1.0); + if (nbytes == 0) throw uhd::io_error("udp fw peek32 - reply timed out"); + + //Sanity checks + const size_t flags = uhd::ntohx<boost::uint32_t>(reply.flags); + UHD_ASSERT_THROW(nbytes == sizeof(reply)); + UHD_ASSERT_THROW(not (flags & FW_COMM_FLAGS_ERROR_MASK)); + UHD_ASSERT_THROW(flags & FW_COMM_CMD_PEEK32); + UHD_ASSERT_THROW(flags & FW_COMM_FLAGS_ACK); + UHD_ASSERT_THROW(reply.sequence == request.sequence); + UHD_ASSERT_THROW(reply.addr == request.addr); + + //return result! + return uhd::ntohx<boost::uint32_t>(reply.data[0]); +} + +void usrp3_fw_ctrl_iface::_flush(void) +{ + char buff[FW_COMM_PROTOCOL_MTU] = {}; + while (_udp_xport->recv(boost::asio::buffer(buff), 0.0)) { + /*NOP*/ + } +} + +std::vector<std::string> usrp3_fw_ctrl_iface::discover_devices( + const std::string& addr_hint, const std::string& port, + boost::uint16_t product_id) +{ + std::vector<std::string> addrs; + + //Create a UDP transport to communicate: + //Some devices will cause a throw when opened for a broadcast address. + //We print and recover so the caller can loop through all bcast addrs. + uhd::transport::udp_simple::sptr udp_bcast_xport; + try { + udp_bcast_xport = uhd::transport::udp_simple::make_broadcast(addr_hint, port); + } catch(const std::exception &e) { + UHD_MSG(error) << boost::format("Cannot open UDP transport on %s for discovery\n%s") + % addr_hint % e.what() << std::endl; + return addrs; + } + + //Send dummy request + fw_comm_pkt_t request; + request.id = uhd::htonx<boost::uint32_t>(FW_COMM_GENERATE_ID(product_id)); + request.flags = uhd::htonx<boost::uint32_t>(FW_COMM_FLAGS_ACK|FW_COMM_CMD_ECHO); + request.sequence = uhd::htonx<boost::uint32_t>(std::rand()); + udp_bcast_xport->send(boost::asio::buffer(&request, sizeof(request))); + + //loop for replies until timeout + while (true) { + char buff[FW_COMM_PROTOCOL_MTU] = {}; + const size_t nbytes = udp_bcast_xport->recv(boost::asio::buffer(buff), 0.050); + if (nbytes != sizeof(fw_comm_pkt_t)) break; //No more responses or responses are invalid + + const fw_comm_pkt_t *reply = (const fw_comm_pkt_t *)buff; + if (request.id == reply->id && + request.flags == reply->flags && + request.sequence == reply->sequence) + { + addrs.push_back(udp_bcast_xport->get_recv_addr()); + } + } + + return addrs; +} + +boost::uint32_t usrp3_fw_ctrl_iface::get_iface_id( + const std::string& addr, const std::string& port, + boost::uint16_t product_id) +{ + uhd::transport::udp_simple::sptr udp_xport = + uhd::transport::udp_simple::make_connected(addr, port); + + //Send dummy request + fw_comm_pkt_t request; + request.id = uhd::htonx<boost::uint32_t>(FW_COMM_GENERATE_ID(product_id)); + request.flags = uhd::htonx<boost::uint32_t>(FW_COMM_FLAGS_ACK|FW_COMM_CMD_ECHO); + request.sequence = uhd::htonx<boost::uint32_t>(std::rand()); + udp_xport->send(boost::asio::buffer(&request, sizeof(request))); + + //loop for replies until timeout + char buff[FW_COMM_PROTOCOL_MTU] = {}; + const size_t nbytes = udp_xport->recv(boost::asio::buffer(buff), 1.0); + + const fw_comm_pkt_t *reply = (const fw_comm_pkt_t *)buff; + if (nbytes > 0 && + request.id == reply->id && + request.flags == reply->flags && + request.sequence == reply->sequence) + { + return uhd::ntohx<boost::uint32_t>(reply->data[0]); + } else { + throw uhd::io_error("udp get_iface_id - bad response"); + } +} + +}}} //namespace diff --git a/host/lib/usrp/common/usrp3_fw_ctrl_iface.hpp b/host/lib/usrp/common/usrp3_fw_ctrl_iface.hpp new file mode 100644 index 000000000..33286861b --- /dev/null +++ b/host/lib/usrp/common/usrp3_fw_ctrl_iface.hpp @@ -0,0 +1,72 @@ +// +// Copyright 2014 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_USRP3_UDP_FW_CTRL_IFACE_HPP +#define INCLUDED_LIBUHD_USRP_USRP3_UDP_FW_CTRL_IFACE_HPP + +#include <uhd/types/wb_iface.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <boost/thread/mutex.hpp> +#include <vector> + +namespace uhd { namespace usrp { namespace usrp3 { + +class usrp3_fw_ctrl_iface : public uhd::wb_iface +{ +public: + usrp3_fw_ctrl_iface( + uhd::transport::udp_simple::sptr udp_xport, + const boost::uint16_t product_id, + const bool verbose); + virtual ~usrp3_fw_ctrl_iface(); + + // -- uhd::wb_iface -- + void poke32(const wb_addr_type addr, const boost::uint32_t data); + boost::uint32_t peek32(const wb_addr_type addr); + void flush(); + + static uhd::wb_iface::sptr make( + uhd::transport::udp_simple::sptr udp_xport, + const boost::uint16_t product_id, + const bool verbose = true); + // -- uhd::wb_iface -- + + static std::vector<std::string> discover_devices( + const std::string& addr_hint, const std::string& port, + boost::uint16_t product_id); + + static boost::uint32_t get_iface_id( + const std::string& addr, const std::string& port, + boost::uint16_t product_id); + +private: + void _poke32(const wb_addr_type addr, const boost::uint32_t data); + boost::uint32_t _peek32(const wb_addr_type addr); + void _flush(void); + + const boost::uint16_t _product_id; + const bool _verbose; + uhd::transport::udp_simple::sptr _udp_xport; + boost::uint32_t _seq_num; + boost::mutex _mutex; + + static const size_t NUM_RETRIES = 3; +}; + +}}} //namespace + +#endif //INCLUDED_LIBUHD_USRP_USRP3_USRP3_UDP_FW_CTRL_HPP |