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  | 
