summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--host/include/uhd/transport/CMakeLists.txt3
-rw-r--r--host/include/uhd/transport/convert_types.hpp59
-rw-r--r--host/include/uhd/transport/smart_buffer.hpp45
-rw-r--r--host/include/uhd/transport/udp_zero_copy.hpp28
-rw-r--r--host/include/uhd/transport/vrt.hpp2
-rw-r--r--host/include/uhd/transport/zero_copy.hpp133
-rw-r--r--host/lib/CMakeLists.txt1
-rwxr-xr-xhost/lib/ic_reg_maps/gen_adf4360_regs.py4
-rw-r--r--host/lib/transport/convert_types.cpp144
-rwxr-xr-xhost/lib/transport/gen_vrt.py13
-rw-r--r--host/lib/transport/udp_zero_copy_asio.cpp106
-rw-r--r--host/lib/usrp/usrp2/io_impl.cpp142
-rw-r--r--host/lib/usrp/usrp2/usrp2_impl.hpp4
13 files changed, 462 insertions, 222 deletions
diff --git a/host/include/uhd/transport/CMakeLists.txt b/host/include/uhd/transport/CMakeLists.txt
index 14b5ccd29..4cefffa24 100644
--- a/host/include/uhd/transport/CMakeLists.txt
+++ b/host/include/uhd/transport/CMakeLists.txt
@@ -17,10 +17,11 @@
INSTALL(FILES
+ convert_types.hpp
if_addrs.hpp
- smart_buffer.hpp
udp_simple.hpp
udp_zero_copy.hpp
vrt.hpp
+ zero_copy.hpp
DESTINATION ${INCLUDE_DIR}/uhd/transport
)
diff --git a/host/include/uhd/transport/convert_types.hpp b/host/include/uhd/transport/convert_types.hpp
new file mode 100644
index 000000000..a4d999240
--- /dev/null
+++ b/host/include/uhd/transport/convert_types.hpp
@@ -0,0 +1,59 @@
+//
+// 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/>.
+//
+
+#ifndef INCLUDED_UHD_TRANSPORT_CONVERT_TYPES_HPP
+#define INCLUDED_UHD_TRANSPORT_CONVERT_TYPES_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/types/io_type.hpp>
+#include <uhd/types/otw_type.hpp>
+
+namespace uhd{ namespace transport{
+
+/*!
+ * Convert IO samples to OWT samples.
+ *
+ * \param io_buff memory containing samples
+ * \param io_type the type of these samples
+ * \param otw_buff memory to write converted samples
+ * \param otw_type the type of these samples
+ * \param num_samps the number of samples in io_buff
+ */
+UHD_API void convert_io_type_to_otw_type(
+ const void *io_buff, const io_type_t &io_type,
+ void *otw_buff, const otw_type_t &otw_type,
+ size_t num_samps
+);
+
+/*!
+ * Convert OTW samples to IO samples.
+ *
+ * \param otw_buff memory containing samples
+ * \param otw_type the type of these samples
+ * \param io_buff memory to write converted samples
+ * \param io_type the type of these samples
+ * \param num_samps the number of samples in io_buff
+ */
+UHD_API void convert_otw_type_to_io_type(
+ const void *otw_buff, const otw_type_t &otw_type,
+ void *io_buff, const io_type_t &io_type,
+ size_t num_samps
+);
+
+}} //namespace
+
+#endif /* INCLUDED_UHD_TRANSPORT_CONVERT_TYPES_HPP */
diff --git a/host/include/uhd/transport/smart_buffer.hpp b/host/include/uhd/transport/smart_buffer.hpp
deleted file mode 100644
index a9bc259e9..000000000
--- a/host/include/uhd/transport/smart_buffer.hpp
+++ /dev/null
@@ -1,45 +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/>.
-//
-
-#ifndef INCLUDED_UHD_TRANSPORT_SMART_BUFFER_HPP
-#define INCLUDED_UHD_TRANSPORT_SMART_BUFFER_HPP
-
-#include <boost/asio/buffer.hpp>
-#include <boost/utility.hpp>
-#include <boost/shared_ptr.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_zero_copy.hpp b/host/include/uhd/transport/udp_zero_copy.hpp
index 0441a8e74..fd1cec46e 100644
--- a/host/include/uhd/transport/udp_zero_copy.hpp
+++ b/host/include/uhd/transport/udp_zero_copy.hpp
@@ -19,24 +19,22 @@
#define INCLUDED_UHD_TRANSPORT_UDP_ZERO_COPY_HPP
#include <uhd/config.hpp>
-#include <uhd/transport/smart_buffer.hpp>
-#include <boost/asio/buffer.hpp>
-#include <boost/utility.hpp>
+#include <uhd/transport/zero_copy.hpp>
#include <boost/shared_ptr.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.
+ * by avoiding the extra copy when recv() or send() is called on the socket.
+ * Rather, the zero copy transport gives the caller memory references.
* 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.
+ * implementation that wraps the functionality around a standard send/recv calls.
*/
-class UHD_API udp_zero_copy : boost::noncopyable{
+class UHD_API udp_zero_copy : public zero_copy_if{
public:
typedef boost::shared_ptr<udp_zero_copy> sptr;
@@ -54,22 +52,6 @@ public:
* \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.
- * Blocks until data is received or a timeout occurs.
- * The memory is managed by the implementation.
- * \return a smart buffer (empty on timeout)
- */
- virtual smart_buffer::sptr recv(void) = 0;
};
}} //namespace
diff --git a/host/include/uhd/transport/vrt.hpp b/host/include/uhd/transport/vrt.hpp
index 137698e57..f2f42f9eb 100644
--- a/host/include/uhd/transport/vrt.hpp
+++ b/host/include/uhd/transport/vrt.hpp
@@ -26,7 +26,7 @@ namespace uhd{ namespace transport{
namespace vrt{
- static const size_t max_header_words32 = 7;
+ static const size_t max_header_words32 = 5; //hdr+sid+tsi+tsf (no class id supported)
/*!
* Pack a vrt header from metadata.
diff --git a/host/include/uhd/transport/zero_copy.hpp b/host/include/uhd/transport/zero_copy.hpp
new file mode 100644
index 000000000..4fc1df9de
--- /dev/null
+++ b/host/include/uhd/transport/zero_copy.hpp
@@ -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/>.
+//
+
+#ifndef INCLUDED_UHD_TRANSPORT_ZERO_COPY_HPP
+#define INCLUDED_UHD_TRANSPORT_ZERO_COPY_HPP
+
+#include <uhd/config.hpp>
+#include <boost/asio/buffer.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace uhd{ namespace transport{
+
+/*!
+ * A managed receive buffer:
+ * Contains a reference to transport-managed memory,
+ * and a method to release the memory after reading.
+ */
+class UHD_API managed_recv_buffer : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<managed_recv_buffer> sptr;
+
+ /*!
+ * Signal to the transport that we are done with the buffer.
+ * This should be called to release the buffer to the transport.
+ * After calling, the referenced memory should be considered invalid.
+ */
+ virtual void done(void) = 0;
+
+ /*!
+ * Get the size of the underlying buffer.
+ * \return the number of bytes
+ */
+ size_t size(void){
+ return boost::asio::buffer_size(this->get());
+ }
+
+ /*!
+ * Get a pointer to the underlying buffer.
+ * \return a pointer into memory
+ */
+ template <class T> T cast(void){
+ return boost::asio::buffer_cast<T>(this->get());
+ }
+
+private:
+ /*!
+ * Get a reference to the internal const buffer.
+ * The buffer has a reference to memory and a size.
+ * \return a boost asio const buffer
+ */
+ virtual const boost::asio::const_buffer &get(void) = 0;
+};
+
+/*!
+ * A managed send buffer:
+ * Contains a reference to transport-managed memory,
+ * and a method to release the memory after writing.
+ */
+class UHD_API managed_send_buffer : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<managed_send_buffer> sptr;
+
+ /*!
+ * Signal to the transport that we are done with the buffer.
+ * This should be called to commit the write to the transport object.
+ * After calling, the referenced memory should be considered invalid.
+ * \param num_bytes the number of bytes written into the buffer
+ */
+ virtual void done(size_t num_bytes) = 0;
+
+ /*!
+ * Get the size of the underlying buffer.
+ * \return the number of bytes
+ */
+ size_t size(void){
+ return boost::asio::buffer_size(this->get());
+ }
+
+ /*!
+ * Get a pointer to the underlying buffer.
+ * \return a pointer into memory
+ */
+ template <class T> T cast(void){
+ return boost::asio::buffer_cast<T>(this->get());
+ }
+
+private:
+ /*!
+ * Get a reference to the internal mutable buffer.
+ * The buffer has a reference to memory and a size.
+ * \return a boost asio mutable buffer
+ */
+ virtual const boost::asio::mutable_buffer &get(void) = 0;
+};
+
+/*!
+ * A zero-copy interface for transport objects.
+ * Provides a way to get send and receive buffers
+ * with memory managed by the transport object.
+ */
+class UHD_API zero_copy_if : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<zero_copy_if> sptr;
+
+ /*!
+ * Get a new receive buffer from this transport object.
+ */
+ virtual managed_recv_buffer::sptr get_recv_buff(void) = 0;
+
+ /*!
+ * Get a new send buffer from this transport object.
+ */
+ virtual managed_send_buffer::sptr get_send_buff(void) = 0;
+};
+
+}} //namespace
+
+#endif /* INCLUDED_UHD_TRANSPORT_ZERO_COPY_HPP */
diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt
index 58afe099d..52ed809ab 100644
--- a/host/lib/CMakeLists.txt
+++ b/host/lib/CMakeLists.txt
@@ -51,6 +51,7 @@ SET(libuhd_sources
load_modules.cpp
types.cpp
wax.cpp
+ transport/convert_types.cpp
transport/if_addrs.cpp
transport/udp_simple.cpp
usrp/dboard/db_basic_and_lf.cpp
diff --git a/host/lib/ic_reg_maps/gen_adf4360_regs.py b/host/lib/ic_reg_maps/gen_adf4360_regs.py
index 702c3060f..e7c2c9435 100755
--- a/host/lib/ic_reg_maps/gen_adf4360_regs.py
+++ b/host/lib/ic_reg_maps/gen_adf4360_regs.py
@@ -20,10 +20,13 @@
# Boston, MA 02110-1301, USA.
import re
+import os
import sys
from Cheetah.Template import Template
def parse_tmpl(_tmpl_text, **kwargs):
return str(Template(_tmpl_text, kwargs))
+def safe_makedirs(path):
+ not os.path.isdir(path) and os.makedirs(path)
########################################################################
# Template for raw text data describing registers
@@ -166,4 +169,5 @@ class reg:
if __name__ == '__main__':
regs = map(reg, parse_tmpl(REGS_DATA_TMPL).splitlines())
+ safe_makedirs(os.path.dirname(sys.argv[1]))
open(sys.argv[1], 'w').write(parse_tmpl(HEADER_TEXT, regs=regs, file=__file__))
diff --git a/host/lib/transport/convert_types.cpp b/host/lib/transport/convert_types.cpp
new file mode 100644
index 000000000..2a6854f50
--- /dev/null
+++ b/host/lib/transport/convert_types.cpp
@@ -0,0 +1,144 @@
+//
+// 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/convert_types.hpp>
+#include <uhd/utils/assert.hpp>
+#include <boost/asio.hpp> //endianness conversion
+#include <boost/cstdint.hpp>
+
+using namespace uhd;
+
+/***********************************************************************
+ * Constants
+ **********************************************************************/
+typedef std::complex<float> fc32_t;
+
+static const float shorts_per_float = float(1 << 15);
+static const float floats_per_short = float(1.0/shorts_per_float);
+
+#define unrolled_loop(__inst, __len){ \
+ size_t __i = 0; \
+ for(; __i < (__len & ~0x3); __i+= 4){ \
+ __inst(__i+0); __inst(__i+1); \
+ __inst(__i+2); __inst(__i+3); \
+ } \
+ for(; __i < __len; __i++){ \
+ __inst(__i); \
+ } \
+}
+
+// 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(
+ boost::uint32_t *usrp2_items,
+ const fc32_t *host_floats,
+ size_t num_samps
+){
+ #define host_floats_to_usrp2_items_i(i){ \
+ boost::uint16_t real = boost::int16_t(host_floats[i].real()*shorts_per_float); \
+ boost::uint16_t imag = boost::int16_t(host_floats[i].imag()*shorts_per_float); \
+ usrp2_items[i] = htonl((real << 16) | (imag << 0)); \
+ }
+ unrolled_loop(host_floats_to_usrp2_items_i, num_samps);
+}
+
+static inline void usrp2_items_to_host_floats(
+ fc32_t *host_floats,
+ const boost::uint32_t *usrp2_items,
+ size_t num_samps
+){
+ #define usrp2_items_to_host_floats_i(i){ \
+ boost::uint32_t item = ntohl(usrp2_items[i]); \
+ boost::int16_t real = boost::uint16_t(item >> 16); \
+ boost::int16_t imag = boost::uint16_t(item >> 0); \
+ host_floats[i] = fc32_t(float(real*floats_per_short), float(imag*floats_per_short)); \
+ }
+ unrolled_loop(usrp2_items_to_host_floats_i, num_samps);
+}
+
+static inline void host_items_to_usrp2_items(
+ boost::uint32_t *usrp2_items,
+ const boost::uint32_t *host_items,
+ size_t num_samps
+){
+ #define host_items_to_usrp2_items_i(i) usrp2_items[i] = htonl(host_items[i])
+ if (is_big_endian){
+ std::memcpy(usrp2_items, host_items, num_samps*sizeof(boost::uint32_t));
+ }
+ else{
+ unrolled_loop(host_items_to_usrp2_items_i, num_samps);
+ }
+}
+
+static inline void usrp2_items_to_host_items(
+ boost::uint32_t *host_items,
+ const boost::uint32_t *usrp2_items,
+ size_t num_samps
+){
+ #define usrp2_items_to_host_items_i(i) host_items[i] = ntohl(usrp2_items[i])
+ if (is_big_endian){
+ std::memcpy(host_items, usrp2_items, num_samps*sizeof(boost::uint32_t));
+ }
+ else{
+ unrolled_loop(usrp2_items_to_host_items_i, num_samps);
+ }
+}
+
+void transport::convert_io_type_to_otw_type(
+ const void *io_buff, const io_type_t &io_type,
+ void *otw_buff, const otw_type_t &otw_type,
+ size_t num_samps
+){
+ //all we handle for now:
+ ASSERT_THROW(otw_type.width == 16 and otw_type.byteorder == otw_type_t::BO_BIG_ENDIAN);
+
+ switch(io_type.tid){
+ case io_type_t::COMPLEX_FLOAT32:
+ host_floats_to_usrp2_items((boost::uint32_t *)otw_buff, (const fc32_t*)io_buff, num_samps);
+ break;
+ case io_type_t::COMPLEX_INT16:
+ host_items_to_usrp2_items((boost::uint32_t *)otw_buff, (const boost::uint32_t*)io_buff, num_samps);
+ break;
+ default:
+ throw std::runtime_error(str(boost::format("convert_types: cannot handle type \"%c\"") % io_type.tid));
+ }
+}
+
+void transport::convert_otw_type_to_io_type(
+ const void *otw_buff, const otw_type_t &otw_type,
+ void *io_buff, const io_type_t &io_type,
+ size_t num_samps
+){
+ //all we handle for now:
+ ASSERT_THROW(otw_type.width == 16 and otw_type.byteorder == otw_type_t::BO_BIG_ENDIAN);
+
+ switch(io_type.tid){
+ case io_type_t::COMPLEX_FLOAT32:
+ usrp2_items_to_host_floats((fc32_t*)io_buff, (const boost::uint32_t *)otw_buff, num_samps);
+ break;
+ case io_type_t::COMPLEX_INT16:
+ usrp2_items_to_host_items((boost::uint32_t*)io_buff, (const boost::uint32_t *)otw_buff, num_samps);
+ break;
+ default:
+ throw std::runtime_error(str(boost::format("convert_types: cannot handle type \"%c\"") % io_type.tid));
+ }
+}
diff --git a/host/lib/transport/gen_vrt.py b/host/lib/transport/gen_vrt.py
index 0f961efc2..38a394dee 100755
--- a/host/lib/transport/gen_vrt.py
+++ b/host/lib/transport/gen_vrt.py
@@ -200,10 +200,15 @@ void vrt::unpack(
}
"""
+import os
import sys
-from Cheetah import Template
-def parse_str(_tmpl_text, **kwargs): return str(Template.Template(_tmpl_text, kwargs))
+
+from Cheetah.Template import Template
+def parse_tmpl(_tmpl_text, **kwargs):
+ return str(Template(_tmpl_text, kwargs))
+def safe_makedirs(path):
+ not os.path.isdir(path) and os.makedirs(path)
if __name__ == '__main__':
- from Cheetah import Template
- open(sys.argv[1], 'w').write(parse_str(TMPL_TEXT, file=__file__))
+ safe_makedirs(os.path.dirname(sys.argv[1]))
+ open(sys.argv[1], 'w').write(parse_tmpl(TMPL_TEXT, file=__file__))
diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp
index 1fc8ce14a..7e643abad 100644
--- a/host/lib/transport/udp_zero_copy_asio.cpp
+++ b/host/lib/transport/udp_zero_copy_asio.cpp
@@ -25,39 +25,74 @@
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 [].
+ * Managed receive buffer implementation for udp zero-copy asio:
+ * Frees the memory held by the const buffer on done.
**********************************************************************/
-class smart_buffer_impl : public smart_buffer{
+class managed_recv_buffer_impl : public managed_recv_buffer{
public:
- smart_buffer_impl(const boost::asio::const_buffer &buff){
- _buff = buff;
+ managed_recv_buffer_impl(const boost::asio::const_buffer &buff) : _buff(buff){
+ _done = false;
}
- ~smart_buffer_impl(void){
+ ~managed_recv_buffer_impl(void){
+ if (not _done) this->done();
+ }
+
+ void done(void){
+ _done = true;
delete [] boost::asio::buffer_cast<const boost::uint32_t *>(_buff);
}
- const boost::asio::const_buffer &get(void) const{
+private:
+ const boost::asio::const_buffer &get(void){
return _buff;
}
+ const boost::asio::const_buffer _buff;
+ bool _done;
+};
+
+/***********************************************************************
+ * Managed send buffer implementation for udp zero-copy asio:
+ * Sends and frees the memory held by the mutable buffer on done.
+ **********************************************************************/
+class managed_send_buffer_impl : public managed_send_buffer{
+public:
+ managed_send_buffer_impl(
+ const boost::asio::mutable_buffer &buff,
+ boost::asio::ip::udp::socket *socket
+ ) : _buff(buff){
+ _done = false;
+ _socket = socket;
+ }
+
+ ~managed_send_buffer_impl(void){
+ if (not _done) this->done(0);
+ }
+
+ void done(size_t num_bytes){
+ _done = true;
+ boost::uint32_t *mem = boost::asio::buffer_cast<boost::uint32_t *>(_buff);
+ _socket->send(boost::asio::buffer(mem, num_bytes));
+ delete [] mem;
+ }
+
private:
- boost::asio::const_buffer _buff;
+ const boost::asio::mutable_buffer &get(void){
+ return _buff;
+ }
+
+ const boost::asio::mutable_buffer _buff;
+ boost::asio::ip::udp::socket *_socket;
+ bool _done;
};
/***********************************************************************
- * 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.
+ * Zero Copy UDP implementation with ASIO:
+ * This is the portable zero copy implementation for systems
+ * where a faster, platform specific solution is not available.
+ * However, it is not a true zero copy implementation as each
+ * send and recv requires a copy operation to/from userspace.
**********************************************************************/
class udp_zero_copy_impl : public udp_zero_copy{
public:
@@ -66,8 +101,8 @@ public:
~udp_zero_copy_impl(void);
//send/recv
- size_t send(const boost::asio::const_buffer &buff);
- smart_buffer::sptr recv(void);
+ managed_recv_buffer::sptr get_recv_buff(void);
+ managed_send_buffer::sptr get_send_buff(void);
private:
boost::asio::ip::udp::socket *_socket;
@@ -107,31 +142,32 @@ 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 = 0;
-
+managed_recv_buffer::sptr udp_zero_copy_impl::get_recv_buff(void){
//implement timeout through polling and sleeping
+ size_t available = 0;
boost::asio::deadline_timer timer(_socket->get_io_service());
timer.expires_from_now(boost::posix_time::milliseconds(100));
while (not ((available = _socket->available()) or timer.expires_from_now().is_negative())){
boost::this_thread::sleep(boost::posix_time::milliseconds(1));
}
- //allocate memory and create buffer
- boost::uint32_t *buff_mem = new boost::uint32_t[available/sizeof(boost::uint32_t)];
- boost::asio::mutable_buffer buff(buff_mem, available);
-
//receive only if data is available
+ boost::uint32_t *buff_mem = new boost::uint32_t[available/sizeof(boost::uint32_t)];
if (available){
- _socket->receive(boost::asio::buffer(buff));
+ available = _socket->receive(boost::asio::buffer(buff_mem, available));
}
- //create a new smart buffer to house the data
- return smart_buffer::sptr(new smart_buffer_impl(buff));
+ //create a new managed buffer to house the data
+ return managed_recv_buffer::sptr(
+ new managed_recv_buffer_impl(boost::asio::buffer(buff_mem, available))
+ );
+}
+
+managed_send_buffer::sptr udp_zero_copy_impl::get_send_buff(void){
+ boost::uint32_t *buff_mem = new boost::uint32_t[2000/sizeof(boost::uint32_t)];
+ return managed_send_buffer::sptr(
+ new managed_send_buffer_impl(boost::asio::buffer(buff_mem, 2000), _socket)
+ );
}
size_t udp_zero_copy_impl::get_recv_buff_size(void){
diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp
index eb6b5f7b9..a760cbe3d 100644
--- a/host/lib/usrp/usrp2/io_impl.cpp
+++ b/host/lib/usrp/usrp2/io_impl.cpp
@@ -16,6 +16,7 @@
//
#include "usrp2_impl.hpp"
+#include <uhd/transport/convert_types.hpp>
#include <boost/format.hpp>
#include <complex>
#include <iostream>
@@ -26,101 +27,26 @@ using namespace uhd::transport;
namespace asio = boost::asio;
/***********************************************************************
- * Constants
- **********************************************************************/
-typedef std::complex<float> fc32_t;
-typedef std::complex<boost::int16_t> sc16_t;
-
-static const float shorts_per_float = float(1 << 15);
-static const float floats_per_short = float(1.0/shorts_per_float);
-
-/***********************************************************************
* Helper Functions
**********************************************************************/
void usrp2_impl::io_init(void){
+ //setup otw type
+ _otw_type.width = 16;
+ _otw_type.shift = 0;
+ _otw_type.byteorder = otw_type_t::BO_BIG_ENDIAN;
+
//initially empty copy buffer
_rx_copy_buff = asio::buffer("", 0);
//send a small data packet so the usrp2 knows the udp source port
//and the maximum number of lines (32 bit words) per packet
+ managed_send_buffer::sptr send_buff = _data_transport->get_send_buff();
boost::uint32_t data[2] = {
htonl(USRP2_INVALID_VRT_HEADER),
htonl(_max_rx_samples_per_packet)
};
- _data_transport->send(asio::buffer(&data, sizeof(data)));
-}
-
-#define unrolled_loop(__inst, __len){ \
- size_t __i = 0; \
- for(; __i < (__len & ~0x3); __i+= 4){ \
- __inst(__i+0); __inst(__i+1); \
- __inst(__i+2); __inst(__i+3); \
- } \
- for(; __i < __len; __i++){ \
- __inst(__i); \
- } \
-}
-
-// 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(
- boost::uint32_t *usrp2_items,
- const fc32_t *host_floats,
- size_t num_samps
-){
- #define host_floats_to_usrp2_items_i(i){ \
- boost::uint16_t real = boost::int16_t(host_floats[i].real()*shorts_per_float); \
- boost::uint16_t imag = boost::int16_t(host_floats[i].imag()*shorts_per_float); \
- usrp2_items[i] = htonl((real << 16) | (imag << 0)); \
- }
- unrolled_loop(host_floats_to_usrp2_items_i, num_samps);
-}
-
-static inline void usrp2_items_to_host_floats(
- fc32_t *host_floats,
- const boost::uint32_t *usrp2_items,
- size_t num_samps
-){
- #define usrp2_items_to_host_floats_i(i){ \
- boost::uint32_t item = ntohl(usrp2_items[i]); \
- boost::int16_t real = boost::uint16_t(item >> 16); \
- boost::int16_t imag = boost::uint16_t(item >> 0); \
- host_floats[i] = fc32_t(float(real*floats_per_short), float(imag*floats_per_short)); \
- }
- unrolled_loop(usrp2_items_to_host_floats_i, num_samps);
-}
-
-static inline void host_items_to_usrp2_items(
- boost::uint32_t *usrp2_items,
- const boost::uint32_t *host_items,
- size_t num_samps
-){
- #define host_items_to_usrp2_items_i(i) usrp2_items[i] = htonl(host_items[i])
- if (is_big_endian){
- std::memcpy(usrp2_items, host_items, num_samps*sizeof(boost::uint32_t));
- }
- else{
- unrolled_loop(host_items_to_usrp2_items_i, num_samps);
- }
-}
-
-static inline void usrp2_items_to_host_items(
- boost::uint32_t *host_items,
- const boost::uint32_t *usrp2_items,
- size_t num_samps
-){
- #define usrp2_items_to_host_items_i(i) host_items[i] = ntohl(usrp2_items[i])
- if (is_big_endian){
- std::memcpy(host_items, usrp2_items, num_samps*sizeof(boost::uint32_t));
- }
- else{
- unrolled_loop(usrp2_items_to_host_items_i, num_samps);
- }
+ memcpy(send_buff->cast<void*>(), data, sizeof(data));
+ send_buff->done(sizeof(data));
}
/***********************************************************************
@@ -128,15 +54,15 @@ static inline void usrp2_items_to_host_items(
**********************************************************************/
void usrp2_impl::recv_raw(rx_metadata_t &metadata){
//do a receive
- _rx_smart_buff = _data_transport->recv();
+ _rx_smart_buff = _data_transport->get_recv_buff();
//unpack the vrt header
- size_t num_packet_words32 = asio::buffer_size(_rx_smart_buff->get())/sizeof(boost::uint32_t);
+ size_t num_packet_words32 = _rx_smart_buff->size()/sizeof(boost::uint32_t);
if (num_packet_words32 == 0){
_rx_copy_buff = boost::asio::buffer("", 0);
return; //must exit here after setting the buffer
}
- const boost::uint32_t *vrt_hdr = asio::buffer_cast<const boost::uint32_t *>(_rx_smart_buff->get());
+ const boost::uint32_t *vrt_hdr = _rx_smart_buff->cast<const boost::uint32_t *>();
size_t num_header_words32_out, num_payload_words32_out, packet_count_out;
try{
vrt::unpack(
@@ -178,10 +104,12 @@ size_t usrp2_impl::send(
){
tx_metadata_t metadata = metadata_; //rw copy to change later
- boost::uint32_t tx_mem[_mtu/sizeof(boost::uint32_t)];
- size_t num_samps = std::min(
+ transport::managed_send_buffer::sptr send_buff = _data_transport->get_send_buff();
+ boost::uint32_t *tx_mem = send_buff->cast<boost::uint32_t *>();
+ size_t num_samps = std::min(std::min(
asio::buffer_size(buff)/io_type.size,
- size_t(_max_tx_samples_per_packet)
+ size_t(_max_tx_samples_per_packet)),
+ send_buff->size()/io_type.size
);
//kill the end of burst flag if this is a fragment
@@ -204,20 +132,15 @@ size_t usrp2_impl::send(
boost::uint32_t *items = tx_mem + num_header_words32; //offset for data
- //copy the samples into the send buffer
- switch(io_type.tid){
- case io_type_t::COMPLEX_FLOAT32:
- host_floats_to_usrp2_items(items, asio::buffer_cast<const fc32_t*>(buff), num_samps);
- break;
- case io_type_t::COMPLEX_INT16:
- host_items_to_usrp2_items(items, asio::buffer_cast<const boost::uint32_t*>(buff), num_samps);
- break;
- default:
- throw std::runtime_error(str(boost::format("usrp2 send: cannot handle type \"%c\"") % io_type.tid));
- }
+ //copy-convert the samples into the send buffer
+ convert_io_type_to_otw_type(
+ asio::buffer_cast<const void*>(buff), io_type,
+ (void*)items, _otw_type,
+ num_samps
+ );
//send and return number of samples
- _data_transport->send(asio::buffer(tx_mem, num_packet_words32*sizeof(boost::uint32_t)));
+ send_buff->done(num_packet_words32*sizeof(boost::uint32_t));
return num_samps;
}
@@ -254,17 +177,12 @@ size_t usrp2_impl::recv(
metadata.fragment_offset = _fragment_offset_in_samps;
_fragment_offset_in_samps += num_samps; //set for next time
- //copy the samples from the recv buffer
- switch(io_type.tid){
- case io_type_t::COMPLEX_FLOAT32:
- usrp2_items_to_host_floats(asio::buffer_cast<fc32_t*>(buff), items, num_samps);
- break;
- case io_type_t::COMPLEX_INT16:
- usrp2_items_to_host_items(asio::buffer_cast<boost::uint32_t*>(buff), items, num_samps);
- break;
- default:
- throw std::runtime_error(str(boost::format("usrp2 recv: cannot handle type \"%c\"") % io_type.tid));
- }
+ //copy-convert the samples from the recv buffer
+ convert_otw_type_to_io_type(
+ (const void*)items, _otw_type,
+ asio::buffer_cast<void*>(buff), io_type,
+ num_samps
+ );
//update the rx copy buffer to reflect the bytes copied
_rx_copy_buff = asio::buffer(
diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp
index baa6530b8..4e7154afa 100644
--- a/host/lib/usrp/usrp2/usrp2_impl.hpp
+++ b/host/lib/usrp/usrp2/usrp2_impl.hpp
@@ -20,6 +20,7 @@
#include <uhd/usrp/usrp2.hpp>
#include <uhd/types/dict.hpp>
+#include <uhd/types/otw_type.hpp>
#include <uhd/types/stream_cmd.hpp>
#include <uhd/types/clock_config.hpp>
#include <boost/asio.hpp>
@@ -136,9 +137,10 @@ private:
(_mtu - _hdrs)/sizeof(boost::uint32_t) -
uhd::transport::vrt::max_header_words32
;
- uhd::transport::smart_buffer::sptr _rx_smart_buff;
+ uhd::transport::managed_recv_buffer::sptr _rx_smart_buff;
boost::asio::const_buffer _rx_copy_buff;
size_t _fragment_offset_in_samps;
+ uhd::otw_type_t _otw_type;
void io_init(void);
//udp transports for control and data