summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--host/AUTHORS3
-rw-r--r--host/README4
-rw-r--r--host/docs/build.rst21
-rw-r--r--host/docs/usrp1.rst35
-rw-r--r--host/docs/usrp2.rst16
-rw-r--r--host/examples/test_async_messages.cpp2
-rw-r--r--host/include/uhd/transport/usb_control.hpp4
-rw-r--r--host/include/uhd/transport/usb_device_handle.hpp6
-rw-r--r--host/include/uhd/transport/usb_zero_copy.hpp24
-rw-r--r--host/include/uhd/transport/zero_copy.hpp11
-rw-r--r--host/include/uhd/usrp/dboard_iface.hpp9
-rw-r--r--host/lib/ic_reg_maps/CMakeLists.txt5
-rwxr-xr-x[-rw-r--r--]host/lib/ic_reg_maps/gen_max2118_regs.py0
-rw-r--r--host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py75
-rw-r--r--host/lib/transport/CMakeLists.txt7
-rw-r--r--host/lib/transport/libusb1_base.cpp309
-rw-r--r--host/lib/transport/libusb1_base.hpp171
-rw-r--r--host/lib/transport/libusb1_control.cpp75
-rw-r--r--host/lib/transport/libusb1_device_handle.cpp117
-rw-r--r--host/lib/transport/libusb1_zero_copy.cpp647
-rw-r--r--host/lib/transport/msvc/stdint.h (renamed from host/lib/transport/include/stdint.h)0
-rw-r--r--host/lib/transport/udp_zero_copy_asio.cpp5
-rw-r--r--host/lib/transport/zero_copy.cpp4
-rw-r--r--host/lib/usrp/dboard/CMakeLists.txt1
-rw-r--r--host/lib/usrp/dboard/db_tvrx.cpp477
-rw-r--r--host/lib/usrp/dboard/db_wbx.cpp2
-rw-r--r--host/lib/usrp/dboard_manager.cpp25
-rw-r--r--host/lib/usrp/usrp1/codec_ctrl.cpp14
-rw-r--r--host/lib/usrp/usrp1/codec_ctrl.hpp3
-rw-r--r--host/lib/usrp/usrp1/dboard_iface.cpp11
-rw-r--r--host/lib/usrp/usrp1/io_impl.cpp8
-rw-r--r--host/lib/usrp/usrp1/usrp1_ctrl.cpp16
-rw-r--r--host/lib/usrp/usrp1/usrp1_iface.cpp16
-rw-r--r--host/lib/usrp/usrp1/usrp1_iface.hpp14
-rw-r--r--host/lib/usrp/usrp1/usrp1_impl.cpp46
-rw-r--r--host/lib/usrp/usrp2/dboard_iface.cpp4
-rw-r--r--host/lib/usrp/usrp2/io_impl.cpp5
37 files changed, 1336 insertions, 856 deletions
diff --git a/host/AUTHORS b/host/AUTHORS
index e0775f3a1..512d4752e 100644
--- a/host/AUTHORS
+++ b/host/AUTHORS
@@ -24,6 +24,7 @@ Tom Tsou - ttsou@vt.edu
USRP1 host code
USRP1 firmware
-Nick Foster - nick@nerdnetworks.org
+Nick Foster - nick@ettus.com
LIBUSB host code
USRP1 host code
+ TVRX host code
diff --git a/host/README b/host/README
index 5018ef541..cab1e0b10 100644
--- a/host/README
+++ b/host/README
@@ -6,7 +6,8 @@ The hardware driver for Ettus Research products.
########################################################################
# Supported USRP Motherboards
########################################################################
-USRP2 - udp over gigabit ethernet
+USRP1
+USRP2
########################################################################
# Supported USRP Daughterboards
@@ -19,6 +20,7 @@ RFX Series
XCVR 2450
WBX Series
DBSRX
+TVRX
########################################################################
# Documentation
diff --git a/host/docs/build.rst b/host/docs/build.rst
index f37b5dce7..9cf37db4a 100644
--- a/host/docs/build.rst
+++ b/host/docs/build.rst
@@ -58,7 +58,8 @@ LibUSB
* **Purpose:** USB-based hardware support
* **Version:** at least 1.0
* **Usage:** build time + run time (optional)
-* **Download URL:** http://www.libusb.org/
+* **Download URL:** http://sourceforge.net/projects/libusb/files/libusb-1.0/
+* **Download URL (windows binaries):** http://www.libusb.org/wiki/windows_backend#LatestBinarySnapshots
^^^^^^^^^^^^^^^^
Python
@@ -152,6 +153,19 @@ Generate the project with cmake
* Click generate and a project file will be created in the build directory.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LibUSB cmake notes
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+On Windows, cmake does not have the advantage of pkg-config,
+so we must manually tell cmake how to locate the LibUSB header and lib.
+
+From the cmake gui, select "Advanded View":
+
+* Set LIBUSB_INCLUDE_DIR to the directory with "libusb.h".
+* Set LIBUSB_LIBRARIES to the full path for "libusb-1.0.lib".
+
+Then check the boxes to enable USRP1 support, click configure and generate.
+
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Build the project in MSVC
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Open the generated project file in MSVC.
@@ -177,3 +191,8 @@ Setup the PATH environment variable
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Add the boost library path to %PATH% (usually c:\\program files\\boost\\<version>\\lib)
* Add the uhd library path to %PATH% (usually c:\\program files\\uhd\\lib)
+* Add the libusb library to %PATH% (if using usb support)
+
+**Note:**
+The interface for editing environment variable paths in Windows is very poor.
+I recommend using "Rapid Environment Editor" (http://www.rapidee.com) over the default editor.
diff --git a/host/docs/usrp1.rst b/host/docs/usrp1.rst
index 3c1431d30..0baa93a45 100644
--- a/host/docs/usrp1.rst
+++ b/host/docs/usrp1.rst
@@ -60,6 +60,29 @@ Example device address string representations to specify non-standard firmware a
fpga=usrp1_fpga_4rx.rbf, fw=usrp1_fw_custom.ihx
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Change USB transfer parameters
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+The advanced user may manipulate parameters of the usb bulk transfers
+for various reasons, such as lowering latency or increasing buffer size.
+By default, the UHD will use values for these parameters
+that are known to perform well on a variety of systems.
+The following device address parameters can be used to manipulate USB bulk transfers:
+
+* **recv_xfer_size:** the size of each receive bulk transfer in bytes
+* **recv_num_xfers:** the number of simultaneous receive bulk transfers
+* **send_xfer_size:** the size of each send bulk transfer in bytes
+* **send_num_xfers:** the number of simultaneous send bulk transfers
+
+Example usage, set the device address markup string to the following:
+::
+
+ serial=12345678, recv_num_xfers=16
+
+ -- OR --
+
+ serial=12345678, recv_xfer_size=2048, recv_num_xfers=16
+
------------------------------------------------------------------------
Specifying the subdevice to use
------------------------------------------------------------------------
@@ -100,10 +123,10 @@ OS Specific Notes
------------------------------------------------------------------------
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Setup Udev on Linux
+Linux - setup udev
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-On Linux, Udev handles USB plug and unplug events.
-The following command creates a Udev rule for the USRP1
+On Linux, udev handles USB plug and unplug events.
+The following commands create a udev rule for the USRP1
so that non-root users may access the device:
::
@@ -113,3 +136,9 @@ so that non-root users may access the device:
sudo mv tmpfile /etc/udev/rules.d/10-usrp.rules
sudo udevadm control --reload-rules
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Windows - install driver
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+On Windows, a driver must be installed the first time the USRP1 is attached to the host computer.
+A download link for this driver can be found on the UHD wiki page.
+Download and unpack the driver, and direct the Windows driver install wizard to the .inf file.
diff --git a/host/docs/usrp2.rst b/host/docs/usrp2.rst
index 4c95fb24c..70e5ea28b 100644
--- a/host/docs/usrp2.rst
+++ b/host/docs/usrp2.rst
@@ -201,7 +201,7 @@ Each parameter will accept a numeric value for the number of bytes.
* recv_buff_size
* send_buff_size
-Example, set the args string to the following:
+Example usage, set the device address markup string to the following:
::
addr=192.168.10.2, recv_buff_size=100e6
@@ -211,6 +211,20 @@ Hardware setup notes
------------------------------------------------------------------------
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Front panel LEDs
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+The LEDs on the front panel can be useful in debugging hardware and software issues.
+The LEDs reveal the following about the state of the device:
+
+* **LED A:** transmitting
+* **LED B:** undocumented
+* **LED C:** receiving
+* **LED D:** firmware loaded
+* **LED E:** undocumented
+* **LED F:** FPGA loaded
+
+
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Ref Clock - 10MHz
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Using an external 10MHz reference clock requires a signal level between
diff --git a/host/examples/test_async_messages.cpp b/host/examples/test_async_messages.cpp
index 4c9d18121..0a5c076ea 100644
--- a/host/examples/test_async_messages.cpp
+++ b/host/examples/test_async_messages.cpp
@@ -58,7 +58,7 @@ void test_no_async_message(uhd::usrp::single_usrp::sptr sdev){
" Got unexpected event code 0x%x.\n"
) % async_md.event_code << std::endl;
//clear the async messages
- while (dev->recv_async_msg(async_md, 0));
+ while (dev->recv_async_msg(async_md, 0)){};
}
else{
std::cout << boost::format(
diff --git a/host/include/uhd/transport/usb_control.hpp b/host/include/uhd/transport/usb_control.hpp
index 6137ecf84..f9829c3ec 100644
--- a/host/include/uhd/transport/usb_control.hpp
+++ b/host/include/uhd/transport/usb_control.hpp
@@ -50,9 +50,9 @@ public:
* \param index 2-byte (wIndex)
* \param buff buffer to hold send or receive data
* \param length 2-byte (wLength)
- * \return number of bytes submitted
+ * \return number of bytes submitted or error code
*/
- virtual size_t submit(boost::uint8_t request_type,
+ virtual ssize_t submit(boost::uint8_t request_type,
boost::uint8_t request,
boost::uint16_t value,
boost::uint16_t index,
diff --git a/host/include/uhd/transport/usb_device_handle.hpp b/host/include/uhd/transport/usb_device_handle.hpp
index 735a3acbe..9bb7db9c4 100644
--- a/host/include/uhd/transport/usb_device_handle.hpp
+++ b/host/include/uhd/transport/usb_device_handle.hpp
@@ -61,12 +61,6 @@ public:
virtual boost::uint16_t get_product_id() const = 0;
/*!
- * Return the device's USB address
- * \return a Product ID
- */
- virtual boost::uint16_t get_device_addr() const = 0;
-
- /*!
* Return a vector of USB devices on this host
* \return a vector of USB device handles that match vid and pid
*/
diff --git a/host/include/uhd/transport/usb_zero_copy.hpp b/host/include/uhd/transport/usb_zero_copy.hpp
index 75232c22a..61bf380ba 100644
--- a/host/include/uhd/transport/usb_zero_copy.hpp
+++ b/host/include/uhd/transport/usb_zero_copy.hpp
@@ -45,16 +45,22 @@ public:
* The underlying implementation may be platform specific.
*
* \param handle a device handle that uniquely identifying the device
- * \param rx_endpoint an integer specifiying an IN endpoint number
- * \param tx_endpoint an integer specifiying an OUT endpoint number
- * \param buff_size total number of bytes of buffer space to allocate
- * \param block_size number of bytes allocated for each I/O transaction
+ * \param recv_endpoint an integer specifiying an IN endpoint number
+ * \param send_endpoint an integer specifiying an OUT endpoint number
+ * \param recv_xfer_size the number of bytes for each receive transfer
+ * \param recv_num_xfers the number of simultaneous receive transfers
+ * \param send_xfer_size the number of bytes for each send transfer
+ * \param send_num_xfers the number of simultaneous send transfers
*/
- static sptr make(usb_device_handle::sptr handle,
- unsigned int rx_endpoint,
- unsigned int tx_endpoint,
- size_t buff_size = 0,
- size_t block_size = 0);
+ static sptr make(
+ usb_device_handle::sptr handle,
+ unsigned int recv_endpoint,
+ unsigned int send_endpoint,
+ size_t recv_xfer_size = 0,
+ size_t recv_num_xfers = 0,
+ size_t send_xfer_size = 0,
+ size_t send_num_xfers = 0
+ );
};
}} //namespace
diff --git a/host/include/uhd/transport/zero_copy.hpp b/host/include/uhd/transport/zero_copy.hpp
index 513291b63..8ecafd3fb 100644
--- a/host/include/uhd/transport/zero_copy.hpp
+++ b/host/include/uhd/transport/zero_copy.hpp
@@ -122,9 +122,10 @@ namespace uhd{ namespace transport{
/*!
* Get a new receive buffer from this transport object.
+ * \param timeout_ms the timeout to get the buffer in ms
* \return a managed buffer, or null sptr on timeout/error
*/
- virtual managed_recv_buffer::sptr get_recv_buff(void) = 0;
+ virtual managed_recv_buffer::sptr get_recv_buff(size_t timeout_ms) = 0;
/*!
* Get the maximum number of receive frames:
@@ -171,16 +172,19 @@ namespace uhd{ namespace transport{
/*!
* Get a new receive buffer from this transport object.
+ * \param timeout_ms the timeout to get the buffer in ms
+ * \return a managed buffer, or null sptr on timeout/error
*/
- managed_recv_buffer::sptr get_recv_buff(void);
+ managed_recv_buffer::sptr get_recv_buff(size_t timeout_ms);
private:
/*!
* Perform a private copying recv.
* \param buff the buffer to write data into
+ * \param timeout_ms the timeout to get the buffer in ms
* \return the number of bytes written to buff, 0 for timeout, negative for error
*/
- virtual ssize_t recv(const boost::asio::mutable_buffer &buff) = 0;
+ virtual ssize_t recv(const boost::asio::mutable_buffer &buff, size_t timeout_ms) = 0;
UHD_PIMPL_DECL(impl) _impl;
};
@@ -204,6 +208,7 @@ namespace uhd{ namespace transport{
/*!
* Get a new send buffer from this transport object.
+ * \return a managed buffer, or null sptr on timeout/error
*/
managed_send_buffer::sptr get_send_buff(void);
diff --git a/host/include/uhd/usrp/dboard_iface.hpp b/host/include/uhd/usrp/dboard_iface.hpp
index c7db244f2..c430ecd3f 100644
--- a/host/include/uhd/usrp/dboard_iface.hpp
+++ b/host/include/uhd/usrp/dboard_iface.hpp
@@ -242,6 +242,15 @@ public:
* \param enb true for enabled
*/
virtual void set_clock_enabled(unit_t unit, bool enb) = 0;
+
+ /*!
+ * Get the rate of the codec.
+ * For rx, this is the rate the ADC feeds the DSP.
+ * For tx, this is the rate the DSP feeds the DAC.
+ * \param unit which unit rx or tx
+ * \return the codec rate in Hz
+ */
+ virtual double get_codec_rate(unit_t unit) = 0;
};
}} //namespace
diff --git a/host/lib/ic_reg_maps/CMakeLists.txt b/host/lib/ic_reg_maps/CMakeLists.txt
index f8e15c13d..25f34a280 100644
--- a/host/lib/ic_reg_maps/CMakeLists.txt
+++ b/host/lib/ic_reg_maps/CMakeLists.txt
@@ -68,3 +68,8 @@ LIBUHD_PYTHON_GEN_SOURCE(
${CMAKE_SOURCE_DIR}/lib/ic_reg_maps/gen_ad9522_regs.py
${CMAKE_BINARY_DIR}/lib/ic_reg_maps/ad9522_regs.hpp
)
+
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_SOURCE_DIR}/lib/ic_reg_maps/gen_tuner_4937di5_regs.py
+ ${CMAKE_BINARY_DIR}/lib/ic_reg_maps/tuner_4937di5_regs.hpp
+)
diff --git a/host/lib/ic_reg_maps/gen_max2118_regs.py b/host/lib/ic_reg_maps/gen_max2118_regs.py
index 506fbaec8..506fbaec8 100644..100755
--- a/host/lib/ic_reg_maps/gen_max2118_regs.py
+++ b/host/lib/ic_reg_maps/gen_max2118_regs.py
diff --git a/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py b/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py
new file mode 100644
index 000000000..73f7aa3db
--- /dev/null
+++ b/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+#
+# 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/>.
+#
+
+########################################################################
+# Template for raw text data describing registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+REGS_TMPL="""\
+########################################################################
+## Note: offsets given from perspective of data bits (excludes address)
+########################################################################
+## Divider byte 1
+########################################################################
+db1 0[0:6] 0x00
+########################################################################
+## Divider byte 2
+########################################################################
+db2 1[0:7] 0x00
+########################################################################
+## Control byte 1
+########################################################################
+cb7 2[7] 0x01
+cp 2[6] 0x00 low,high
+os 2[0] 0x00 on,off
+rs 2[1:2] 0x00 d512=3,d640=0,d1024=1
+test 2[3:5] 0x01 normal=0x01,cpoff=0x02,cpsink=0x06,cpsrc=0x07,cptest1=0x04,cptest2=0x05
+########################################################################
+## Control byte 2
+########################################################################
+bandsel 3[4:7] 0x03 uhf=0x03,vhfhi=0x09,vhflo=0x0a
+power 3[3] 0x00 on,off
+"""
+
+########################################################################
+# Template for methods in the body of the struct
+########################################################################
+BODY_TMPL="""\
+boost::uint8_t get_reg(boost::uint8_t addr){
+ boost::uint8_t reg = 0;
+ switch(addr){
+ #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs)))
+ case $addr:
+ #for $reg in filter(lambda r: r.get_addr() == addr, $regs)
+ reg |= (boost::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift();
+ #end for
+ break;
+ #end for
+ }
+ return boost::uint8_t(reg);
+}
+
+"""
+
+if __name__ == '__main__':
+ import common; common.generate(
+ name='tuner_4937di5_regs',
+ regs_tmpl=REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ )
diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt
index 62c4f62b1..e9e82932c 100644
--- a/host/lib/transport/CMakeLists.txt
+++ b/host/lib/transport/CMakeLists.txt
@@ -31,11 +31,10 @@ IF(LIBUSB_FOUND)
${CMAKE_SOURCE_DIR}/lib/transport/libusb1_zero_copy.cpp
${CMAKE_SOURCE_DIR}/lib/transport/libusb1_base.cpp
${CMAKE_SOURCE_DIR}/lib/transport/libusb1_base.hpp
- ${CMAKE_SOURCE_DIR}/lib/transport/libusb1_device_handle.cpp
)
- IF(WIN32) #include our custom stdint for libusb
- INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/transport/include)
- ENDIF(WIN32)
+ IF(MSVC) #include our custom stdint for libusb
+ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/transport/msvc)
+ ENDIF(MSVC)
SET(HAVE_USB_SUPPORT TRUE)
ENDIF(LIBUSB_FOUND)
diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp
index 1f816c6e2..49f524a32 100644
--- a/host/lib/transport/libusb1_base.cpp
+++ b/host/lib/transport/libusb1_base.cpp
@@ -16,111 +16,262 @@
//
#include "libusb1_base.hpp"
+#include <uhd/utils/thread_priority.hpp>
#include <uhd/utils/assert.hpp>
+#include <uhd/types/dict.hpp>
+#include <boost/weak_ptr.hpp>
+#include <boost/foreach.hpp>
+#include <boost/thread.hpp>
#include <iostream>
+using namespace uhd;
using namespace uhd::transport;
-/**********************************************************
- * Helper Methods
- **********************************************************/
+/***********************************************************************
+ * libusb session
+ **********************************************************************/
+class libusb_session_impl : public libusb::session{
+public:
+ libusb_session_impl(void){
+ UHD_ASSERT_THROW(libusb_init(&_context) == 0);
+ libusb_set_debug(_context, debug_level);
+ _thread_group.create_thread(boost::bind(&libusb_session_impl::run_event_loop, this));
+ }
-/**********************************************************
- * libusb namespace
- **********************************************************/
-void libusb::init(libusb_context **ctx, int debug_level)
-{
- if (libusb_init(ctx) < 0)
- std::cerr << "error: libusb_init" << std::endl;
+ ~libusb_session_impl(void){
+ _running = false;
+ _thread_group.join_all();
+ libusb_exit(_context);
+ }
- libusb_set_debug(*ctx, debug_level);
-}
+ libusb_context *get_context(void) const{
+ return _context;
+ }
-libusb_device_handle *libusb::open_device(libusb_context *ctx,
- usb_device_handle::sptr handle)
-{
- libusb_device_handle *dev_handle = NULL;
- libusb_device **libusb_dev_list;
- size_t dev_cnt = libusb_get_device_list(ctx, &libusb_dev_list);
-
- //find and open the USB device
- for (size_t i = 0; i < dev_cnt; i++) {
- libusb_device *dev = libusb_dev_list[i];
-
- if (compare_device(dev, handle)) {
- libusb_open(dev, &dev_handle);
- libusb_unref_device(dev);
- break;
+private:
+ libusb_context *_context;
+ boost::thread_group _thread_group;
+ bool _running;
+
+ void run_event_loop(void){
+ set_thread_priority_safe();
+ _running = true;
+ timeval tv;
+ while(_running){
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000; //100ms
+ libusb_handle_events_timeout(this->get_context(), &tv);
}
-
- libusb_unref_device(dev);
}
+};
+
+libusb::session::sptr libusb::session::get_global_session(void){
+ static boost::weak_ptr<session> global_session;
+
+ //not expired -> get existing session
+ if (not global_session.expired()) return global_session.lock();
- return dev_handle;
+ //create a new global session
+ sptr new_global_session(new libusb_session_impl());
+ global_session = new_global_session;
+ return new_global_session;
}
-//note: changed order of checks so it only tries to get_serial and get_device_address if vid and pid match
-//doing this so it doesn't try to open the device if it's not ours
-bool libusb::compare_device(libusb_device *dev,
- usb_device_handle::sptr handle)
-{
- std::string serial = handle->get_serial();
- boost::uint16_t vendor_id = handle->get_vendor_id();
- boost::uint16_t product_id = handle->get_product_id();
- boost::uint16_t device_addr = handle->get_device_addr();
-
- libusb_device_descriptor libusb_desc;
- if (libusb_get_device_descriptor(dev, &libusb_desc) < 0)
- return false;
- if (vendor_id != libusb_desc.idVendor)
- return false;
- if (product_id != libusb_desc.idProduct)
- return false;
- if (serial != get_serial(dev))
- return false;
- if (device_addr != libusb_get_device_address(dev))
- return false;
-
- return true;
+/***********************************************************************
+ * libusb device
+ **********************************************************************/
+class libusb_device_impl : public libusb::device{
+public:
+ libusb_device_impl(libusb_device *dev){
+ _session = libusb::session::get_global_session();
+ _dev = dev;
+ }
+
+ ~libusb_device_impl(void){
+ libusb_unref_device(this->get());
+ }
+
+ libusb_device *get(void) const{
+ return _dev;
+ }
+
+private:
+ libusb::session::sptr _session; //always keep a reference to session
+ libusb_device *_dev;
+};
+
+/***********************************************************************
+ * libusb device list
+ **********************************************************************/
+class libusb_device_list_impl : public libusb::device_list{
+public:
+ libusb_device_list_impl(void){
+ libusb::session::sptr sess = libusb::session::get_global_session();
+
+ //allocate a new list of devices
+ libusb_device** dev_list;
+ ssize_t ret = libusb_get_device_list(sess->get_context(), &dev_list);
+ if (ret < 0) throw std::runtime_error("cannot enumerate usb devices");
+
+ //fill the vector of device references
+ for (size_t i = 0; i < size_t(ret); i++) _devs.push_back(
+ libusb::device::sptr(new libusb_device_impl(dev_list[i]))
+ );
+
+ //free the device list but dont unref (done in ~device)
+ libusb_free_device_list(dev_list, false/*dont unref*/);
+ }
+
+ size_t size(void) const{
+ return _devs.size();
+ }
+
+ libusb::device::sptr at(size_t i) const{
+ return _devs.at(i);
+ }
+
+private:
+ std::vector<libusb::device::sptr> _devs;
+};
+
+libusb::device_list::sptr libusb::device_list::make(void){
+ return sptr(new libusb_device_list_impl());
}
+/***********************************************************************
+ * libusb device descriptor
+ **********************************************************************/
+class libusb_device_descriptor_impl : public libusb::device_descriptor{
+public:
+ libusb_device_descriptor_impl(libusb::device::sptr dev){
+ _dev = dev;
+ UHD_ASSERT_THROW(libusb_get_device_descriptor(_dev->get(), &_desc) == 0);
+ }
-bool libusb::open_interface(libusb_device_handle *dev_handle,
- int interface)
-{
- int ret = libusb_claim_interface(dev_handle, interface);
- if (ret < 0) {
- std::cerr << "error: libusb_claim_interface() " << ret << std::endl;
- return false;
+ const libusb_device_descriptor &get(void) const{
+ return _desc;
}
- else {
- return true;
+
+ std::string get_ascii_serial(void) const{
+ if (this->get().iSerialNumber == 0) return "";
+
+ libusb::device_handle::sptr handle(
+ libusb::device_handle::get_cached_handle(_dev)
+ );
+
+ unsigned char buff[512];
+ ssize_t ret = libusb_get_string_descriptor_ascii(
+ handle->get(), this->get().iSerialNumber, buff, sizeof(buff)
+ );
+ if (ret < 0) return ""; //on error, just return empty string
+
+ return std::string((char *)buff, ret);
}
+
+private:
+ libusb::device::sptr _dev; //always keep a reference to device
+ libusb_device_descriptor _desc;
+};
+
+libusb::device_descriptor::sptr libusb::device_descriptor::make(device::sptr dev){
+ return sptr(new libusb_device_descriptor_impl(dev));
}
+/***********************************************************************
+ * libusb device handle
+ **********************************************************************/
+class libusb_device_handle_impl : public libusb::device_handle{
+public:
+ libusb_device_handle_impl(libusb::device::sptr dev){
+ _dev = dev;
+ UHD_ASSERT_THROW(libusb_open(_dev->get(), &_handle) == 0);
+ }
+
+ ~libusb_device_handle_impl(void){
+ //release all claimed interfaces
+ for (size_t i = 0; i < _claimed.size(); i++){
+ libusb_release_interface(this->get(), _claimed[i]);
+ }
+ libusb_close(_handle);
+ }
+
+ libusb_device_handle *get(void) const{
+ return _handle;
+ }
-std::string libusb::get_serial(libusb_device *dev)
-{
- unsigned char buff[32];
+ void claim_interface(int interface){
+ UHD_ASSERT_THROW(libusb_claim_interface(this->get(), interface) == 0);
+ _claimed.push_back(interface);
+ }
- libusb_device_descriptor desc;
- if (libusb_get_device_descriptor(dev, &desc) < 0)
- return "";
+private:
+ libusb::device::sptr _dev; //always keep a reference to device
+ libusb_device_handle *_handle;
+ std::vector<int> _claimed;
+};
- if (desc.iSerialNumber == 0)
- return "";
+libusb::device_handle::sptr libusb::device_handle::get_cached_handle(device::sptr dev){
+ static uhd::dict<libusb_device *, boost::weak_ptr<device_handle> > handles;
- //open the device because we have to
- libusb_device_handle *dev_handle;
- if (libusb_open(dev, &dev_handle) < 0)
- return "";
+ //not expired -> get existing session
+ if (handles.has_key(dev->get()) and not handles[dev->get()].expired()){
+ return handles[dev->get()].lock();
+ }
- if (libusb_get_string_descriptor_ascii(dev_handle, desc.iSerialNumber,
- buff, sizeof(buff)) < 0) {
- return "";
+ //create a new global session
+ sptr new_handle(new libusb_device_handle_impl(dev));
+ handles[dev->get()] = new_handle;
+ return new_handle;
+}
+
+/***********************************************************************
+ * libusb special handle
+ **********************************************************************/
+class libusb_special_handle_impl : public libusb::special_handle{
+public:
+ libusb_special_handle_impl(libusb::device::sptr dev){
+ _dev = dev;
}
- libusb_close(dev_handle);
+ libusb::device::sptr get_device(void) const{
+ return _dev;
+ }
+
+ std::string get_serial(void) const{
+ return libusb::device_descriptor::make(this->get_device())->get_ascii_serial();
+ }
+
+ boost::uint16_t get_vendor_id(void) const{
+ return libusb::device_descriptor::make(this->get_device())->get().idVendor;
+ }
+
+ boost::uint16_t get_product_id(void) const{
+ return libusb::device_descriptor::make(this->get_device())->get().idProduct;
+ }
+
+private:
+ libusb::device::sptr _dev; //always keep a reference to device
+};
+
+libusb::special_handle::sptr libusb::special_handle::make(device::sptr dev){
+ return sptr(new libusb_special_handle_impl(dev));
+}
+
+/***********************************************************************
+ * list device handles implementations
+ **********************************************************************/
+std::vector<usb_device_handle::sptr> usb_device_handle::get_device_list(
+ boost::uint16_t vid, boost::uint16_t pid
+){
+ std::vector<usb_device_handle::sptr> handles;
+
+ libusb::device_list::sptr dev_list = libusb::device_list::make();
+ for (size_t i = 0; i < dev_list->size(); i++){
+ usb_device_handle::sptr handle = libusb::special_handle::make(dev_list->at(i));
+ if (handle->get_vendor_id() == vid and handle->get_product_id() == pid){
+ handles.push_back(handle);
+ }
+ }
- return (char*) buff;
+ return handles;
}
diff --git a/host/lib/transport/libusb1_base.hpp b/host/lib/transport/libusb1_base.hpp
index 484bcf3d9..04c1d6574 100644
--- a/host/lib/transport/libusb1_base.hpp
+++ b/host/lib/transport/libusb1_base.hpp
@@ -19,74 +19,129 @@
#define INCLUDED_LIBUHD_TRANSPORT_LIBUSB_HPP
#include <uhd/config.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
#include <uhd/transport/usb_device_handle.hpp>
-#include <libusb-1.0/libusb.h>
+#include <libusb.h>
+/***********************************************************************
+ * Libusb object oriented smart pointer wrappers:
+ * The following wrappers provide allocation and automatic deallocation
+ * for various libusb data types and handles. The construction routines
+ * also store tables of already allocated structures to avoid multiple
+ * occurrences of opened handles (for example).
+ **********************************************************************/
namespace uhd { namespace transport {
namespace libusb {
- /*
- * Initialize libusb and set debug level
- * Takes a pointer to context pointer because that's
- * how libusb rolls. Debug levels.
- *
- * Level 0: no messages ever printed by the library (default)
- * Level 1: error messages are printed to stderr
- * Level 2: warning and error messages are printed to stderr
- * Level 3: informational messages are printed to stdout, warning
- * and error messages are printed to stderr
- *
- * \param ctx pointer to context pointer
- * \param debug_level
+
+ /*!
+ * This session class holds a global libusb context for this process.
+ * The get global session call will create a new context if none exists.
+ * When all references to session are destroyed, the context will be freed.
*/
- void init(libusb_context **ctx, int debug_level);
-
- /*
- * Open the device specified by a generic handle
- * Find the libusb_device cooresponding to the generic handle
- * and open it for I/O, which returns a libusb_device_handle
- * ready for an interface
- * \param ctx the libusb context used for init
- * \return a libusb_device_handle ready for action
+ class session : boost::noncopyable {
+ public:
+ typedef boost::shared_ptr<session> sptr;
+
+ /*!
+ * Level 0: no messages ever printed by the library (default)
+ * Level 1: error messages are printed to stderr
+ * Level 2: warning and error messages are printed to stderr
+ * Level 3: informational messages are printed to stdout, warning
+ * and error messages are printed to stderr
+ */
+ static const int debug_level = 0;
+
+ //! get a shared pointer to the global session
+ static sptr get_global_session(void);
+
+ //! get the underlying libusb context pointer
+ virtual libusb_context *get_context(void) const = 0;
+ };
+
+ /*!
+ * Holds a device pointer with a reference to the session.
*/
- libusb_device_handle *open_device(libusb_context *ctx,
- usb_device_handle::sptr handle);
-
- /*
- * Compare a libusb device with a generic handle
- * Check the descriptors and open the device to check the
- * serial number string. Compare values against the given
- * handle. The libusb context is already implied in the
- * libusb_device.
- * \param dev a libusb_device pointer
- * \param handle a generic handle specifier
- * \return true if handle and device match, false otherwise
+ class device : boost::noncopyable {
+ public:
+ typedef boost::shared_ptr<device> sptr;
+
+ //! get the underlying device pointer
+ virtual libusb_device *get(void) const = 0;
+ };
+
+ /*!
+ * This device list class holds a device list that will be
+ * automatically freed when the last reference is destroyed.
*/
- bool compare_device(libusb_device *dev, usb_device_handle::sptr handle);
-
- /*
- * Open an interface to the device
- * This is a logical operation for operating system housekeeping as
- * nothing is sent over the bus. The interface much correspond
- * to the USB device descriptors.
- * \param dev_handle libusb handle to an opened device
- * \param interface integer of the interface to use
- * \return true on success, false on error
+ class device_list : boost::noncopyable {
+ public:
+ typedef boost::shared_ptr<device_list> sptr;
+
+ //! make a new device list
+ static sptr make(void);
+
+ //! the number of devices in this list
+ virtual size_t size() const = 0;
+
+ //! get the device pointer at a particular index
+ virtual device::sptr at(size_t index) const = 0;
+ };
+
+ /*!
+ * Holds a device descriptor and a reference to the device.
*/
- bool open_interface(libusb_device_handle *dev_handle, int interface);
-
- /*
- * Get serial number
- * The standard USB device descriptor contains an index to an
- * actual serial number string descriptor. The index is readily
- * readble, but the string descriptor requires probing the device.
- * Because this call attempts to open the device, it may not
- * succeed because not all USB devices are readily opened.
- * The default language is used for the request (English).
- * \param dev a libusb_device pointer
- * \return string serial number or 0 on error or unavailablity
+ class device_descriptor : boost::noncopyable {
+ public:
+ typedef boost::shared_ptr<device_descriptor> sptr;
+
+ //! make a new descriptor from a device reference
+ static sptr make(device::sptr);
+
+ //! get the underlying device descriptor
+ virtual const libusb_device_descriptor &get(void) const = 0;
+
+ virtual std::string get_ascii_serial(void) const = 0;
+ };
+
+ /*!
+ * Holds a device handle and a reference to the device.
*/
- std::string get_serial(libusb_device *dev);
+ class device_handle : boost::noncopyable {
+ public:
+ typedef boost::shared_ptr<device_handle> sptr;
+
+ //! get a cached handle or make a new one given the device
+ static sptr get_cached_handle(device::sptr);
+
+ //! get the underlying device handle
+ virtual libusb_device_handle *get(void) const = 0;
+
+ /*!
+ * Open USB interfaces for control using magic value
+ * IN interface: 2
+ * OUT interface: 1
+ * Control interface: 0
+ */
+ virtual void claim_interface(int) = 0;
+ };
+
+ /*!
+ * The special handle is our internal implementation of the
+ * usb device handle which is used publicly to identify a device.
+ */
+ class special_handle : public usb_device_handle {
+ public:
+ typedef boost::shared_ptr<special_handle> sptr;
+
+ //! make a new special handle from device
+ static sptr make(device::sptr);
+
+ //! get the underlying device reference
+ virtual device::sptr get_device(void) const = 0;
+ };
+
}
}} //namespace
diff --git a/host/lib/transport/libusb1_control.cpp b/host/lib/transport/libusb1_control.cpp
index 3531128b2..f903907d0 100644
--- a/host/lib/transport/libusb1_control.cpp
+++ b/host/lib/transport/libusb1_control.cpp
@@ -20,7 +20,6 @@
using namespace uhd::transport;
-const int libusb_debug_level = 0;
const int libusb_timeout = 0;
/***********************************************************************
@@ -28,68 +27,38 @@ const int libusb_timeout = 0;
**********************************************************************/
class libusb_control_impl : public usb_control {
public:
- libusb_control_impl(usb_device_handle::sptr handle);
- ~libusb_control_impl();
+ libusb_control_impl(libusb::device_handle::sptr handle):
+ _handle(handle)
+ {
+ _handle->claim_interface(0 /* control interface */);
+ }
- size_t submit(boost::uint8_t request_type,
+ ssize_t submit(boost::uint8_t request_type,
boost::uint8_t request,
boost::uint16_t value,
boost::uint16_t index,
unsigned char *buff,
- boost::uint16_t length);
+ boost::uint16_t length
+ ){
+ return libusb_control_transfer(_handle->get(),
+ request_type,
+ request,
+ value,
+ index,
+ buff,
+ length,
+ libusb_timeout);
+ }
private:
- libusb_context *_ctx;
- libusb_device_handle *_dev_handle;
+ libusb::device_handle::sptr _handle;
};
-
-libusb_control_impl::libusb_control_impl(usb_device_handle::sptr handle)
-{
- libusb::init(&_ctx, libusb_debug_level);
-
- // Find and open the libusb_device corresponding to the
- // given handle and return the libusb_device_handle
- // that can be used for I/O purposes.
- _dev_handle = libusb::open_device(_ctx, handle);
-
- // Open USB interfaces for control using magic value
- // IN interface: 2
- // OUT interface: 1
- // Control interface: 0
- libusb::open_interface(_dev_handle, 0);
-}
-
-
-libusb_control_impl::~libusb_control_impl()
-{
- libusb_close(_dev_handle);
- libusb_exit(_ctx);
-}
-
-
-size_t libusb_control_impl::submit(boost::uint8_t request_type,
- boost::uint8_t request,
- boost::uint16_t value,
- boost::uint16_t index,
- unsigned char *buff,
- boost::uint16_t length)
-{
- return libusb_control_transfer(_dev_handle,
- request_type,
- request,
- value,
- index,
- buff,
- length,
- libusb_timeout);
-}
-
-
/***********************************************************************
* USB control public make functions
**********************************************************************/
-usb_control::sptr usb_control::make(usb_device_handle::sptr handle)
-{
- return sptr(new libusb_control_impl(handle));
+usb_control::sptr usb_control::make(usb_device_handle::sptr handle){
+ return sptr(new libusb_control_impl(libusb::device_handle::get_cached_handle(
+ boost::static_pointer_cast<libusb::special_handle>(handle)->get_device()
+ )));
}
diff --git a/host/lib/transport/libusb1_device_handle.cpp b/host/lib/transport/libusb1_device_handle.cpp
deleted file mode 100644
index 7efddd410..000000000
--- a/host/lib/transport/libusb1_device_handle.cpp
+++ /dev/null
@@ -1,117 +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 "libusb1_base.hpp"
-#include <uhd/utils/assert.hpp>
-#include <iostream>
-
-using namespace uhd::transport;
-
-const int libusb_debug_level = 0;
-
-/****************************************************************
- * libusb USB device handle implementation class
- ***************************************************************/
-class libusb1_device_handle_impl : public usb_device_handle {
-public:
- libusb1_device_handle_impl(std::string serial,
- boost::uint16_t product_id,
- boost::uint16_t vendor_id,
- boost::uint16_t device_addr)
- : _serial(serial), _product_id(product_id),
- _vendor_id(vendor_id), _device_addr(device_addr)
- {
- /* NOP */
- }
-
- ~libusb1_device_handle_impl()
- {
- /* NOP */
- }
-
- std::string get_serial() const
- {
- return _serial;
- }
-
- boost::uint16_t get_vendor_id() const
- {
- return _vendor_id;
- }
-
-
- boost::uint16_t get_product_id() const
- {
- return _product_id;
- }
-
- boost::uint16_t get_device_addr() const
- {
- return _device_addr;
- }
-
-private:
- std::string _serial;
- boost::uint16_t _product_id;
- boost::uint16_t _vendor_id;
- boost::uint16_t _device_addr;
-};
-
-
-usb_device_handle::sptr make_usb_device_handle(libusb_device *dev)
-{
- libusb_device_descriptor desc;
-
- if (libusb_get_device_descriptor(dev, &desc) < 0) {
- UHD_ASSERT_THROW("USB: failed to get device descriptor");
- }
-
- std::string serial = libusb::get_serial(dev);
- boost::uint16_t product_id = desc.idProduct;
- boost::uint16_t vendor_id = desc.idVendor;
- boost::uint16_t device_addr = libusb_get_device_address(dev);
-
- return usb_device_handle::sptr(new libusb1_device_handle_impl(
- serial,
- product_id,
- vendor_id,
- device_addr));
-}
-
-std::vector<usb_device_handle::sptr> usb_device_handle::get_device_list(boost::uint16_t vid, boost::uint16_t pid)
-{
- libusb_context *ctx = NULL;
- libusb_device** libusb_device_list;
- std::vector<usb_device_handle::sptr> device_handle_list;
- libusb_device_descriptor desc;
-
- libusb::init(&ctx, libusb_debug_level);
-
- size_t dev_size = libusb_get_device_list(ctx, &libusb_device_list);
- for (size_t i = 0; i < dev_size; i++) {
- libusb_device *dev = libusb_device_list[i];
- if(libusb_get_device_descriptor(dev, &desc) < 0) {
- UHD_ASSERT_THROW("USB: failed to get device descriptor");
- }
- if(desc.idVendor == vid && desc.idProduct == pid) {
- device_handle_list.push_back(make_usb_device_handle(dev));
- }
- }
-
- libusb_exit(ctx);
- return device_handle_list;
-}
diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp
index b3a160462..f9beb0b4c 100644
--- a/host/lib/transport/libusb1_zero_copy.cpp
+++ b/host/lib/transport/libusb1_zero_copy.cpp
@@ -17,16 +17,22 @@
#include "libusb1_base.hpp"
#include <uhd/transport/usb_zero_copy.hpp>
+#include <uhd/transport/bounded_buffer.hpp>
#include <uhd/utils/assert.hpp>
-#include <boost/format.hpp>
+#include <boost/shared_array.hpp>
+#include <boost/foreach.hpp>
+#include <boost/thread.hpp>
+#include <vector>
#include <iostream>
#include <iomanip>
using namespace uhd::transport;
-const int libusb_debug_level = 0;
const int libusb_timeout = 0;
+static const size_t DEFAULT_NUM_XFERS = 16; //num xfers
+static const size_t DEFAULT_XFER_SIZE = 32*512; //bytes
+
/***********************************************************************
* Helper functions
***********************************************************************/
@@ -53,56 +59,60 @@ void pp_transfer(libusb_transfer *lut)
* create a bidirectional interface. It is a zero copy implementation
* with respect to libusb, however, each send and recv requires a copy
* operation from kernel to userspace; this is due to the usbfs
- * interface provided by the kernel.
+ * interface provided by the kernel.
**********************************************************************/
class usb_endpoint {
+public:
+ typedef boost::shared_ptr<usb_endpoint> sptr;
+
+ usb_endpoint(
+ libusb::device_handle::sptr handle,
+ int endpoint,
+ bool input,
+ size_t transfer_size,
+ size_t num_transfers
+ );
+
+ ~usb_endpoint(void);
+
+ // Exposed interface for submitting / retrieving transfer buffers
+
+ //! Submit a new transfer that was presumably just filled or emptied.
+ void submit(libusb_transfer *lut);
+
+ /*!
+ * Get an available transfer:
+ * For inputs, this is a just filled transfer.
+ * For outputs, this is a just emptied transfer.
+ * \param timeout_ms the timeout to wait for a lut
+ * \return the transfer pointer or NULL if timeout
+ */
+ libusb_transfer *get_lut_with_wait(size_t timeout_ms = 100);
+
+ //Callback use only
+ void callback_handle_transfer(libusb_transfer *lut);
+
private:
- libusb_device_handle *_dev_handle;
- libusb_context *_ctx;
+ libusb::device_handle::sptr _handle;
int _endpoint;
bool _input;
size_t _transfer_size;
size_t _num_transfers;
- // Transfer state lists (transfers are free, pending, or completed)
- std::list<libusb_transfer *> _free_list;
- std::list<libusb_transfer *> _pending_list;
- std::list<libusb_transfer *> _completed_list;
-
- // Calls for processing asynchronous I/O
- libusb_transfer *allocate_transfer(int buff_len);
- bool cancel(libusb_transfer *lut);
- bool cancel_all();
- bool reap_pending_list();
- bool reap_pending_list_timeout();
- bool reap_completed_list();
-
- // Transfer state manipulators
- void free_list_add(libusb_transfer *lut);
- void pending_list_add(libusb_transfer *lut);
- void completed_list_add(libusb_transfer *lut);
- libusb_transfer *free_list_get();
- libusb_transfer *completed_list_get();
- bool pending_list_remove(libusb_transfer *lut);
-
- // Debug use
- void print_transfer_status(libusb_transfer *lut);
-
-public:
- usb_endpoint(libusb_device_handle *dev_handle,
- libusb_context *ctx, int endpoint, bool input,
- size_t transfer_size, size_t num_transfers);
+ //! hold a bounded buffer of completed transfers
+ typedef bounded_buffer<libusb_transfer *> lut_buff_type;
+ lut_buff_type::sptr _completed_list;
- ~usb_endpoint();
+ //! a list of all transfer structs we allocated
+ std::vector<libusb_transfer *> _all_luts;
- // Exposed interface for submitting / retrieving transfer buffers
- bool submit(libusb_transfer *lut);
- libusb_transfer *get_completed_transfer();
- libusb_transfer *get_free_transfer();
+ //! a list of shared arrays for the transfer buffers
+ std::vector<boost::shared_array<boost::uint8_t> > _buffers;
- //Callback use only
- void callback_handle_transfer(libusb_transfer *lut);
+ // Calls for processing asynchronous I/O
+ libusb_transfer *allocate_transfer(int buff_len);
+ void print_transfer_status(libusb_transfer *lut);
};
@@ -115,9 +125,8 @@ public:
* it from the pending to completed status list.
* \param lut pointer to libusb_transfer
*/
-static void callback(libusb_transfer *lut)
-{
- usb_endpoint *endpoint = (usb_endpoint *) lut->user_data;
+static void callback(libusb_transfer *lut){
+ usb_endpoint *endpoint = (usb_endpoint *) lut->user_data;
endpoint->callback_handle_transfer(lut);
}
@@ -126,14 +135,9 @@ static void callback(libusb_transfer *lut)
* Accessor call to allow list access from callback space
* \param pointer to libusb_transfer
*/
-void usb_endpoint::callback_handle_transfer(libusb_transfer *lut)
-{
- if (!pending_list_remove(lut)) {
- std::cerr << "USB: pending remove failed" << std::endl;
- return;
- }
-
- completed_list_add(lut);
+void usb_endpoint::callback_handle_transfer(libusb_transfer *lut){
+ boost::this_thread::disable_interruption di; //disable because the wait can throw
+ _completed_list->push_with_wait(lut);
}
@@ -141,21 +145,30 @@ void usb_endpoint::callback_handle_transfer(libusb_transfer *lut)
* Constructor
* Allocate libusb transfers and mark as free. For IN endpoints,
* submit the transfers so that they're ready to return when
- * data is available.
+ * data is available.
*/
-usb_endpoint::usb_endpoint(libusb_device_handle *dev_handle,
- libusb_context *ctx, int endpoint, bool input,
- size_t transfer_size, size_t num_transfers)
- : _dev_handle(dev_handle),
- _ctx(ctx), _endpoint(endpoint), _input(input),
- _transfer_size(transfer_size), _num_transfers(num_transfers)
+usb_endpoint::usb_endpoint(
+ libusb::device_handle::sptr handle,
+ int endpoint,
+ bool input,
+ size_t transfer_size,
+ size_t num_transfers
+):
+ _handle(handle),
+ _endpoint(endpoint),
+ _input(input),
+ _transfer_size(transfer_size),
+ _num_transfers(num_transfers)
{
- unsigned int i;
- for (i = 0; i < _num_transfers; i++) {
- free_list_add(allocate_transfer(_transfer_size));
+ _completed_list = lut_buff_type::make(num_transfers);
+
+ for (size_t i = 0; i < _num_transfers; i++){
+ _all_luts.push_back(allocate_transfer(_transfer_size));
- if (_input)
- submit(free_list_get());
+ //input luts are immediately submitted to be filled
+ //output luts go into the completed list as free buffers
+ if (_input) this->submit(_all_luts.back());
+ else _completed_list->push_with_wait(_all_luts.back());
}
}
@@ -167,47 +180,44 @@ usb_endpoint::usb_endpoint(libusb_device_handle *dev_handle,
* the transfers. Libusb will deallocate the data buffer held by
* each transfer.
*/
-usb_endpoint::~usb_endpoint()
-{
- cancel_all();
-
- while (!_pending_list.empty()) {
- if (!reap_pending_list())
- std::cerr << "error: destructor failed to reap" << std::endl;
+usb_endpoint::~usb_endpoint(void){
+ //cancel all transfers
+ BOOST_FOREACH(libusb_transfer *lut, _all_luts){
+ libusb_cancel_transfer(lut);
}
- while (!_completed_list.empty()) {
- if (!reap_completed_list())
- std::cerr << "error: destructor failed to reap" << std::endl;
- }
+ //collect canceled transfers (drain the queue)
+ while (this->get_lut_with_wait() != NULL){};
- while (!_free_list.empty()) {
- libusb_free_transfer(free_list_get());
+ //free all transfers
+ BOOST_FOREACH(libusb_transfer *lut, _all_luts){
+ libusb_free_transfer(lut);
}
}
/*
- * Allocate a libusb transfer
+ * Allocate a libusb transfer
* The allocated transfer - and buffer it contains - is repeatedly
* submitted, reaped, and reused and should not be freed until shutdown.
* \param buff_len size of the individual buffer held by each transfer
* \return pointer to an allocated libusb_transfer
*/
-libusb_transfer *usb_endpoint::allocate_transfer(int buff_len)
-{
+libusb_transfer *usb_endpoint::allocate_transfer(int buff_len){
libusb_transfer *lut = libusb_alloc_transfer(0);
- unsigned char *buff = new unsigned char[buff_len];
+ boost::shared_array<boost::uint8_t> buff(new boost::uint8_t[buff_len]);
+ _buffers.push_back(buff); //store a reference to this shared array
unsigned int endpoint = ((_endpoint & 0x7f) | (_input ? 0x80 : 0));
+ libusb_transfer_cb_fn lut_callback = libusb_transfer_cb_fn(&callback);
libusb_fill_bulk_transfer(lut, // transfer
- _dev_handle, // dev_handle
+ _handle->get(), // dev_handle
endpoint, // endpoint
- buff, // buffer
+ buff.get(), // buffer
buff_len, // length
- libusb_transfer_cb_fn(callback), // callback
+ lut_callback, // callback
this, // user_data
0); // timeout
return lut;
@@ -218,97 +228,17 @@ libusb_transfer *usb_endpoint::allocate_transfer(int buff_len)
* Asynchonous transfer submission
* Submit a libusb transfer to libusb add pending status
* \param lut pointer to libusb_transfer
- * \return true on success or false on error
- */
-bool usb_endpoint::submit(libusb_transfer *lut)
-{
- int retval;
- if ((retval = libusb_submit_transfer(lut)) < 0) {
- std::cerr << "error: libusb_submit_transfer: " << retval << std::endl;
- return false;
- }
-
- pending_list_add(lut);
- return true;
-}
-
-
-/*
- * Cancel a pending transfer
- * Search the pending list for the transfer and cancel if found.
- * \param lut pointer to libusb_transfer to cancel
- * \return true on success or false if transfer is not found
- *
- * Note: success only indicates submission of cancelation request.
- * Sucessful cancelation is not known until the callback occurs.
- */
-bool usb_endpoint::cancel(libusb_transfer *lut)
-{
- std::list<libusb_transfer*>::iterator iter;
- for (iter = _pending_list.begin(); iter != _pending_list.end(); iter++) {
- if (*iter == lut) {
- libusb_cancel_transfer(lut);
- return true;
- }
- }
- return false;
-}
-
-
-/*
- * Cancel all pending transfers
- * \return bool true if cancelation request is submitted
- *
- * Note: success only indicates submission of cancelation request.
- * Sucessful cancelation is not known until the callback occurs.
+ * \return true on success or false on error
*/
-bool usb_endpoint::cancel_all()
-{
- std::list<libusb_transfer*>::iterator iter;
-
- for (iter = _pending_list.begin(); iter != _pending_list.end(); iter++) {
- if (libusb_cancel_transfer(*iter) < 0) {
- std::cerr << "error: libusb_cancal_transfer() failed" << std::endl;
- return false;
- }
- }
-
- return true;
-}
-
-
-/*
- * Reap completed transfers
- * return true if at least one transfer was reaped, false otherwise.
- * Check completed transfers for errors and mark as free. This is a
- * blocking call.
- * \return bool true if a libusb transfer is reaped, false otherwise
- */
-bool usb_endpoint::reap_completed_list()
-{
- libusb_transfer *lut;
-
- if (_completed_list.empty()) {
- if (!reap_pending_list_timeout())
- return false;
- }
-
- while (!_completed_list.empty()) {
- lut = completed_list_get();
- print_transfer_status(lut);
- free_list_add(lut);
- }
-
- return true;
+void usb_endpoint::submit(libusb_transfer *lut){
+ UHD_ASSERT_THROW(libusb_submit_transfer(lut) == 0);
}
-
/*
* Print status errors of a completed transfer
* \param lut pointer to an libusb_transfer
*/
-void usb_endpoint::print_transfer_status(libusb_transfer *lut)
-{
+void usb_endpoint::print_transfer_status(libusb_transfer *lut){
switch (lut->status) {
case LIBUSB_TRANSFER_COMPLETED:
if (lut->actual_length < lut->length) {
@@ -344,200 +274,46 @@ void usb_endpoint::print_transfer_status(libusb_transfer *lut)
}
}
-
-/*
- * Reap pending transfers without timeout
- * This is a blocking call. Reaping submitted transfers is
- * handled by libusb and the assigned callback function.
- * Block until at least one transfer is reaped.
- * \return true true if a transfer was reaped or false otherwise
- */
-bool usb_endpoint::reap_pending_list()
-{
- int retval;
-
- if ((retval = libusb_handle_events(_ctx)) < 0) {
- std::cerr << "error: libusb_handle_events: " << retval << std::endl;
- return false;
- }
-
- return true;
-}
-
-
-/*
- * Reap pending transfers with timeout
- * This call blocks until a transfer is reaped or timeout.
- * Reaping submitted transfers is handled by libusb and the
- * assigned callback function. Block until at least one
- * transfer is reaped or timeout occurs.
- * \return true if a transfer was reaped or false otherwise
- */
-bool usb_endpoint::reap_pending_list_timeout()
-{
- int retval;
- timeval tv;
-
- tv.tv_sec = 0;
- tv.tv_usec = 100000; //100ms
-
- size_t pending_list_size = _pending_list.size();
-
- if ((retval = libusb_handle_events_timeout(_ctx, &tv)) < 0) {
- std::cerr << "error: libusb_handle_events: " << retval << std::endl;
- return false;
- }
-
- if (_pending_list.size() < pending_list_size) {
- return true;
- }
- else {
- return false;
- }
-}
-
-
-/*
- * Get a free transfer
- * The transfer has an empty data bufer for OUT requests
- * \return pointer to a libusb_transfer
- */
-libusb_transfer *usb_endpoint::get_free_transfer()
-{
- if (_free_list.empty()) {
- if (!reap_completed_list())
- return NULL;
- }
-
- return free_list_get();
-}
-
-
-/*
- * Get a completed transfer
- * The transfer has a full data buffer for IN requests
- * \return pointer to libusb_transfer
- */
-libusb_transfer *usb_endpoint::get_completed_transfer()
-{
- if (_completed_list.empty()) {
- if (!reap_pending_list_timeout())
- return NULL;
- }
-
- return completed_list_get();
-}
-
-/*
- * List operations
- */
-void usb_endpoint::free_list_add(libusb_transfer *lut)
-{
- _free_list.push_back(lut);
-}
-
-void usb_endpoint::pending_list_add(libusb_transfer *lut)
-{
- _pending_list.push_back(lut);
-}
-
-void usb_endpoint::completed_list_add(libusb_transfer *lut)
-{
- _completed_list.push_back(lut);
-}
-
-
-/*
- * Free and completed lists don't have ordered content
- * Pop transfers from the front as needed
- */
-libusb_transfer *usb_endpoint::free_list_get()
-{
+libusb_transfer *usb_endpoint::get_lut_with_wait(size_t timeout_ms){
+ boost::this_thread::disable_interruption di; //disable because the wait can throw
libusb_transfer *lut;
-
- if (_free_list.size() == 0) {
- return NULL;
- }
- else {
- lut = _free_list.front();
- _free_list.pop_front();
- return lut;
- }
+ if (_completed_list->pop_with_timed_wait(
+ lut, boost::posix_time::milliseconds(timeout_ms)
+ )) return lut;
+ return NULL;
}
-
-/*
- * Free and completed lists don't have ordered content
- * Pop transfers from the front as needed
- */
-libusb_transfer *usb_endpoint::completed_list_get()
-{
- libusb_transfer *lut;
-
- if (_completed_list.empty()) {
- return NULL;
- }
- else {
- lut = _completed_list.front();
- _completed_list.pop_front();
- return lut;
- }
-}
-
-
-/*
- * Search and remove transfer from pending list
- * Assuming that the callbacks occur in order, the front element
- * should yield the correct transfer. If not, then something else
- * is going on. If no transfers match, then something went wrong.
- */
-bool usb_endpoint::pending_list_remove(libusb_transfer *lut)
-{
- std::list<libusb_transfer*>::iterator iter;
- for (iter = _pending_list.begin(); iter != _pending_list.end(); iter++) {
- if (*iter == lut) {
- _pending_list.erase(iter);
- return true;
- }
- }
- return false;
-}
-
-
/***********************************************************************
- * Managed buffers
+ * Managed buffers
**********************************************************************/
/*
* Libusb managed receive buffer
* Construct a recv buffer from a libusb transfer. The memory held by
* the libusb transfer is exposed through the managed buffer interface.
* Upon destruction, the transfer and buffer are resubmitted to the
- * endpoint for further use.
+ * endpoint for further use.
*/
class libusb_managed_recv_buffer_impl : public managed_recv_buffer {
public:
libusb_managed_recv_buffer_impl(libusb_transfer *lut,
- usb_endpoint *endpoint)
+ usb_endpoint::sptr endpoint)
: _buff(lut->buffer, lut->length)
{
_lut = lut;
_endpoint = endpoint;
}
- ~libusb_managed_recv_buffer_impl()
- {
- if (!_endpoint->submit(_lut))
- std::cerr << "USB: failed to submit IN transfer" << std::endl;
+ ~libusb_managed_recv_buffer_impl(void){
+ _endpoint->submit(_lut);
}
private:
- const boost::asio::const_buffer &get() const
- {
- return _buff;
+ const boost::asio::const_buffer &get(void) const{
+ return _buff;
}
libusb_transfer *_lut;
- usb_endpoint *_endpoint;
+ usb_endpoint::sptr _endpoint;
const boost::asio::const_buffer _buff;
};
@@ -553,16 +329,14 @@ private:
class libusb_managed_send_buffer_impl : public managed_send_buffer {
public:
libusb_managed_send_buffer_impl(libusb_transfer *lut,
- usb_endpoint *endpoint,
- size_t buff_size)
- : _buff(lut->buffer, buff_size), _committed(false)
+ usb_endpoint::sptr endpoint)
+ : _buff(lut->buffer, lut->length), _committed(false)
{
_lut = lut;
_endpoint = endpoint;
}
- ~libusb_managed_send_buffer_impl()
- {
+ ~libusb_managed_send_buffer_impl(void){
if (!_committed) {
_lut->length = 0;
_lut->actual_length = 0;
@@ -576,29 +350,30 @@ public:
std::cerr << "UHD: send buffer already committed" << std::endl;
return 0;
}
-
+
UHD_ASSERT_THROW(num_bytes <= boost::asio::buffer_size(_buff));
_lut->length = num_bytes;
_lut->actual_length = 0;
- if (_endpoint->submit(_lut)) {
+ try{
+ _endpoint->submit(_lut);
_committed = true;
return num_bytes;
}
- else {
- return 0;
+ catch(const std::exception &e){
+ std::cerr << "Error in commit: " << e.what() << std::endl;
+ return -1;
}
}
private:
- const boost::asio::mutable_buffer &get() const
- {
- return _buff;
+ const boost::asio::mutable_buffer &get(void) const{
+ return _buff;
}
libusb_transfer *_lut;
- usb_endpoint *_endpoint;
+ usb_endpoint::sptr _endpoint;
const boost::asio::mutable_buffer _buff;
bool _committed;
};
@@ -610,116 +385,89 @@ private:
class libusb_zero_copy_impl : public usb_zero_copy
{
private:
- usb_endpoint *_rx_ep;
- usb_endpoint *_tx_ep;
-
- // Maintain libusb values
- libusb_context *_rx_ctx;
- libusb_context *_tx_ctx;
- libusb_device_handle *_rx_dev_handle;
- libusb_device_handle *_tx_dev_handle;
-
- size_t _recv_buff_size;
- size_t _send_buff_size;
- size_t _num_frames;
+ libusb::device_handle::sptr _handle;
+ size_t _recv_num_frames, _send_num_frames;
+ usb_endpoint::sptr _recv_ep, _send_ep;
public:
typedef boost::shared_ptr<libusb_zero_copy_impl> sptr;
- libusb_zero_copy_impl(usb_device_handle::sptr handle,
- unsigned int rx_endpoint,
- unsigned int tx_endpoint,
- size_t recv_buff_size,
- size_t send_buff_size);
-
- ~libusb_zero_copy_impl();
+ libusb_zero_copy_impl(
+ libusb::device_handle::sptr handle,
+ unsigned int recv_endpoint, unsigned int send_endpoint,
+ size_t recv_xfer_size, size_t recv_num_xfers,
+ size_t send_xfer_size, size_t send_num_xfers
+ );
- managed_recv_buffer::sptr get_recv_buff(void);
+ managed_recv_buffer::sptr get_recv_buff(size_t timeout_ms);
managed_send_buffer::sptr get_send_buff(void);
- size_t get_num_recv_frames(void) const { return _num_frames; }
- size_t get_num_send_frames(void) const { return _num_frames; }
+ size_t get_num_recv_frames(void) const { return _recv_num_frames; }
+ size_t get_num_send_frames(void) const { return _send_num_frames; }
};
/*
* Constructor
* Initializes libusb, opens devices, and sets up interfaces for I/O.
- * Finally, creates endpoints for asynchronous I/O.
+ * Finally, creates endpoints for asynchronous I/O.
*/
-libusb_zero_copy_impl::libusb_zero_copy_impl(usb_device_handle::sptr handle,
- unsigned int rx_endpoint,
- unsigned int tx_endpoint,
- size_t buff_size,
- size_t block_size)
- : _rx_ctx(NULL), _tx_ctx(NULL), _rx_dev_handle(NULL), _tx_dev_handle(NULL),
- _recv_buff_size(block_size), _send_buff_size(block_size),
- _num_frames(buff_size / block_size)
-{
- // Initialize libusb with separate contexts to allow
- // thread safe operation of transmit and receive
- libusb::init(&_rx_ctx, libusb_debug_level);
- libusb::init(&_tx_ctx, libusb_debug_level);
-
- UHD_ASSERT_THROW((_rx_ctx != NULL) && (_tx_ctx != NULL));
-
- // Find and open the libusb_device corresponding to the
- // given handle and return the libusb_device_handle
- // that can be used for I/O purposes.
- _rx_dev_handle = libusb::open_device(_rx_ctx, handle);
- _tx_dev_handle = libusb::open_device(_tx_ctx, handle);
-
- // Open USB interfaces for tx/rx using magic values.
- // IN interface: 2
- // OUT interface: 1
- // Control interface: 0
- libusb::open_interface(_rx_dev_handle, 2);
- libusb::open_interface(_tx_dev_handle, 1);
-
- _rx_ep = new usb_endpoint(_rx_dev_handle, // libusb device_handle
- _rx_ctx, // libusb context
- rx_endpoint, // USB endpoint number
+libusb_zero_copy_impl::libusb_zero_copy_impl(
+ libusb::device_handle::sptr handle,
+ unsigned int recv_endpoint, unsigned int send_endpoint,
+ size_t recv_xfer_size, size_t recv_num_xfers,
+ size_t send_xfer_size, size_t send_num_xfers
+){
+ _handle = handle;
+
+ //if the sizes are left at 0 (automatic) -> use the defaults
+ if (recv_xfer_size == 0) recv_xfer_size = DEFAULT_XFER_SIZE;
+ if (recv_num_xfers == 0) recv_num_xfers = DEFAULT_NUM_XFERS;
+ if (send_xfer_size == 0) send_xfer_size = DEFAULT_XFER_SIZE;
+ if (send_num_xfers == 0) send_num_xfers = DEFAULT_NUM_XFERS;
+
+ //sanity check the transfer sizes
+ UHD_ASSERT_THROW(recv_xfer_size % 512 == 0);
+ UHD_ASSERT_THROW(send_xfer_size % 512 == 0);
+
+ //store the num xfers for the num frames count
+ _recv_num_frames = recv_num_xfers;
+ _send_num_frames = send_num_xfers;
+
+ _handle->claim_interface(2 /*in interface*/);
+ _handle->claim_interface(1 /*out interface*/);
+
+ _recv_ep = usb_endpoint::sptr(new usb_endpoint(
+ _handle, // libusb device_handle
+ recv_endpoint, // USB endpoint number
true, // IN endpoint
- _recv_buff_size, // buffer size per transfer
- _num_frames); // number of libusb transfers
+ recv_xfer_size, // buffer size per transfer
+ recv_num_xfers // number of libusb transfers
+ ));
- _tx_ep = new usb_endpoint(_tx_dev_handle, // libusb device_handle
- _tx_ctx, // libusb context
- tx_endpoint, // USB endpoint number
+ _send_ep = usb_endpoint::sptr(new usb_endpoint(
+ _handle, // libusb device_handle
+ send_endpoint, // USB endpoint number
false, // OUT endpoint
- _send_buff_size, // buffer size per transfer
- _num_frames); // number of libusb transfers
-}
-
-
-libusb_zero_copy_impl::~libusb_zero_copy_impl()
-{
- delete _rx_ep;
- delete _tx_ep;
-
- libusb_close(_rx_dev_handle);
- libusb_close(_tx_dev_handle);
-
- libusb_exit(_rx_ctx);
- libusb_exit(_tx_ctx);
+ send_xfer_size, // buffer size per transfer
+ send_num_xfers // number of libusb transfers
+ ));
}
-
/*
* Construct a managed receive buffer from a completed libusb transfer
* (happy with buffer full of data) obtained from the receive endpoint.
* Return empty pointer if no transfer is available (timeout or error).
- * \return pointer to a managed receive buffer
+ * \return pointer to a managed receive buffer
*/
-managed_recv_buffer::sptr libusb_zero_copy_impl::get_recv_buff()
-{
- libusb_transfer *lut = _rx_ep->get_completed_transfer();
+managed_recv_buffer::sptr libusb_zero_copy_impl::get_recv_buff(size_t timeout_ms){
+ libusb_transfer *lut = _recv_ep->get_lut_with_wait(timeout_ms);
if (lut == NULL) {
return managed_recv_buffer::sptr();
}
else {
return managed_recv_buffer::sptr(
new libusb_managed_recv_buffer_impl(lut,
- _rx_ep));
+ _recv_ep));
}
}
@@ -728,39 +476,36 @@ managed_recv_buffer::sptr libusb_zero_copy_impl::get_recv_buff()
* Construct a managed send buffer from a free libusb transfer (with
* empty buffer). Return empty pointer of no transfer is available
* (timeout or error).
- * \return pointer to a managed send buffer
+ * \return pointer to a managed send buffer
*/
-managed_send_buffer::sptr libusb_zero_copy_impl::get_send_buff()
-{
- libusb_transfer *lut = _tx_ep->get_free_transfer();
+managed_send_buffer::sptr libusb_zero_copy_impl::get_send_buff(void){
+ libusb_transfer *lut = _send_ep->get_lut_with_wait(/* TODO timeout API */);
if (lut == NULL) {
return managed_send_buffer::sptr();
}
else {
return managed_send_buffer::sptr(
new libusb_managed_send_buffer_impl(lut,
- _tx_ep,
- _send_buff_size));
+ _send_ep));
}
}
-
/***********************************************************************
* USB zero_copy make functions
**********************************************************************/
-usb_zero_copy::sptr usb_zero_copy::make(usb_device_handle::sptr handle,
- unsigned int rx_endpoint,
- unsigned int tx_endpoint,
- size_t buff_size,
- size_t block_size)
-
-{
- return sptr(new libusb_zero_copy_impl(handle,
- rx_endpoint,
- tx_endpoint,
- buff_size,
- block_size));
+usb_zero_copy::sptr usb_zero_copy::make(
+ usb_device_handle::sptr handle,
+ unsigned int recv_endpoint, unsigned int send_endpoint,
+ size_t recv_xfer_size, size_t recv_num_xfers,
+ size_t send_xfer_size, size_t send_num_xfers
+){
+ libusb::device_handle::sptr dev_handle(libusb::device_handle::get_cached_handle(
+ boost::static_pointer_cast<libusb::special_handle>(handle)->get_device()
+ ));
+ return sptr(new libusb_zero_copy_impl(
+ dev_handle,
+ recv_endpoint, send_endpoint,
+ recv_xfer_size, recv_num_xfers,
+ send_xfer_size, send_num_xfers
+ ));
}
-
-
-
diff --git a/host/lib/transport/include/stdint.h b/host/lib/transport/msvc/stdint.h
index b3eb61aae..b3eb61aae 100644
--- a/host/lib/transport/include/stdint.h
+++ b/host/lib/transport/msvc/stdint.h
diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp
index ee989ee2b..0a6c9f2af 100644
--- a/host/lib/transport/udp_zero_copy_asio.cpp
+++ b/host/lib/transport/udp_zero_copy_asio.cpp
@@ -35,7 +35,6 @@ static const size_t MIN_RECV_SOCK_BUFF_SIZE = size_t(sizeof(boost::uint32_t) * 2
//Perhaps this is due to the kernel scheduling,
//but may change with host-based flow control.
static const size_t MIN_SEND_SOCK_BUFF_SIZE = size_t(10e3);
-static const double RECV_TIMEOUT = 0.1; //100 ms
/***********************************************************************
* Zero Copy UDP implementation with ASIO:
@@ -110,11 +109,11 @@ private:
boost::asio::io_service _io_service;
int _sock_fd;
- ssize_t recv(const boost::asio::mutable_buffer &buff){
+ ssize_t recv(const boost::asio::mutable_buffer &buff, size_t timeout_ms){
//setup timeval for timeout
timeval tv;
tv.tv_sec = 0;
- tv.tv_usec = int(RECV_TIMEOUT*1e6);
+ tv.tv_usec = timeout_ms*1000;
//setup rset for timeout
fd_set rset;
diff --git a/host/lib/transport/zero_copy.cpp b/host/lib/transport/zero_copy.cpp
index 8a1cde694..1fcf846a0 100644
--- a/host/lib/transport/zero_copy.cpp
+++ b/host/lib/transport/zero_copy.cpp
@@ -68,12 +68,12 @@ phony_zero_copy_recv_if::~phony_zero_copy_recv_if(void){
/* NOP */
}
-managed_recv_buffer::sptr phony_zero_copy_recv_if::get_recv_buff(void){
+managed_recv_buffer::sptr phony_zero_copy_recv_if::get_recv_buff(size_t timeout_ms){
//allocate memory
boost::uint8_t *recv_mem = new boost::uint8_t[_impl->max_buff_size];
//call recv() with timeout option
- ssize_t num_bytes = this->recv(boost::asio::buffer(recv_mem, _impl->max_buff_size));
+ ssize_t num_bytes = this->recv(boost::asio::buffer(recv_mem, _impl->max_buff_size), timeout_ms);
if (num_bytes <= 0) return managed_recv_buffer::sptr(); //NULL sptr
diff --git a/host/lib/usrp/dboard/CMakeLists.txt b/host/lib/usrp/dboard/CMakeLists.txt
index 3e995009e..8d3d11530 100644
--- a/host/lib/usrp/dboard/CMakeLists.txt
+++ b/host/lib/usrp/dboard/CMakeLists.txt
@@ -24,5 +24,6 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_wbx.cpp
${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_dbsrx.cpp
${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_unknown.cpp
+ ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_tvrx.cpp
)
diff --git a/host/lib/usrp/dboard/db_tvrx.cpp b/host/lib/usrp/dboard/db_tvrx.cpp
new file mode 100644
index 000000000..1cb74c5ae
--- /dev/null
+++ b/host/lib/usrp/dboard/db_tvrx.cpp
@@ -0,0 +1,477 @@
+//
+// 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/>.
+//
+
+// No RX IO Pins Used
+
+// RX IO Functions
+
+//ADC/DAC functions:
+//DAC 1: RF AGC
+//DAC 2: IF AGC
+
+//min freq: 50e6
+//max freq: 860e6
+//gain range: [0:1dB:115dB]
+
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/assert.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/utils/warning.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/usrp/subdev_props.hpp>
+#include <uhd/usrp/dboard_base.hpp>
+#include <uhd/usrp/dboard_manager.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/format.hpp>
+#include <boost/thread.hpp>
+#include <boost/array.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <utility>
+#include <cmath>
+#include <cfloat>
+#include <limits>
+#include <tuner_4937di5_regs.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+/***********************************************************************
+ * The tvrx constants
+ **********************************************************************/
+static const bool tvrx_debug = false;
+
+static const freq_range_t tvrx_freq_range(50e6, 860e6);
+
+static const std::string tvrx_antennas = std::string(""); //only got one
+
+static const uhd::dict<std::string, freq_range_t> tvrx_freq_ranges = map_list_of
+ ("VHFLO", freq_range_t(50e6, 158e6))
+ ("VHFHI", freq_range_t(158e6, 454e6))
+ ("UHF" , freq_range_t(454e6, 860e6))
+;
+
+static const boost::array<float, 17> vhflo_gains_db =
+ {{-6.00000, -6.00000, -6.00000, -4.00000, 0.00000,
+ 5.00000, 10.00000, 17.40000, 26.30000, 36.00000,
+ 43.00000, 48.00000, 49.50000, 50.10000, 50.30000,
+ 50.30000, 50.30000}};
+
+static const boost::array<float, 17> vhfhi_gains_db =
+ {{13.3000, -13.3000, -13.3000, -1.0000, 7.7000,
+ 11.0000, 14.7000, 19.3000, 26.1000, 36.0000,
+ 42.7000, 46.0000, 47.0000, 47.8000, 48.2000,
+ 48.2000, 48.2000}};
+
+static const boost::array<float, 17> uhf_gains_db =
+ {{-8.0000, -8.0000, -7.0000, 4.0000, 10.2000,
+ 14.5000, 17.5000, 20.0000, 24.5000, 30.8000,
+ 37.0000, 39.8000, 40.7000, 41.6000, 42.6000,
+ 43.2000, 43.8000}};
+
+static const boost::array<float, 17> tvrx_if_gains_db =
+ {{-1.50000, -1.50000, -1.50000, -1.00000, 0.20000,
+ 2.10000, 4.30000, 6.40000, 9.00000, 12.00000,
+ 14.80000, 18.20000, 26.10000, 32.50000, 32.50000,
+ 32.50000, 32.50000}};
+
+//gain linearization data
+//this is from the datasheet and is dB vs. volts (below)
+//i tried to curve fit this, but it's really just so nonlinear that you'd
+//need dang near as many coefficients as to just map it like this and interp.
+//these numbers are culled from the 4937DI5 datasheet and are probably totally inaccurate
+//but if it's better than the old linear fit i'm happy
+static const uhd::dict<std::string, boost::array<float, 17> > tvrx_rf_gains_db = map_list_of
+ ("VHFLO", vhflo_gains_db)
+ ("VHFHI", vhfhi_gains_db)
+ ("UHF" , uhf_gains_db)
+;
+
+//sample voltages for the above points
+static const boost::array<float, 17> tvrx_gains_volts =
+ {{0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0}};
+
+static uhd::dict<std::string, gain_range_t> get_tvrx_gain_ranges(void) {
+ float rfmax = 0.0, rfmin = FLT_MAX;
+ BOOST_FOREACH(const std::string range, tvrx_rf_gains_db.keys()) {
+ float my_max = tvrx_rf_gains_db[range].back(); //we're assuming it's monotonic
+ float my_min = tvrx_rf_gains_db[range].front(); //if it's not this is wrong wrong wrong
+ if(my_max > rfmax) rfmax = my_max;
+ if(my_min < rfmin) rfmin = my_min;
+ }
+
+ float ifmin = tvrx_if_gains_db.front();
+ float ifmax = tvrx_if_gains_db.back();
+
+ return map_list_of
+ ("RF", gain_range_t(rfmin, rfmax, (rfmax-rfmin)/4096.0))
+ ("IF", gain_range_t(ifmin, ifmax, (ifmax-ifmin)/4096.0))
+ ;
+}
+
+static const double opamp_gain = 1.22; //onboard DAC opamp gain
+static const double tvrx_if_freq = 43.75e6; //IF freq of TVRX module
+static const boost::uint16_t reference_divider = 640; //clock reference divider to use
+static const double reference_freq = 4.0e6;
+
+/***********************************************************************
+ * The tvrx dboard class
+ **********************************************************************/
+class tvrx : public rx_dboard_base{
+public:
+ tvrx(ctor_args_t args);
+ ~tvrx(void);
+
+ void rx_get(const wax::obj &key, wax::obj &val);
+ void rx_set(const wax::obj &key, const wax::obj &val);
+
+private:
+ uhd::dict<std::string, float> _gains;
+ double _lo_freq;
+ tuner_4937di5_regs_t _tuner_4937di5_regs;
+ boost::uint8_t _tuner_4937di5_addr(void){
+ return (this->get_iface()->get_special_props().mangle_i2c_addrs)? 0x61 : 0x60; //ok really? we could rename that call
+ };
+
+ void set_gain(float gain, const std::string &name);
+ void set_freq(double freq);
+
+ void update_regs(void){
+ byte_vector_t regs_vector(4);
+
+ //get the register data
+ for(int i=0; i<4; i++){
+ regs_vector[i] = _tuner_4937di5_regs.get_reg(i);
+ if(tvrx_debug) std::cerr << boost::format(
+ "tvrx: send reg 0x%02x, value 0x%04x"
+ ) % int(i) % int(regs_vector[i]) << std::endl;
+ }
+
+ //send the data
+ this->get_iface()->write_i2c(
+ _tuner_4937di5_addr(), regs_vector
+ );
+ }
+
+};
+
+/***********************************************************************
+ * Register the tvrx dboard
+ **********************************************************************/
+static dboard_base::sptr make_tvrx(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new tvrx(args));
+}
+
+UHD_STATIC_BLOCK(reg_tvrx_dboard){
+ //register the factory function for the rx dbid
+ dboard_manager::register_dboard(0x0040, &make_tvrx, "tvrx");
+}
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+tvrx::tvrx(ctor_args_t args) : rx_dboard_base(args){
+ this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true);
+
+ //set the gpio directions and atr controls (identically)
+ this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0x0); // All unused in atr
+ if (this->get_iface()->get_special_props().soft_clock_divider){
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x1); // GPIO0 is clock
+ }
+ else{
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x0); // All Inputs
+ }
+
+ //send initial register settings if necessary
+
+ //set default freq
+ _lo_freq = tvrx_freq_range.min + tvrx_if_freq; //init _lo_freq to a sane default
+ set_freq(tvrx_freq_range.min);
+
+ //set default gains
+ BOOST_FOREACH(const std::string &name, get_tvrx_gain_ranges().keys()){
+ set_gain(get_tvrx_gain_ranges()[name].min, name);
+ }
+}
+
+tvrx::~tvrx(void){
+}
+
+/*! Return a string corresponding to the relevant band
+ * \param freq the frequency of interest
+ * \return a string corresponding to the band
+ */
+
+static std::string get_band(double freq) {
+ BOOST_FOREACH(const std::string &band, tvrx_freq_ranges.keys()) {
+ if(freq >= tvrx_freq_ranges[band].min && freq <= tvrx_freq_ranges[band].max){
+ if(tvrx_debug) std::cout << "Band: " << band << std::endl;
+ return band;
+ }
+ }
+ UHD_THROW_INVALID_CODE_PATH();
+}
+
+/***********************************************************************
+ * Gain Handling
+ **********************************************************************/
+/*!
+ * Execute a linear interpolation to find the voltage corresponding to a desired gain
+ * \param gain the desired gain in dB
+ * \param db_vector the vector of dB readings
+ * \param volts_vector the corresponding vector of voltages db_vector was sampled at
+ * \return a voltage to feed the TVRX analog gain
+ */
+
+static float gain_interp(float gain, boost::array<float, 17> db_vector, boost::array<float, 17> volts_vector) {
+ float volts;
+ gain = std::clip<float>(gain, db_vector.front(), db_vector.back()); //let's not get carried away here
+
+ boost::uint8_t gain_step = 0;
+ //find which bin we're in
+ for(size_t i = 0; i < db_vector.size()-1; i++) {
+ if(gain >= db_vector[i] && gain <= db_vector[i+1]) gain_step = i;
+ }
+
+ //find the current slope for linear interpolation
+ float slope = (volts_vector[gain_step + 1] - volts_vector[gain_step])
+ / (db_vector[gain_step + 1] - db_vector[gain_step]);
+
+ //the problem here is that for gains approaching the maximum, the voltage slope becomes infinite
+ //i.e., a small change in gain requires an infinite change in voltage
+ //to cope, we limit the slope
+
+ if(slope == std::numeric_limits<float>::infinity())
+ return volts_vector[gain_step];
+
+ //use the volts per dB slope to find the final interpolated voltage
+ volts = volts_vector[gain_step] + (slope * (gain - db_vector[gain_step]));
+
+ if(tvrx_debug)
+ std::cout << "Gain interp: gain: " << gain << ", gain_step: " << int(gain_step) << ", slope: " << slope << ", volts: " << volts << std::endl;
+
+ return volts;
+}
+
+/*!
+ * Convert a requested gain for the RF gain into a DAC voltage.
+ * The gain passed into the function will be set to the actual value.
+ * \param gain the requested gain in dB
+ * \return dac voltage value
+ */
+
+static float rf_gain_to_voltage(float gain, double lo_freq){
+ //clip the input
+ gain = std::clip<float>(gain, get_tvrx_gain_ranges()["RF"].min, get_tvrx_gain_ranges()["RF"].max);
+
+ //first we need to find out what band we're in, because gains are different across different bands
+ std::string band = get_band(lo_freq + tvrx_if_freq);
+
+ //this is the voltage at the TVRX gain input
+ float gain_volts = gain_interp(gain, tvrx_rf_gains_db[band], tvrx_gains_volts);
+ //this is the voltage at the USRP DAC output
+ float dac_volts = gain_volts / opamp_gain;
+
+ dac_volts = std::clip<float>(dac_volts, 0.0, 3.3);
+
+ if (tvrx_debug) std::cerr << boost::format(
+ "tvrx RF AGC gain: %f dB, dac_volts: %f V"
+ ) % gain % dac_volts << std::endl;
+
+ return dac_volts;
+}
+
+/*!
+ * Convert a requested gain for the IF gain into a DAC voltage.
+ * The gain passed into the function will be set to the actual value.
+ * \param gain the requested gain in dB
+ * \return dac voltage value
+ */
+
+static float if_gain_to_voltage(float gain){
+ //clip the input
+ gain = std::clip<float>(gain, get_tvrx_gain_ranges()["IF"].min, get_tvrx_gain_ranges()["IF"].max);
+
+ float gain_volts = gain_interp(gain, tvrx_if_gains_db, tvrx_gains_volts);
+ float dac_volts = gain_volts / opamp_gain;
+
+ dac_volts = std::clip<float>(dac_volts, 0.0, 3.3);
+
+ if (tvrx_debug) std::cerr << boost::format(
+ "tvrx IF AGC gain: %f dB, dac_volts: %f V"
+ ) % gain % dac_volts << std::endl;
+
+ return dac_volts;
+}
+
+void tvrx::set_gain(float gain, const std::string &name){
+ assert_has(get_tvrx_gain_ranges().keys(), name, "tvrx gain name");
+ if (name == "RF"){
+ this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_B, rf_gain_to_voltage(gain, _lo_freq));
+ }
+ else if(name == "IF"){
+ this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_A, if_gain_to_voltage(gain));
+ }
+ else UHD_THROW_INVALID_CODE_PATH();
+ _gains[name] = gain;
+}
+
+/*!
+ * Set the tuner to center the desired frequency at 43.75MHz
+ * \param freq the requested frequency
+ */
+
+void tvrx::set_freq(double freq) {
+ freq = std::clip<float>(freq, tvrx_freq_range.min, tvrx_freq_range.max);
+ std::string prev_band = get_band(_lo_freq - tvrx_if_freq);
+ std::string new_band = get_band(freq);
+
+ double target_lo_freq = freq + tvrx_if_freq; //the desired LO freq for high-side mixing
+ double f_ref = reference_freq / double(reference_divider); //your tuning step size
+
+ int divisor = int((target_lo_freq + (f_ref * 4.0)) / (f_ref * 8)); //the divisor we'll use
+ double actual_lo_freq = (f_ref * 8 * divisor); //the LO freq we'll actually get
+
+ if((divisor & ~0x7fff)) UHD_THROW_INVALID_CODE_PATH();
+
+ //now we update the registers
+ _tuner_4937di5_regs.db1 = (divisor >> 8) & 0xff;
+ _tuner_4937di5_regs.db2 = divisor & 0xff;
+
+ if(new_band == "VHFLO") _tuner_4937di5_regs.bandsel = tuner_4937di5_regs_t::BANDSEL_VHFLO;
+ else if(new_band == "VHFHI") _tuner_4937di5_regs.bandsel = tuner_4937di5_regs_t::BANDSEL_VHFHI;
+ else if(new_band == "UHF") _tuner_4937di5_regs.bandsel = tuner_4937di5_regs_t::BANDSEL_UHF;
+ else UHD_THROW_INVALID_CODE_PATH();
+
+ _tuner_4937di5_regs.power = tuner_4937di5_regs_t::POWER_OFF;
+ update_regs();
+
+ //ok don't forget to reset RF gain here if the new band != the old band
+ //we do this because the gains are different for different band settings
+ //not FAR off, but we do this to be consistent
+ if(prev_band != new_band) set_gain(_gains["RF"], "RF");
+
+ if(tvrx_debug)
+ std::cout << boost::format("set_freq: target LO: %f f_ref: %f divisor: %i actual LO: %f") % target_lo_freq % f_ref % divisor % actual_lo_freq << std::endl;
+
+ _lo_freq = actual_lo_freq; //for rx props
+}
+
+/***********************************************************************
+ * Get the alias frequency of frequency freq when sampled at fs.
+ * \param freq the frequency of interest
+ * \param fs the sample rate
+ * \return the alias frequency
+ **********************************************************************/
+
+static double get_alias(double freq, double fs) {
+ double alias;
+ freq = fmod(freq, fs);
+ if(freq >= (fs/2)) {
+ alias = fs - freq;
+ } else {
+ alias = freq;
+ }
+ return alias;
+}
+
+/***********************************************************************
+ * RX Get and Set
+ **********************************************************************/
+void tvrx::rx_get(const wax::obj &key_, wax::obj &val){
+ named_prop_t key = named_prop_t::extract(key_);
+ double codec_rate;
+
+ //handle the get request conditioned on the key
+ switch(key.as<subdev_prop_t>()){
+ case SUBDEV_PROP_NAME:
+ val = get_rx_id().to_pp_string();
+ return;
+
+ case SUBDEV_PROP_OTHERS:
+ val = prop_names_t(); //empty
+ return;
+
+ case SUBDEV_PROP_GAIN:
+ assert_has(_gains.keys(), key.name, "tvrx gain name");
+ val = _gains[key.name];
+ return;
+
+ case SUBDEV_PROP_GAIN_RANGE:
+ assert_has(get_tvrx_gain_ranges().keys(), key.name, "tvrx gain name");
+ val = get_tvrx_gain_ranges()[key.name];
+ return;
+
+ case SUBDEV_PROP_GAIN_NAMES:
+ val = prop_names_t(get_tvrx_gain_ranges().keys());
+ return;
+
+ case SUBDEV_PROP_FREQ:
+ /*
+ * so here we have to do some magic. because the TVRX uses a relatively high IF,
+ * we have to watch the sample rate to see if the IF will be aliased
+ * or if it will fall within Nyquist.
+ */
+ codec_rate = this->get_iface()->get_codec_rate(dboard_iface::UNIT_RX);
+ val = (_lo_freq - tvrx_if_freq) + get_alias(tvrx_if_freq, codec_rate);
+ return;
+
+ case SUBDEV_PROP_FREQ_RANGE:
+ val = tvrx_freq_range;
+ return;
+
+ case SUBDEV_PROP_ANTENNA:
+ val = std::string(tvrx_antennas);
+ return;
+
+ case SUBDEV_PROP_ANTENNA_NAMES:
+ val = tvrx_antennas;
+ return;
+
+ case SUBDEV_PROP_CONNECTION:
+ val = SUBDEV_CONN_COMPLEX_IQ;
+ return;
+
+ case SUBDEV_PROP_USE_LO_OFFSET:
+ val = false;
+ return;
+
+ case SUBDEV_PROP_LO_LOCKED:
+ val = true;
+ return;
+
+ default: UHD_THROW_PROP_GET_ERROR();
+ }
+}
+
+void tvrx::rx_set(const wax::obj &key_, const wax::obj &val){
+ named_prop_t key = named_prop_t::extract(key_);
+
+ //handle the get request conditioned on the key
+ switch(key.as<subdev_prop_t>()){
+ case SUBDEV_PROP_GAIN:
+ this->set_gain(val.as<float>(), key.name);
+ return;
+ case SUBDEV_PROP_FREQ:
+ this->set_freq(val.as<double>());
+ return;
+
+ default: UHD_THROW_PROP_SET_ERROR();
+ }
+}
+
diff --git a/host/lib/usrp/dboard/db_wbx.cpp b/host/lib/usrp/dboard/db_wbx.cpp
index ccd897674..e473ce41e 100644
--- a/host/lib/usrp/dboard/db_wbx.cpp
+++ b/host/lib/usrp/dboard/db_wbx.cpp
@@ -154,7 +154,7 @@ static dboard_base::sptr make_wbx(dboard_base::ctor_args_t args){
}
UHD_STATIC_BLOCK(reg_wbx_dboards){
- dboard_manager::register_dboard(0x0052, 0x0053, &make_wbx, "WBX");
+ dboard_manager::register_dboard(0x0053, 0x0052, &make_wbx, "WBX");
}
/***********************************************************************
diff --git a/host/lib/usrp/dboard_manager.cpp b/host/lib/usrp/dboard_manager.cpp
index ab80875f5..181f843a0 100644
--- a/host/lib/usrp/dboard_manager.cpp
+++ b/host/lib/usrp/dboard_manager.cpp
@@ -18,6 +18,7 @@
#include "dboard_ctor_args.hpp"
#include <uhd/usrp/dboard_manager.hpp>
#include <uhd/usrp/subdev_props.hpp>
+#include <uhd/utils/warning.hpp>
#include <uhd/utils/static.hpp>
#include <uhd/utils/assert.hpp>
#include <uhd/types/dict.hpp>
@@ -176,10 +177,6 @@ static args_t get_dboard_args(
){
//special case, the none id was provided, use the following ids
if (dboard_id == dboard_id_t::none() or force_to_unknown){
- std::cerr << boost::format(
- "Warning: unknown dboard-id or dboard-id combination: %s\n"
- " -> defaulting to the unknown board type"
- ) % dboard_id.to_pp_string() << std::endl;
UHD_ASSERT_THROW(get_id_to_args_map().has_key(0xfff1));
UHD_ASSERT_THROW(get_id_to_args_map().has_key(0xfff0));
switch(unit){
@@ -191,6 +188,9 @@ static args_t get_dboard_args(
//verify that there is a registered constructor for this id
if (not get_id_to_args_map().has_key(dboard_id)){
+ uhd::print_warning(str(boost::format(
+ "Unknown dboard ID: %s.\n"
+ ) % dboard_id.to_pp_string()));
return get_dboard_args(unit, dboard_id, true);
}
@@ -214,12 +214,25 @@ dboard_manager_impl::dboard_manager_impl(
(get_xcvr_id_to_id_map()[tx_dboard_id] == rx_dboard_id)
);
+ //warn for invalid dboard id xcvr combinations
+ if (rx_dboard_is_xcvr != this_dboard_is_xcvr or tx_dboard_is_xcvr != this_dboard_is_xcvr){
+ uhd::print_warning(str(boost::format(
+ "Unknown transceiver board ID combination...\n"
+ "RX dboard ID: %s\n"
+ "TX dboard ID: %s\n"
+ ) % rx_dboard_id.to_pp_string() % tx_dboard_id.to_pp_string()));
+ }
+
//extract dboard constructor and settings (force to unknown for messed up xcvr status)
dboard_ctor_t rx_dboard_ctor; std::string rx_name; prop_names_t rx_subdevs;
- boost::tie(rx_dboard_ctor, rx_name, rx_subdevs) = get_dboard_args(dboard_iface::UNIT_RX, rx_dboard_id, rx_dboard_is_xcvr != this_dboard_is_xcvr);
+ boost::tie(rx_dboard_ctor, rx_name, rx_subdevs) = get_dboard_args(
+ dboard_iface::UNIT_RX, rx_dboard_id, rx_dboard_is_xcvr != this_dboard_is_xcvr
+ );
dboard_ctor_t tx_dboard_ctor; std::string tx_name; prop_names_t tx_subdevs;
- boost::tie(tx_dboard_ctor, tx_name, tx_subdevs) = get_dboard_args(dboard_iface::UNIT_TX, tx_dboard_id, tx_dboard_is_xcvr != this_dboard_is_xcvr);
+ boost::tie(tx_dboard_ctor, tx_name, tx_subdevs) = get_dboard_args(
+ dboard_iface::UNIT_TX, tx_dboard_id, tx_dboard_is_xcvr != this_dboard_is_xcvr
+ );
//initialize the gpio pins before creating subdevs
set_nice_dboard_if();
diff --git a/host/lib/usrp/usrp1/codec_ctrl.cpp b/host/lib/usrp/usrp1/codec_ctrl.cpp
index ad16f6b3a..4aa730573 100644
--- a/host/lib/usrp/usrp1/codec_ctrl.cpp
+++ b/host/lib/usrp/usrp1/codec_ctrl.cpp
@@ -61,6 +61,9 @@ public:
float get_tx_pga_gain(void);
void set_rx_pga_gain(float, char);
float get_rx_pga_gain(char);
+
+ //rx adc buffer control
+ void bypass_adc_buffers(bool bypass);
private:
usrp1_iface::sptr _iface;
@@ -419,6 +422,17 @@ void usrp1_codec_ctrl_impl::set_duc_freq(double freq)
}
/***********************************************************************
+ * Codec Control ADC buffer bypass
+ * Disable this for AC-coupled daughterboards (TVRX)
+ * By default it is initialized TRUE.
+ **********************************************************************/
+void usrp1_codec_ctrl_impl::bypass_adc_buffers(bool bypass) {
+ _ad9862_regs.byp_buffer_a = bypass;
+ _ad9862_regs.byp_buffer_b = bypass;
+ this->send_reg(2);
+}
+
+/***********************************************************************
* Codec Control Make
**********************************************************************/
usrp1_codec_ctrl::sptr usrp1_codec_ctrl::make(usrp1_iface::sptr iface,
diff --git a/host/lib/usrp/usrp1/codec_ctrl.hpp b/host/lib/usrp/usrp1/codec_ctrl.hpp
index 259d10ef4..e2e8a010d 100644
--- a/host/lib/usrp/usrp1/codec_ctrl.hpp
+++ b/host/lib/usrp/usrp1/codec_ctrl.hpp
@@ -92,6 +92,9 @@ public:
//! Set the TX modulator frequency
virtual void set_duc_freq(double freq) = 0;
+
+ //! Enable or disable ADC buffer bypass
+ virtual void bypass_adc_buffers(bool bypass) = 0;
};
#endif /* INCLUDED_USRP1_CODEC_CTRL_HPP */
diff --git a/host/lib/usrp/usrp1/dboard_iface.cpp b/host/lib/usrp/usrp1/dboard_iface.cpp
index 4791b55ce..1ac15a46a 100644
--- a/host/lib/usrp/usrp1/dboard_iface.cpp
+++ b/host/lib/usrp/usrp1/dboard_iface.cpp
@@ -32,6 +32,8 @@ using namespace uhd;
using namespace uhd::usrp;
using namespace boost::assign;
+static const dboard_id_t tvrx_id(0x0040);
+
class usrp1_dboard_iface : public dboard_iface {
public:
@@ -51,6 +53,10 @@ public:
//init the clock rate shadows
this->set_clock_rate(UNIT_RX, this->get_clock_rates(UNIT_RX).front());
this->set_clock_rate(UNIT_TX, this->get_clock_rates(UNIT_TX).front());
+
+ //yes this is evil but it's necessary for TVRX to work on USRP1
+ if(_rx_dboard_id == tvrx_id) _codec->bypass_adc_buffers(false);
+ //else _codec->bypass_adc_buffers(false); //don't think this is necessary
}
~usrp1_dboard_iface()
@@ -93,6 +99,7 @@ public:
std::vector<double> get_clock_rates(unit_t);
double get_clock_rate(unit_t);
void set_clock_enabled(unit_t, bool);
+ double get_codec_rate(unit_t);
private:
usrp1_iface::sptr _iface;
@@ -170,6 +177,10 @@ void usrp1_dboard_iface::set_clock_enabled(unit_t, bool)
//TODO we can only enable for special case anyway...
}
+double usrp1_dboard_iface::get_codec_rate(unit_t){
+ return _clock->get_master_clock_freq();
+}
+
/***********************************************************************
* GPIO
**********************************************************************/
diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp
index 73974f2d6..aee760a83 100644
--- a/host/lib/usrp/usrp1/io_impl.cpp
+++ b/host/lib/usrp/usrp1/io_impl.cpp
@@ -272,18 +272,18 @@ static void usrp1_bs_vrt_unpacker(
}
static bool get_recv_buffs(
- zero_copy_if::sptr zc_if,
+ zero_copy_if::sptr zc_if, size_t timeout_ms,
vrt_packet_handler::managed_recv_buffs_t &buffs
){
UHD_ASSERT_THROW(buffs.size() == 1);
- buffs[0] = zc_if->get_recv_buff();
+ buffs[0] = zc_if->get_recv_buff(timeout_ms);
return buffs[0].get() != NULL;
}
size_t usrp1_impl::recv(
const std::vector<void *> &buffs, size_t num_samps,
rx_metadata_t &metadata, const io_type_t &io_type,
- recv_mode_t recv_mode, size_t /*timeout_ms TODO*/
+ recv_mode_t recv_mode, size_t timeout_ms
){
size_t num_samps_recvd = vrt_packet_handler::recv(
_io_impl->packet_handler_recv_state, //last state of the recv handler
@@ -292,7 +292,7 @@ size_t usrp1_impl::recv(
io_type, _rx_otw_type, //input and output types to convert
_clock_ctrl->get_master_clock_freq(), //master clock tick rate
&usrp1_bs_vrt_unpacker,
- boost::bind(&get_recv_buffs, _data_transport, _1),
+ boost::bind(&get_recv_buffs, _data_transport, timeout_ms, _1),
&vrt_packet_handler::handle_overflow_nop,
0, //vrt header offset
_rx_subdev_spec.size() //num channels
diff --git a/host/lib/usrp/usrp1/usrp1_ctrl.cpp b/host/lib/usrp/usrp1/usrp1_ctrl.cpp
index 1dc6e6e25..5043aed7d 100644
--- a/host/lib/usrp/usrp1/usrp1_ctrl.cpp
+++ b/host/lib/usrp/usrp1/usrp1_ctrl.cpp
@@ -38,6 +38,8 @@ enum firmware_code {
#define FX2_FIRMWARE_LOAD 0xa0
+static const bool load_img_msg = true;
+
/***********************************************************************
* Helper Functions
**********************************************************************/
@@ -178,6 +180,7 @@ public:
unsigned char reset_n = 0;
//hit the reset line
+ if (load_img_msg) std::cout << "Loading firmware image: " << filestring << "..." << std::flush;
usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0,
&reset_y, 1);
@@ -206,14 +209,14 @@ public:
}
//type 0x01 is end
else if (type == 0x01) {
+ usrp_set_firmware_hash(hash); //set hash before reset
usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0,
&reset_n, 1);
- usrp_set_firmware_hash(hash);
file.close();
//wait for things to settle
boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
-
+ if (load_img_msg) std::cout << " done" << std::endl;
return USRP_FIRMWARE_LOAD_SUCCESS;
}
//type anything else is unhandled
@@ -249,6 +252,7 @@ public:
unsigned char buf[ep0_size];
int ret;
+ if (load_img_msg) std::cout << "Loading FPGA image: " << filestring << "..." << std::flush;
std::ifstream file;
file.open(filename, std::ios::in | std::ios::binary);
if (not file.good()) {
@@ -263,11 +267,12 @@ public:
return -1;
}
- ssize_t n;
- while ((n = file.readsome((char *)buf, sizeof(buf))) > 0) {
+ while (not file.eof()) {
+ file.read((char *)buf, sizeof(buf));
+ size_t n = file.gcount();
ret = usrp_control_write(VRQ_FPGA_LOAD, 0, FL_XFER,
buf, n);
- if (ret != n) {
+ if (ret < 0 or size_t(ret) != n) {
std::cerr << "fpga load error " << ret << std::endl;
file.close();
return -1;
@@ -282,6 +287,7 @@ public:
usrp_set_fpga_hash(hash);
file.close();
+ if (load_img_msg) std::cout << " done" << std::endl;
return 0;
}
diff --git a/host/lib/usrp/usrp1/usrp1_iface.cpp b/host/lib/usrp/usrp1/usrp1_iface.cpp
index 5fd3987d5..64ced2905 100644
--- a/host/lib/usrp/usrp1/usrp1_iface.cpp
+++ b/host/lib/usrp/usrp1/usrp1_iface.cpp
@@ -49,7 +49,7 @@ public:
******************************************************************/
void poke32(boost::uint32_t addr, boost::uint32_t value)
{
- boost::uint32_t swapped = byteswap(value);
+ boost::uint32_t swapped = uhd::htonx(value);
if (iface_debug) {
std::cout.fill('0');
@@ -72,12 +72,6 @@ public:
std::cerr << "USRP: failed memory write: " << ret << std::endl;
}
- void poke16(boost::uint32_t, boost::uint16_t)
- {
- //fpga only handles 32 bit writes
- std::cerr << "USRP: unsupported operation: poke16()" << std::endl;
- }
-
boost::uint32_t peek32(boost::uint32_t addr)
{
boost::uint32_t value_out;
@@ -95,13 +89,7 @@ public:
if (ret < 0)
std::cerr << "USRP: failed memory read: " << ret << std::endl;
- return byteswap(value_out);
- }
-
- boost::uint16_t peek16(boost::uint32_t addr)
- {
- boost::uint32_t val = peek32(addr);
- return boost::uint16_t(val & 0xff);
+ return uhd::ntohx(value_out);
}
/*******************************************************************
diff --git a/host/lib/usrp/usrp1/usrp1_iface.hpp b/host/lib/usrp/usrp1/usrp1_iface.hpp
index 9a3fdd6bc..3f608584a 100644
--- a/host/lib/usrp/usrp1/usrp1_iface.hpp
+++ b/host/lib/usrp/usrp1/usrp1_iface.hpp
@@ -54,20 +54,6 @@ public:
virtual boost::uint32_t peek32(boost::uint32_t addr) = 0;
/*!
- * Write a register (16 bits)
- * \param addr the address
- * \param data the 16bit data
- */
- virtual void poke16(boost::uint32_t addr, boost::uint16_t data) = 0;
-
- /*!
- * read a register (16 bits)
- * \param addr the address
- * \return the 16bit data
- */
- virtual boost::uint16_t peek16(boost::uint32_t addr) = 0;
-
- /*!
* Perform an spi transaction.
* \param which_slave the slave device number
* \param config spi config args
diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp
index 627180b11..793e3027d 100644
--- a/host/lib/usrp/usrp1/usrp1_impl.cpp
+++ b/host/lib/usrp/usrp1/usrp1_impl.cpp
@@ -30,6 +30,7 @@
#include <boost/assign/list_of.hpp>
#include <boost/filesystem.hpp>
#include <boost/thread/thread.hpp>
+#include <boost/lexical_cast.hpp>
#include <iostream>
using namespace uhd;
@@ -74,14 +75,14 @@ static device_addrs_t usrp1_find(const device_addr_t &hint)
boost::uint16_t vid = hint.has_key("uninit") ? FX2_VENDOR_ID : USRP1_VENDOR_ID;
boost::uint16_t pid = hint.has_key("uninit") ? FX2_PRODUCT_ID : USRP1_PRODUCT_ID;
- //see what we got on the USB bus
- std::vector<usb_device_handle::sptr> device_list =
- usb_device_handle::get_device_list(vid, pid);
-
- if(device_list.size() == 0) return usrp1_addrs; //return nothing if no USRPs found
+ // Important note:
+ // The get device list calls are nested inside the for loop.
+ // This allows the usb guts to decontruct when not in use,
+ // so that re-enumeration after fw load can occur successfully.
+ // This requirement is a courtesy of libusb1.0 on windows.
//find the usrps and load firmware
- BOOST_FOREACH(usb_device_handle::sptr handle, device_list) {
+ BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) {
usb_control::sptr ctrl_transport = usb_control::make(handle);
usrp_ctrl::sptr usrp_ctrl = usrp_ctrl::make(ctrl_transport);
usrp_ctrl->usrp_load_firmware(usrp1_fw_image);
@@ -90,13 +91,15 @@ static device_addrs_t usrp1_find(const device_addr_t &hint)
//get descriptors again with serial number, but using the initialized VID/PID now since we have firmware
vid = USRP1_VENDOR_ID;
pid = USRP1_PRODUCT_ID;
- device_list = usb_device_handle::get_device_list(vid, pid);
- BOOST_FOREACH(usb_device_handle::sptr handle, device_list) {
+ BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) {
device_addr_t new_addr;
new_addr["type"] = "usrp1";
new_addr["serial"] = handle->get_serial();
- usrp1_addrs.push_back(new_addr);
+ //this is a found usrp1 when a hint serial is not specified or it matches
+ if (not hint.has_key("serial") or hint["serial"] == new_addr["serial"]){
+ usrp1_addrs.push_back(new_addr);
+ }
}
return usrp1_addrs;
@@ -105,6 +108,15 @@ static device_addrs_t usrp1_find(const device_addr_t &hint)
/***********************************************************************
* Make
**********************************************************************/
+template<typename output_type> static output_type cast_from_dev_addr(
+ const device_addr_t &device_addr,
+ const std::string &key,
+ output_type def_val
+){
+ return (device_addr.has_key(key))?
+ boost::lexical_cast<output_type>(device_addr[key]) : def_val;
+}
+
static device::sptr usrp1_make(const device_addr_t &device_addr)
{
//extract the FPGA path for the USRP1
@@ -128,11 +140,15 @@ static device::sptr usrp1_make(const device_addr_t &device_addr)
usrp_ctrl = usrp_ctrl::make(ctrl_transport);
usrp_ctrl->usrp_load_fpga(usrp1_fpga_image);
- data_transport = usb_zero_copy::make(handle, // identifier
- 6, // IN endpoint
- 2, // OUT endpoint
- 2 * (1 << 20), // buffer size
- 16384); // transfer size
+ data_transport = usb_zero_copy::make(
+ handle, // identifier
+ 6, // IN endpoint
+ 2, // OUT endpoint
+ size_t(cast_from_dev_addr<double>(device_addr, "recv_xfer_size", 0)),
+ size_t(cast_from_dev_addr<double>(device_addr, "recv_num_xfers", 0)),
+ size_t(cast_from_dev_addr<double>(device_addr, "send_xfer_size", 0)),
+ size_t(cast_from_dev_addr<double>(device_addr, "send_num_xfers", 0))
+ );
break;
}
}
@@ -171,7 +187,7 @@ usrp1_impl::usrp1_impl(uhd::transport::usb_zero_copy::sptr data_transport,
//initialize the mboard
mboard_init();
- //initialize the dboards
+ //initialize the dboards
dboard_init();
//initialize the dsps
diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp
index f6d2b718a..fdfbf0d17 100644
--- a/host/lib/usrp/usrp2/dboard_iface.cpp
+++ b/host/lib/usrp/usrp2/dboard_iface.cpp
@@ -61,6 +61,7 @@ public:
double get_clock_rate(unit_t);
std::vector<double> get_clock_rates(unit_t);
void set_clock_enabled(unit_t, bool);
+ double get_codec_rate(unit_t);
void write_spi(
unit_t unit,
@@ -158,6 +159,9 @@ void usrp2_dboard_iface::set_clock_enabled(unit_t unit, bool enb){
}
}
+double usrp2_dboard_iface::get_codec_rate(unit_t){
+ return _clock_ctrl->get_master_clock_rate();
+}
/***********************************************************************
* GPIO
**********************************************************************/
diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp
index 91a1b2344..3395f94e2 100644
--- a/host/lib/usrp/usrp2/io_impl.cpp
+++ b/host/lib/usrp/usrp2/io_impl.cpp
@@ -33,6 +33,7 @@ using namespace uhd::transport;
namespace asio = boost::asio;
static const int underflow_flags = async_metadata_t::EVENT_CODE_UNDERFLOW | async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET;
+static const double RECV_TIMEOUT_MS = 100;
/***********************************************************************
* io impl details (internal to this file)
@@ -90,7 +91,7 @@ void usrp2_impl::io_impl::recv_pirate_loop(
size_t next_packet_seq = 0;
while(recv_pirate_crew_raiding){
- managed_recv_buffer::sptr buff = zc_if->get_recv_buff();
+ managed_recv_buffer::sptr buff = zc_if->get_recv_buff(RECV_TIMEOUT_MS);
if (not buff.get()) continue; //ignore timeout/error buffers
try{
@@ -150,7 +151,7 @@ void usrp2_impl::io_init(void){
std::memcpy(send_buff->cast<void*>(), &data, sizeof(data));
send_buff->commit(sizeof(data));
//drain the recv buffers (may have junk)
- while (data_transport->get_recv_buff().get());
+ while (data_transport->get_recv_buff(RECV_TIMEOUT_MS).get()){};
}
//the number of recv frames is the number for the first transport