diff options
Diffstat (limited to 'host')
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 | 
