diff options
| author | Josh Blum <josh@joshknows.com> | 2010-03-02 22:07:17 -0800 | 
|---|---|---|
| committer | Josh Blum <josh@joshknows.com> | 2010-03-02 22:07:17 -0800 | 
| commit | 4efafcc2e20b9a980800a979edf5ea7a493b6462 (patch) | |
| tree | 49fbf3c6e5b25f31a0de30ecf11ceb19872e590f | |
| parent | 13bd67b4949a91df5e6696e708c935266b14c502 (diff) | |
| download | uhd-4efafcc2e20b9a980800a979edf5ea7a493b6462.tar.gz uhd-4efafcc2e20b9a980800a979edf5ea7a493b6462.tar.bz2 uhd-4efafcc2e20b9a980800a979edf5ea7a493b6462.zip | |
Expanded the UDP api:
We can make simple udp transports for discovery and control.
We can support a udp zero copy transport (currently just asio).
Reworked the io_impl for usrp2 to work with the zero copy api.
So far, all of this untested other than compiling.
A cut-down vrt library is in the works to simplify the io impl.
| -rw-r--r-- | host/include/uhd/transport/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | host/include/uhd/transport/smart_buffer.hpp | 45 | ||||
| -rw-r--r-- | host/include/uhd/transport/udp_simple.hpp (renamed from host/include/uhd/transport/udp.hpp) | 47 | ||||
| -rw-r--r-- | host/include/uhd/transport/udp_zero_copy.hpp | 76 | ||||
| -rw-r--r-- | host/include/uhd/usrp/usrp2.hpp | 10 | ||||
| -rw-r--r-- | host/lib/CMakeLists.txt | 9 | ||||
| -rw-r--r-- | host/lib/transport/udp.cpp | 98 | ||||
| -rw-r--r-- | host/lib/transport/udp_simple.cpp | 133 | ||||
| -rw-r--r-- | host/lib/transport/udp_zero_copy_none.cpp | 117 | ||||
| -rw-r--r-- | host/lib/usrp/usrp2/io_impl.cpp | 202 | ||||
| -rw-r--r-- | host/lib/usrp/usrp2/usrp2_impl.cpp | 18 | ||||
| -rw-r--r-- | host/lib/usrp/usrp2/usrp2_impl.hpp | 24 | 
12 files changed, 523 insertions, 260 deletions
| diff --git a/host/include/uhd/transport/CMakeLists.txt b/host/include/uhd/transport/CMakeLists.txt index b786eb945..ba8b33cc5 100644 --- a/host/include/uhd/transport/CMakeLists.txt +++ b/host/include/uhd/transport/CMakeLists.txt @@ -17,6 +17,8 @@  INSTALL(FILES -    udp.hpp +    smart_buffer.hpp +    udp_simple.hpp +    udp_zero_copy.hpp      DESTINATION ${HEADER_DIR}/uhd/transport  ) diff --git a/host/include/uhd/transport/smart_buffer.hpp b/host/include/uhd/transport/smart_buffer.hpp new file mode 100644 index 000000000..914c02f50 --- /dev/null +++ b/host/include/uhd/transport/smart_buffer.hpp @@ -0,0 +1,45 @@ +// +// Copyright 2010 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 <boost/asio.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> + +#ifndef INCLUDED_UHD_TRANSPORT_SMART_BUFFER_HPP +#define INCLUDED_UHD_TRANSPORT_SMART_BUFFER_HPP + +namespace uhd{ namespace transport{ + +/*! + * A buffer that knows how to free itself: + * + * This is just the smart buffer interface. + * A transport implementation will have its own + * internal (custom) smart buffer implementation. + * + * A smart buffer contains a boost asio const buffer. + * On destruction, the buffer contents will be freed. + */ +class smart_buffer : boost::noncopyable{ +public: +    typedef boost::shared_ptr<smart_buffer> sptr; +    virtual const boost::asio::const_buffer &get(void) const = 0; +}; + +}} //namespace + +#endif /* INCLUDED_UHD_TRANSPORT_SMART_BUFFER_HPP */ diff --git a/host/include/uhd/transport/udp.hpp b/host/include/uhd/transport/udp_simple.hpp index 8c6fb096f..8663128ec 100644 --- a/host/include/uhd/transport/udp.hpp +++ b/host/include/uhd/transport/udp_simple.hpp @@ -19,32 +19,43 @@  #include <boost/utility.hpp>  #include <boost/shared_ptr.hpp> -#ifndef INCLUDED_UHD_TRANSPORT_UDP_HPP -#define INCLUDED_UHD_TRANSPORT_UDP_HPP +#ifndef INCLUDED_UHD_TRANSPORT_UDP_SIMPLE_HPP +#define INCLUDED_UHD_TRANSPORT_UDP_SIMPLE_HPP  namespace uhd{ namespace transport{ -class udp : boost::noncopyable{ +class udp_simple : boost::noncopyable{  public: -    typedef boost::shared_ptr<udp> sptr; +    typedef boost::shared_ptr<udp_simple> sptr;      /*! -     * Make a new udp transport. +     * Make a new connected udp transport: +     * This transport is for sending and receiving +     * between this host and a single endpoint. +     * The primary usage for this transport will be control transactions. +     * The underlying implementation is simple and portable (not fast). +     *       * The address will be resolved, it can be a host name or ipv4.       * The port will be resolved, it can be a port type or number. +     *       * \param addr a string representing the destination address       * \param port a string representing the destination port -     * \param bcast if true, enable the broadcast option on the socket       */ -    static sptr make(const std::string &addr, const std::string &port, bool bcast = false); +    static sptr make_connected(const std::string &addr, const std::string &port);      /*! -     * Send a vector of buffer (like send_msg). -     * Blocks until the data is sent. -     * \param buffs a vector of asio buffers -     * \return the number of bytes sent +     * Make a new broadcasting udp transport: +     * This transport can send udp broadcast datagrams +     * and receive datagrams from multiple sources. +     * The primary usage for this transport will be to discover devices. +     * +     * The address will be resolved, it can be a host name or ipv4. +     * The port will be resolved, it can be a port type or number. +     * +     * \param addr a string representing the destination address +     * \param port a string representing the destination port       */ -    virtual size_t send(const std::vector<boost::asio::const_buffer> &buffs) = 0; +    static sptr make_broadcast(const std::string &addr, const std::string &port);      /*!       * Send a single buffer. @@ -55,15 +66,7 @@ public:      virtual size_t send(const boost::asio::const_buffer &buff) = 0;      /*! -     * Receive a buffer. Write into the memory provided. -     * Returns empty when data is not available. -     * \param buffs a vector of asio buffers -     * \return the number of bytes received. -     */ -    virtual size_t recv(const std::vector<boost::asio::mutable_buffer> &buffs) = 0; - -    /*! -     * Receive a buffer. Write into the memory provided. +     * Receive into the provided buffer.       * Returns empty when data is not available.       * \param buff a mutable buffer to receive into       * \return the number of bytes received. @@ -73,4 +76,4 @@ public:  }} //namespace -#endif /* INCLUDED_UHD_TRANSPORT_UDP_HPP */ +#endif /* INCLUDED_UHD_TRANSPORT_UDP_SIMPLE_HPP */ diff --git a/host/include/uhd/transport/udp_zero_copy.hpp b/host/include/uhd/transport/udp_zero_copy.hpp new file mode 100644 index 000000000..9c3505dd6 --- /dev/null +++ b/host/include/uhd/transport/udp_zero_copy.hpp @@ -0,0 +1,76 @@ +// +// Copyright 2010 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 <uhd/transport/smart_buffer.hpp> +#include <boost/asio.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> + +#ifndef INCLUDED_UHD_TRANSPORT_UDP_ZERO_COPY_HPP +#define INCLUDED_UHD_TRANSPORT_UDP_ZERO_COPY_HPP + +namespace uhd{ namespace transport{ + +/*! + * A zero copy udp transport provides an efficient way to handle data. + * by avoiding the extra copy when recv() is called on the socket. + * Rather, the zero copy transport gives the caller a memory reference. + * The caller informs the transport when it is finished with the reference. + * + * On linux systems, the zero copy transport can use a kernel packet ring. + * If no platform specific solution is available, make returns a boost asio + * implementation that wraps the functionality around a standard recv() call. + */ +class udp_zero_copy : boost::noncopyable{ +public: +    typedef boost::shared_ptr<udp_zero_copy> sptr; + +    /*! +     * Make a new zero copy udp transport: +     * This transport is for sending and receiving +     * between this host and a single endpoint. +     * The primary usage for this transport will be data transactions. +     * The underlying implementation is fast and platform specific. +     * +     * The address will be resolved, it can be a host name or ipv4. +     * The port will be resolved, it can be a port type or number. +     * +     * \param addr a string representing the destination address +     * \param port a string representing the destination port +     */ +    static sptr make(const std::string &addr, const std::string &port); + +    /*! +     * Send a single buffer. +     * Blocks until the data is sent. +     * \param buff single asio buffer +     * \return the number of bytes sent +     */ +    virtual size_t send(const boost::asio::const_buffer &buff) = 0; + +    /*! +     * Receive a buffer. +     * The memory is managed by the implementation. +     * Returns an empty buffer when data is not available. +     * \return a smart buffer with memory and size +     */ +    virtual smart_buffer::sptr recv(void) = 0; +}; + +}} //namespace + +#endif /* INCLUDED_UHD_TRANSPORT_UDP_ZERO_COPY_HPP */ diff --git a/host/include/uhd/usrp/usrp2.hpp b/host/include/uhd/usrp/usrp2.hpp index da7ec595a..b13786546 100644 --- a/host/include/uhd/usrp/usrp2.hpp +++ b/host/include/uhd/usrp/usrp2.hpp @@ -29,6 +29,11 @@ class usrp2 : public device{  public:      /*!       * Discover usrp2 devices over the ethernet. +     * +     * Recommended key/value pairs for the device hint address: +     * hint["addr"] = address, where address is a resolvable address +     * or ip address, which may or may not be a broadcast address. +     *       * This static method will be called by the device::discover.       * \param hint a device addr with the usrp2 address filled in       * \return a vector of device addresses for all usrp2s found @@ -37,6 +42,11 @@ public:      /*!       * Make a usrp2 from a device address. +     * +     * Required key/value pairs for the device address: +     * hint["addr"] = address, where address is a resolvable address +     * or ip address, which must be the specific address of a usrp2. +     *       * \param addr the device address       * \return a device sptr to a new usrp2       */ diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt index 97f1ac52e..a52cd74a9 100644 --- a/host/lib/CMakeLists.txt +++ b/host/lib/CMakeLists.txt @@ -23,7 +23,7 @@ SET(libuhd_sources      device_addr.cpp      gain_handler.cpp      wax.cpp -    transport/udp.cpp +    transport/udp_simple.cpp      usrp/dboard/basic.cpp      usrp/dboard_base.cpp      usrp/dboard_id.cpp @@ -38,6 +38,13 @@ SET(libuhd_sources  )  ######################################################################## +# Conditionally add the udp sources +######################################################################## +LIST(APPEND libuhd_sources +    transport/udp_zero_copy_none.cpp +) + +########################################################################  # Conditionally add the usrp1e sources  ########################################################################  LIST(APPEND libuhd_sources diff --git a/host/lib/transport/udp.cpp b/host/lib/transport/udp.cpp deleted file mode 100644 index 878f71410..000000000 --- a/host/lib/transport/udp.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// -// Copyright 2010 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 <uhd/transport/udp.hpp> -#include <boost/format.hpp> -#include <iostream> - -/*********************************************************************** - * UDP implementation class - **********************************************************************/ -class udp_impl : public uhd::transport::udp{ -public: -    //structors -    udp_impl(const std::string &addr, const std::string &port, bool bcast); -    ~udp_impl(void); - -    //send/recv -    size_t send(const std::vector<boost::asio::const_buffer> &buffs); -    size_t send(const boost::asio::const_buffer &buff); -    size_t recv(const std::vector<boost::asio::mutable_buffer> &buffs); -    size_t recv(const boost::asio::mutable_buffer &buff); - -private: -    boost::asio::ip::udp::socket   *_socket; -    boost::asio::ip::udp::endpoint _receiver_endpoint; -    boost::asio::ip::udp::endpoint _sender_endpoint; -    boost::asio::io_service        _io_service; -}; - -/*********************************************************************** - * UDP public make function - **********************************************************************/ -uhd::transport::udp::sptr uhd::transport::udp::make( -    const std::string &addr, -    const std::string &port, -    bool bcast -){ -    return uhd::transport::udp::sptr(new udp_impl(addr, port, bcast)); -} - -/*********************************************************************** - * UDP implementation methods - **********************************************************************/ -udp_impl::udp_impl(const std::string &addr, const std::string &port, bool bcast){ -    //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; - -    // resolve the address -    boost::asio::ip::udp::resolver resolver(_io_service); -    boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); -    _receiver_endpoint = *resolver.resolve(query); - -    // Create and open the socket -    _socket = new boost::asio::ip::udp::socket(_io_service); -    _socket->open(boost::asio::ip::udp::v4()); - -    if (bcast){ -        // Allow broadcasting -        boost::asio::socket_base::broadcast option(true); -        _socket->set_option(option); -    } - -} - -udp_impl::~udp_impl(void){ -    delete _socket; -} - -size_t udp_impl::send(const std::vector<boost::asio::const_buffer> &buffs){ -    return _socket->send_to(buffs, _receiver_endpoint); -} - -size_t udp_impl::send(const boost::asio::const_buffer &buff){ -    return _socket->send_to(boost::asio::buffer(buff), _receiver_endpoint); -} - -size_t udp_impl::recv(const std::vector<boost::asio::mutable_buffer> &buffs){ -    if (_socket->available() == 0) return 0; -    return _socket->receive_from(buffs, _sender_endpoint); -} - -size_t udp_impl::recv(const boost::asio::mutable_buffer &buff){ -    if (_socket->available() == 0) return 0; -    return _socket->receive_from(boost::asio::buffer(buff), _sender_endpoint); -} diff --git a/host/lib/transport/udp_simple.cpp b/host/lib/transport/udp_simple.cpp new file mode 100644 index 000000000..491cf59db --- /dev/null +++ b/host/lib/transport/udp_simple.cpp @@ -0,0 +1,133 @@ +// +// Copyright 2010 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 <uhd/transport/udp_simple.hpp> +#include <boost/format.hpp> +#include <iostream> + +using namespace uhd::transport; + +/*********************************************************************** + * UDP connected implementation class + **********************************************************************/ +class udp_connected_impl : public udp_simple{ +public: +    //structors +    udp_connected_impl(const std::string &addr, const std::string &port); +    ~udp_connected_impl(void); + +    //send/recv +    size_t send(const boost::asio::const_buffer &buff); +    size_t recv(const boost::asio::mutable_buffer &buff); + +private: +    boost::asio::ip::udp::socket   *_socket; +    boost::asio::io_service        _io_service; +}; + +udp_connected_impl::udp_connected_impl(const std::string &addr, const std::string &port){ +    //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; + +    // resolve the address +    boost::asio::ip::udp::resolver resolver(_io_service); +    boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); +    boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); + +    // Create, open, and connect the socket +    _socket = new boost::asio::ip::udp::socket(_io_service); +    _socket->open(boost::asio::ip::udp::v4()); +    _socket->connect(receiver_endpoint); +} + +udp_connected_impl::~udp_connected_impl(void){ +    delete _socket; +} + +size_t udp_connected_impl::send(const boost::asio::const_buffer &buff){ +    return _socket->send(boost::asio::buffer(buff)); +} + +size_t udp_connected_impl::recv(const boost::asio::mutable_buffer &buff){ +    if (_socket->available() == 0) return 0; +    return _socket->receive(boost::asio::buffer(buff)); +} + +/*********************************************************************** + * UDP broadcast implementation class + **********************************************************************/ +class udp_broadcast_impl : public udp_simple{ +public: +    //structors +    udp_broadcast_impl(const std::string &addr, const std::string &port); +    ~udp_broadcast_impl(void); + +    //send/recv +    size_t send(const boost::asio::const_buffer &buff); +    size_t recv(const boost::asio::mutable_buffer &buff); + +private: +    boost::asio::ip::udp::socket   *_socket; +    boost::asio::ip::udp::endpoint _receiver_endpoint; +    boost::asio::io_service        _io_service; +}; + +udp_broadcast_impl::udp_broadcast_impl(const std::string &addr, const std::string &port){ +    //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; + +    // resolve the address +    boost::asio::ip::udp::resolver resolver(_io_service); +    boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); +    _receiver_endpoint = *resolver.resolve(query); + +    // Create and open the socket +    _socket = new boost::asio::ip::udp::socket(_io_service); +    _socket->open(boost::asio::ip::udp::v4()); + +    // Allow broadcasting +    boost::asio::socket_base::broadcast option(true); +    _socket->set_option(option); + +} + +udp_broadcast_impl::~udp_broadcast_impl(void){ +    delete _socket; +} + +size_t udp_broadcast_impl::send(const boost::asio::const_buffer &buff){ +    return _socket->send_to(boost::asio::buffer(buff), _receiver_endpoint); +} + +size_t udp_broadcast_impl::recv(const boost::asio::mutable_buffer &buff){ +    if (_socket->available() == 0) return 0; +    boost::asio::ip::udp::endpoint sender_endpoint; +    return _socket->receive_from(boost::asio::buffer(buff), sender_endpoint); +} + +/*********************************************************************** + * UDP public make functions + **********************************************************************/ +udp_simple::sptr udp_simple::make_connected( +    const std::string &addr, const std::string &port +){ +    return sptr(new udp_connected_impl(addr, port)); +} + +udp_simple::sptr udp_simple::make_broadcast( +    const std::string &addr, const std::string &port +){ +    return sptr(new udp_broadcast_impl(addr, port)); +} diff --git a/host/lib/transport/udp_zero_copy_none.cpp b/host/lib/transport/udp_zero_copy_none.cpp new file mode 100644 index 000000000..e95706d94 --- /dev/null +++ b/host/lib/transport/udp_zero_copy_none.cpp @@ -0,0 +1,117 @@ +// +// Copyright 2010 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 <uhd/transport/udp_zero_copy.hpp> + +using namespace uhd::transport; + +/*********************************************************************** + * Smart buffer implementation for udp zerocopy none + * + * This smart buffer implemention houses a const buffer. + * When the smart buffer is deleted, the buffer is freed. + * The memory in the const buffer is allocated with new [], + * and so the destructor frees the buffer with delete []. + **********************************************************************/ +class smart_buffer_impl : public smart_buffer{ +public: +    smart_buffer_impl(const boost::asio::const_buffer &buff){ +        _buff = buff; +    } + +    ~smart_buffer_impl(void){ +        delete [] boost::asio::buffer_cast<const uint32_t *>(_buff); +    } + +    const boost::asio::const_buffer &get(void) const{ +        return _buff; +    } + +private: +    boost::asio::const_buffer _buff; +}; + +/*********************************************************************** + * UDP zero copy implementation class + * + * This is the portable zero copy implementation for systems + * where a faster, platform specific solution is not available. + * + * It uses boost asio udp sockets and the standard recv() class, + * and in-fact, is not actually doing a zero-copy implementation. + **********************************************************************/ +class udp_zero_copy_impl : public udp_zero_copy{ +public: +    //structors +    udp_zero_copy_impl(const std::string &addr, const std::string &port); +    ~udp_zero_copy_impl(void); + +    //send/recv +    size_t send(const boost::asio::const_buffer &buff); +    smart_buffer::sptr recv(void); + +private: +    boost::asio::ip::udp::socket   *_socket; +    boost::asio::io_service        _io_service; +}; + +udp_zero_copy_impl::udp_zero_copy_impl(const std::string &addr, const std::string &port){ +    //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; + +    // resolve the address +    boost::asio::ip::udp::resolver resolver(_io_service); +    boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); +    boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); + +    // Create, open, and connect the socket +    _socket = new boost::asio::ip::udp::socket(_io_service); +    _socket->open(boost::asio::ip::udp::v4()); +    _socket->connect(receiver_endpoint); +} + +udp_zero_copy_impl::~udp_zero_copy_impl(void){ +    delete _socket; +} + +size_t udp_zero_copy_impl::send(const boost::asio::const_buffer &buff){ +    return _socket->send(boost::asio::buffer(buff)); +} + +smart_buffer::sptr udp_zero_copy_impl::recv(void){ +    size_t available = _socket->available(); + +    //allocate memory and create buffer +    uint32_t *buff_mem = new uint32_t[available/sizeof(uint32_t)]; +    boost::asio::mutable_buffer buff(buff_mem, available); + +    //receive only if data is available +    if (available > 0){ +        _socket->receive(boost::asio::buffer(buff)); +    } + +    //create a new smart buffer to house the data +    return smart_buffer::sptr(new smart_buffer_impl(buff)); +} + +/*********************************************************************** + * UDP zero copy make function + **********************************************************************/ +udp_zero_copy::sptr udp_zero_copy::make( +    const std::string &addr, const std::string &port +){ +    return sptr(new udp_zero_copy_impl(addr, port)); +} diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index 0ca2409c3..6969e0a89 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -36,12 +36,12 @@ static const float floats_per_short = 1.0/shorts_per_float;   * Helper Functions   **********************************************************************/  void usrp2_impl::io_init(void){ -    //initially empty spillover buffer -    _splillover_buff = asio::buffer(_spillover_mem, 0); +    //initially empty copy buffer +    _rx_copy_buff = asio::buffer("", 0);      //send a small data packet so the usrp2 knows the udp source port      uint32_t zero_data = 0; -    _data_transport->send(boost::asio::buffer(&zero_data, sizeof(zero_data))); +    _data_transport->send(asio::buffer(&zero_data, sizeof(zero_data)));  }  #define unrolled_loop(__i, __len, __inst) {\ @@ -57,6 +57,13 @@ void usrp2_impl::io_init(void){      } \  } +// set a boolean flag that indicates the endianess +#ifdef HAVE_BIG_ENDIAN +static const bool is_big_endian = true; +#else +static const bool is_big_endian = false; +#endif +  static inline void host_floats_to_usrp2_items(      uint32_t *usrp2_items,      const fc32_t *host_floats, @@ -87,9 +94,12 @@ static inline void host_items_to_usrp2_items(      const uint32_t *host_items,      size_t num_samps  ){ -    unrolled_loop(i, num_samps, -        usrp2_items[i] = htonl(host_items[i]) -    ); +    if (is_big_endian){ +        std::memcpy(usrp2_items, host_items, num_samps*sizeof(uint32_t)); +    } +    else{ +        unrolled_loop(i, num_samps, usrp2_items[i] = htonl(host_items[i])); +    }  }  static inline void usrp2_items_to_host_items( @@ -97,20 +107,22 @@ static inline void usrp2_items_to_host_items(      const uint32_t *usrp2_items,      size_t num_samps  ){ -    unrolled_loop(i, num_samps, -        host_items[i] = ntohl(usrp2_items[i]) -    ); +    if (is_big_endian){ +        std::memcpy(host_items, usrp2_items, num_samps*sizeof(uint32_t)); +    } +    else{ +        unrolled_loop(i, num_samps, host_items[i] = ntohl(usrp2_items[i])); +    }  }  /***********************************************************************   * Send Raw Data   **********************************************************************/ -size_t usrp2_impl::send_raw( -    const boost::asio::const_buffer &buff, -    const uhd::metadata_t &metadata -){ -    std::vector<boost::asio::const_buffer> buffs(2); -    uint32_t vrt_hdr[7]; //max size +size_t usrp2_impl::send_raw(const uhd::metadata_t &metadata){ +    size_t num_items = asio::buffer_size(_tx_copy_buff)/sizeof(uint32_t); +    const uint32_t *items = asio::buffer_cast<const uint32_t *>(_tx_copy_buff); + +    uint32_t vrt_hdr[_tx_vrt_max_offset_words32];      uint32_t vrt_hdr_flags = 0;      size_t num_vrt_hdr_words = 1; @@ -131,60 +143,30 @@ size_t usrp2_impl::send_raw(      //fill in complete header word      vrt_hdr[0] = htonl(vrt_hdr_flags |          ((_tx_stream_id_to_packet_seq[metadata.stream_id]++ & 0xf) << 16) | -        ((num_vrt_hdr_words + asio::buffer_size(buff)/sizeof(uint32_t)) & 0xffff) +        ((num_vrt_hdr_words + num_items) & 0xffff)      ); -    //load the buffer vector -    size_t vrt_hdr_size = num_vrt_hdr_words*sizeof(uint32_t); -    buffs[0] = asio::buffer(&vrt_hdr, vrt_hdr_size); -    buffs[1] = buff; +    //copy in the vrt header (yes we left space) +    std::memcpy(((uint32_t *)items) - num_vrt_hdr_words, vrt_hdr, num_vrt_hdr_words); +    asio::const_buffer buff(items - num_vrt_hdr_words, (num_vrt_hdr_words + num_items)*sizeof(uint32_t));      //send and return number of samples -    return (_data_transport->send(buffs) - vrt_hdr_size)/sizeof(sc16_t); +    return (_data_transport->send(buff) - num_vrt_hdr_words*sizeof(uint32_t))/sizeof(sc16_t);  }  /***********************************************************************   * Receive Raw Data   **********************************************************************/ -size_t usrp2_impl::recv_raw( -    const boost::asio::mutable_buffer &buff, -    uhd::metadata_t &metadata -){ -    metadata = metadata_t(); //clear metadata - -    //handle the case where there is spillover -    if (asio::buffer_size(_splillover_buff) != 0){ -        size_t bytes_to_copy = std::min( -            asio::buffer_size(_splillover_buff), -            asio::buffer_size(buff) -        ); -        std::memcpy( -            asio::buffer_cast<void*>(buff), -            asio::buffer_cast<const void*>(_splillover_buff), -            bytes_to_copy -        ); -        _splillover_buff = asio::buffer( -            asio::buffer_cast<uint8_t*>(_splillover_buff)+bytes_to_copy, -            asio::buffer_size(_splillover_buff)-bytes_to_copy -        ); -        //std::cout << boost::format("Copied spillover %d samples") % (bytes_to_copy/sizeof(sc16_t)) << std::endl; -        return bytes_to_copy/sizeof(sc16_t); -    } - -    //load the buffer vector -    std::vector<boost::asio::mutable_buffer> buffs(3); -    uint32_t vrt_hdr[USRP2_HOST_RX_VRT_HEADER_WORDS32]; -    buffs[0] = asio::buffer(vrt_hdr, sizeof(vrt_hdr)); -    buffs[1] = buff; -    buffs[2] = asio::buffer(_spillover_mem, _mtu); +void usrp2_impl::recv_raw(uhd::metadata_t &metadata){ +    //do a receive +    _rx_smart_buff = _data_transport->recv(); -    //receive into the buffers -    size_t bytes_recvd = _data_transport->recv(buffs); - -    //failure case -    if (bytes_recvd < sizeof(vrt_hdr)) return 0; +    //////////////////////////////////////////////////////////////////// +    // !!!! FIXME this is very flawed, use a proper vrt unpacker !!!!!!! +    ////////////////////////////////////////////////////////////////////      //unpack the vrt header +    const uint32_t *vrt_hdr = asio::buffer_cast<const uint32_t *>(_rx_smart_buff->get());      metadata = uhd::metadata_t();      uint32_t vrt_header = ntohl(vrt_hdr[0]);      metadata.has_stream_id = true; @@ -202,13 +184,12 @@ size_t usrp2_impl::recv_raw(      size_t num_words = (vrt_header & 0xffff) -          USRP2_HOST_RX_VRT_HEADER_WORDS32 -          USRP2_HOST_RX_VRT_TRAILER_WORDS32; -    size_t num_bytes = num_words*sizeof(uint32_t); - -    //handle the case where spillover memory was used -    size_t spillover_size = num_bytes - std::min(num_bytes, asio::buffer_size(buff)); -    _splillover_buff = asio::buffer(_spillover_mem, spillover_size); -    return (num_bytes - spillover_size)/sizeof(sc16_t); +    //setup the rx buffer to point to the data +    _rx_copy_buff = boost::asio::buffer( +        vrt_hdr + USRP2_HOST_RX_VRT_HEADER_WORDS32, +        num_words*sizeof(uint32_t) +    );  }  /*********************************************************************** @@ -219,37 +200,26 @@ size_t usrp2_impl::send(      const uhd::metadata_t &metadata,      const std::string &type  ){ -    if (type == "32fc"){ -        size_t num_samps = asio::buffer_size(buff)/sizeof(fc32_t); -        boost::asio::mutable_buffer raw_buff(_tmp_send_mem, num_samps*sizeof(sc16_t)); +    uint32_t *items = _tx_mem + _tx_vrt_max_offset_words32; //offset for data +    size_t num_samps = _max_samples_per_packet; -        host_floats_to_usrp2_items( -            asio::buffer_cast<uint32_t*>(raw_buff), -            asio::buffer_cast<const fc32_t*>(buff), -            num_samps -        ); - -        return send_raw(raw_buff, metadata); +    //calculate the number of samples to be copied +    //and copy the samples into the send buffer +    if (type == "32fc"){ +        num_samps = std::min(asio::buffer_size(buff)/sizeof(fc32_t), num_samps); +        host_floats_to_usrp2_items(items, asio::buffer_cast<const fc32_t*>(buff), num_samps);      } - -    if (type == "16sc"){ -        #ifdef HAVE_BIG_ENDIAN -        return send_raw(buff, metadata); -        #else -        size_t num_samps = asio::buffer_size(buff)/sizeof(sc16_t); -        boost::asio::mutable_buffer raw_buff(_tmp_send_mem, num_samps*sizeof(sc16_t)); - -        host_items_to_usrp2_items( -            asio::buffer_cast<uint32_t*>(raw_buff), -            asio::buffer_cast<const uint32_t*>(buff), -            num_samps -        ); - -        return send_raw(raw_buff, metadata); -        #endif +    else if (type == "16sc"){ +        num_samps = std::min(asio::buffer_size(buff)/sizeof(sc16_t), num_samps); +        host_items_to_usrp2_items(items, asio::buffer_cast<const uint32_t*>(buff), num_samps); +    } +    else{ +        throw std::runtime_error(str(boost::format("usrp2 send: cannot handle type \"%s\"") % type));      } -    throw std::runtime_error(str(boost::format("usrp2 send: cannot handle type \"%s\"") % type)); +    //send the samples (this line seems silly, will be better with vrt lib) +    _tx_copy_buff = asio::buffer(items, num_samps*sizeof(uint32_t)); +    return send_raw(metadata); //return num_samps;  }  /*********************************************************************** @@ -260,39 +230,31 @@ size_t usrp2_impl::recv(      uhd::metadata_t &metadata,      const std::string &type  ){ -    if (type == "32fc"){ -        size_t num_samps = asio::buffer_size(buff)/sizeof(fc32_t); -        boost::asio::mutable_buffer raw_buff(_tmp_recv_mem, num_samps*sizeof(sc16_t)); - -        num_samps = recv_raw(raw_buff, metadata); +    //perform a receive if no rx data is waiting to be copied +    if (asio::buffer_size(_rx_copy_buff) == 0) recv_raw(metadata); +    //TODO otherwise flag the metadata to show that is is a fragment -        usrp2_items_to_host_floats( -            asio::buffer_cast<fc32_t*>(buff), -            asio::buffer_cast<const uint32_t*>(raw_buff), -            num_samps -        ); +    //extract the number of samples available to copy +    //and a pointer into the usrp2 received items memory +    size_t num_samps = asio::buffer_size(_rx_copy_buff)/sizeof(uint32_t); +    const uint32_t *items = asio::buffer_cast<const uint32_t*>(_rx_copy_buff); -        return num_samps; +    //calculate the number of samples to be copied +    //and copy the samples from the recv buffer +    if (type == "32fc"){ +        num_samps = std::min(asio::buffer_size(buff)/sizeof(fc32_t), num_samps); +        usrp2_items_to_host_floats(asio::buffer_cast<fc32_t*>(buff), items, num_samps);      } - -    if (type == "16sc"){ -        #ifdef HAVE_BIG_ENDIAN -        return recv_raw(buff, metadata); -        #else -        size_t num_samps = asio::buffer_size(buff)/sizeof(sc16_t); -        boost::asio::mutable_buffer raw_buff(_tmp_recv_mem, num_samps*sizeof(sc16_t)); - -        num_samps = recv_raw(raw_buff, metadata); - -        usrp2_items_to_host_items( -            asio::buffer_cast<uint32_t*>(buff), -            asio::buffer_cast<const uint32_t*>(raw_buff), -            num_samps -        ); - -        return num_samps; -        #endif +    else if (type == "16sc"){ +        num_samps = std::min(asio::buffer_size(buff)/sizeof(sc16_t), num_samps); +        usrp2_items_to_host_items(asio::buffer_cast<uint32_t*>(buff), items, num_samps); +    } +    else{ +        throw std::runtime_error(str(boost::format("usrp2 recv: cannot handle type \"%s\"") % type));      } -    throw std::runtime_error(str(boost::format("usrp2 recv: cannot handle type \"%s\"") % type)); +    //update the rx copy buffer to reflect the bytes copied +    _rx_copy_buff = asio::buffer(items + num_samps, num_samps*sizeof(uint32_t)); + +    return num_samps;  } diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 700f94ae1..51082df15 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -23,6 +23,7 @@  using namespace uhd;  using namespace uhd::usrp; +using namespace uhd::transport;  /***********************************************************************   * Discovery over the udp transport @@ -33,8 +34,9 @@ uhd::device_addrs_t usrp2::discover(const device_addr_t &hint){      //create a udp transport to communicate      //TODO if an addr is not provided, search all interfaces?      std::string ctrl_port = boost::lexical_cast<std::string>(USRP2_UDP_CTRL_PORT); -    transport::udp::sptr udp_transport = \ -        transport::udp::make(hint["addr"], ctrl_port, true); +    udp_simple::sptr udp_transport = udp_simple::make_broadcast( +        hint["addr"], ctrl_port +    );      //send a hello control packet      usrp2_ctrl_data_t ctrl_data_out; @@ -76,16 +78,18 @@ uhd::device_addrs_t usrp2::discover(const device_addr_t &hint){  /***********************************************************************   * Make   **********************************************************************/ -#define num2str(num) (boost::lexical_cast<std::string>(num)) +template <class T> std::string num2str(T num){ +    return boost::lexical_cast<std::string>(num); +}  device::sptr usrp2::make(const device_addr_t &device_addr){      //create a control transport -    transport::udp::sptr ctrl_transport = transport::udp::make( +    udp_simple::sptr ctrl_transport = udp_simple::make_connected(          device_addr["addr"], num2str(USRP2_UDP_CTRL_PORT)      );      //create a data transport -    transport::udp::sptr data_transport = transport::udp::make( +    udp_zero_copy::sptr data_transport = udp_zero_copy::make(          device_addr["addr"], num2str(USRP2_UDP_DATA_PORT)      ); @@ -99,8 +103,8 @@ device::sptr usrp2::make(const device_addr_t &device_addr){   * Structors   **********************************************************************/  usrp2_impl::usrp2_impl( -    transport::udp::sptr ctrl_transport, -    transport::udp::sptr data_transport +    udp_simple::sptr ctrl_transport, +    udp_zero_copy::sptr data_transport  ){      _ctrl_transport = ctrl_transport;      _data_transport = data_transport; diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 037aed477..a58bf8471 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -22,7 +22,8 @@  #include <boost/thread.hpp>  #include <boost/shared_ptr.hpp>  #include <boost/function.hpp> -#include <uhd/transport/udp.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <uhd/transport/udp_zero_copy.hpp>  #include <uhd/usrp/dboard_manager.hpp>  #include "fw_common.h" @@ -81,8 +82,8 @@ public:       * \param data_transport the udp transport for data       */      usrp2_impl( -        uhd::transport::udp::sptr ctrl_transport, -        uhd::transport::udp::sptr data_transport +        uhd::transport::udp_simple::sptr ctrl_transport, +        uhd::transport::udp_zero_copy::sptr data_transport      );      ~usrp2_impl(void); @@ -103,8 +104,8 @@ public:  private:      //the raw io interface (samples are in the usrp2 native format) -    size_t send_raw(const boost::asio::const_buffer &, const uhd::metadata_t &); -    size_t recv_raw(const boost::asio::mutable_buffer &, uhd::metadata_t &); +    size_t send_raw(const uhd::metadata_t &); +    void recv_raw(uhd::metadata_t &);      uhd::dict<uint32_t, size_t> _tx_stream_id_to_packet_seq;      uhd::dict<uint32_t, size_t> _rx_stream_id_to_packet_seq;      static const size_t _mtu = 1500; //FIXME we have no idea @@ -114,15 +115,16 @@ private:          USRP2_HOST_RX_VRT_TRAILER_WORDS32 -          ((2 + 14 + 20 + 8)/sizeof(uint32_t)) //size of headers (pad, eth, ip, udp)      ; -    uint32_t _tmp_send_mem[_mtu/sizeof(uint32_t)]; -    uint32_t _tmp_recv_mem[_mtu/sizeof(uint32_t)]; -    uint32_t _spillover_mem[_mtu/sizeof(uint32_t)]; -    boost::asio::mutable_buffer _splillover_buff; +    static const size_t _tx_vrt_max_offset_words32 = 7; //TODO move to future vrt lib +    uint32_t _tx_mem[_mtu/sizeof(uint32_t)]; +    boost::asio::const_buffer _tx_copy_buff; +    uhd::transport::smart_buffer::sptr _rx_smart_buff; +    boost::asio::const_buffer _rx_copy_buff;      void io_init(void);      //udp transports for control and data -    uhd::transport::udp::sptr _ctrl_transport; -    uhd::transport::udp::sptr _data_transport; +    uhd::transport::udp_simple::sptr _ctrl_transport; +    uhd::transport::udp_zero_copy::sptr _data_transport;      //private vars for dealing with send/recv control      uint32_t _ctrl_seq_num; | 
