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; |