diff options
Diffstat (limited to 'host/lib/transport')
-rw-r--r-- | host/lib/transport/CMakeLists.txt | 24 | ||||
-rw-r--r-- | host/lib/transport/FindUSB1.cmake | 38 | ||||
-rw-r--r-- | host/lib/transport/convert_types_impl.hpp | 122 | ||||
-rwxr-xr-x | host/lib/transport/gen_convert_types.py | 107 | ||||
-rw-r--r-- | host/lib/transport/libusb1_base.cpp | 126 | ||||
-rw-r--r-- | host/lib/transport/libusb1_base.hpp | 94 | ||||
-rw-r--r-- | host/lib/transport/libusb1_control.cpp | 95 | ||||
-rw-r--r-- | host/lib/transport/libusb1_device_handle.cpp | 117 | ||||
-rw-r--r-- | host/lib/transport/libusb1_zero_copy.cpp | 767 |
9 files changed, 1456 insertions, 34 deletions
diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index bde2b72b9..71a3a1494 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -18,6 +18,30 @@ #This file will be included by cmake, use absolute paths! ######################################################################## +# Setup libusb +######################################################################## +LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/lib/transport) +FIND_PACKAGE(USB1 REQUIRED) + +IF(LIBUSB_FOUND) + INCLUDE_DIRECTORIES(${LIBUSB_INCLUDE_DIR}) + LIBUHD_APPEND_LIBS(${LIBUSB_LIBRARIES}) + LIBUHD_APPEND_SOURCES( + ${CMAKE_SOURCE_DIR}/lib/transport/libusb1_control.cpp + ${CMAKE_SOURCE_DIR}/lib/transport/libusb1_zero_copy.cpp + ${CMAKE_SOURCE_DIR}/lib/transport/libusb1_base.cpp + ${CMAKE_SOURCE_DIR}/lib/transport/libusb1_device_handle.cpp + ) + SET(HAVE_USB_SUPPORT TRUE) +ENDIF(LIBUSB_FOUND) + +IF(HAVE_USB_SUPPORT) + MESSAGE(STATUS "Has USB support - found") +ELSE(HAVE_USB_SUPPORT) + MESSAGE(STATUS "Has USB support - not found") +ENDIF(HAVE_USB_SUPPORT) + +######################################################################## # Check for SIMD headers ######################################################################## INCLUDE(CheckIncludeFileCXX) diff --git a/host/lib/transport/FindUSB1.cmake b/host/lib/transport/FindUSB1.cmake new file mode 100644 index 000000000..ebcac99eb --- /dev/null +++ b/host/lib/transport/FindUSB1.cmake @@ -0,0 +1,38 @@ +# - Try to find the freetype library +# Once done this defines +# +# LIBUSB_FOUND - system has libusb +# LIBUSB_INCLUDE_DIR - the libusb include directory +# LIBUSB_LIBRARIES - Link these to use libusb + +# Copyright (c) 2006, 2008 Laurent Montel, <montel@kde.org> +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + + +if (LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) + + # in cache already + set(LIBUSB_FOUND TRUE) + +else (LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) + IF (NOT WIN32) + # use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + find_package(PkgConfig) + pkg_check_modules(PC_LIBUSB libusb-1.0) + ENDIF(NOT WIN32) + + FIND_PATH(LIBUSB_INCLUDE_DIR libusb.h + PATHS ${PC_LIBUSB_INCLUDEDIR} ${PC_LIBUSB_INCLUDE_DIRS}) + + FIND_LIBRARY(LIBUSB_LIBRARIES NAMES usb-1.0 + PATHS ${PC_LIBUSB_LIBDIR} ${PC_LIBUSB_LIBRARY_DIRS}) + + include(FindPackageHandleStandardArgs) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBUSB DEFAULT_MSG LIBUSB_LIBRARIES LIBUSB_INCLUDE_DIR) + + MARK_AS_ADVANCED(LIBUSB_INCLUDE_DIR LIBUSB_LIBRARIES) + +endif (LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) diff --git a/host/lib/transport/convert_types_impl.hpp b/host/lib/transport/convert_types_impl.hpp index 5958b08cb..90618dec6 100644 --- a/host/lib/transport/convert_types_impl.hpp +++ b/host/lib/transport/convert_types_impl.hpp @@ -28,6 +28,10 @@ #define USE_EMMINTRIN_H //use sse2 intrinsics #endif +#if defined(USE_EMMINTRIN_H) + #include <emmintrin.h> +#endif + /*********************************************************************** * Typedefs **********************************************************************/ @@ -38,41 +42,56 @@ typedef boost::uint32_t item32_t; /*********************************************************************** * Convert complex short buffer to items32 **********************************************************************/ +static UHD_INLINE item32_t sc16_to_item32(sc16_t num){ + boost::uint16_t real = num.real(); + boost::uint16_t imag = num.imag(); + return (item32_t(real) << 16) | (item32_t(imag) << 0); +} + static UHD_INLINE void sc16_to_item32_nswap( const sc16_t *input, item32_t *output, size_t nsamps ){ - std::memcpy(output, input, nsamps*sizeof(item32_t)); + for (size_t i = 0; i < nsamps; i++){ + output[i] = sc16_to_item32(input[i]); + } } static UHD_INLINE void sc16_to_item32_bswap( const sc16_t *input, item32_t *output, size_t nsamps ){ - const item32_t *item32_input = (const item32_t *)input; for (size_t i = 0; i < nsamps; i++){ - output[i] = uhd::byteswap(item32_input[i]); + output[i] = uhd::byteswap(sc16_to_item32(input[i])); } } /*********************************************************************** * Convert items32 buffer to complex short **********************************************************************/ +static UHD_INLINE sc16_t item32_to_sc16(item32_t item){ + return sc16_t( + boost::int16_t(item >> 16), + boost::int16_t(item >> 0) + ); +} + static UHD_INLINE void item32_to_sc16_nswap( const item32_t *input, sc16_t *output, size_t nsamps ){ - std::memcpy(output, input, nsamps*sizeof(item32_t)); + for (size_t i = 0; i < nsamps; i++){ + output[i] = item32_to_sc16(input[i]); + } } static UHD_INLINE void item32_to_sc16_bswap( const item32_t *input, sc16_t *output, size_t nsamps ){ - item32_t *item32_output = (item32_t *)output; for (size_t i = 0; i < nsamps; i++){ - item32_output[i] = uhd::byteswap(input[i]); + output[i] = item32_to_sc16(uhd::byteswap(input[i])); } } /*********************************************************************** - * Convert complex float buffer to items32 + * Convert complex float buffer to items32 (no swap) **********************************************************************/ static const float shorts_per_float = float(32767); @@ -82,6 +101,41 @@ static UHD_INLINE item32_t fc32_to_item32(fc32_t num){ return (item32_t(real) << 16) | (item32_t(imag) << 0); } +//////////////////////////////////// +// none-swap +//////////////////////////////////// +#if defined(USE_EMMINTRIN_H) +static UHD_INLINE void fc32_to_item32_nswap( + const fc32_t *input, item32_t *output, size_t nsamps +){ + __m128 scalar = _mm_set_ps1(shorts_per_float); + + //convert blocks of samples with intrinsics + size_t i = 0; for (; i < (nsamps & ~0x3); i+=4){ + //load from input + __m128 tmplo = _mm_loadu_ps(reinterpret_cast<const float *>(input+i+0)); + __m128 tmphi = _mm_loadu_ps(reinterpret_cast<const float *>(input+i+2)); + + //convert and scale + __m128i tmpilo = _mm_cvtps_epi32(_mm_mul_ps(tmplo, scalar)); + __m128i tmpihi = _mm_cvtps_epi32(_mm_mul_ps(tmphi, scalar)); + + //pack + swap 16-bit pairs + __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); + tmpi = _mm_shufflelo_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); + tmpi = _mm_shufflehi_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); + + //store to output + _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); + } + + //convert remainder + for (; i < nsamps; i++){ + output[i] = fc32_to_item32(input[i]); + } +} + +#else static UHD_INLINE void fc32_to_item32_nswap( const fc32_t *input, item32_t *output, size_t nsamps ){ @@ -90,9 +144,12 @@ static UHD_INLINE void fc32_to_item32_nswap( } } -#if defined(USE_EMMINTRIN_H) -#include <emmintrin.h> +#endif +//////////////////////////////////// +// byte-swap +//////////////////////////////////// +#if defined(USE_EMMINTRIN_H) static UHD_INLINE void fc32_to_item32_bswap( const fc32_t *input, item32_t *output, size_t nsamps ){ @@ -108,7 +165,7 @@ static UHD_INLINE void fc32_to_item32_bswap( __m128i tmpilo = _mm_cvtps_epi32(_mm_mul_ps(tmplo, scalar)); __m128i tmpihi = _mm_cvtps_epi32(_mm_mul_ps(tmphi, scalar)); - //pack + byteswap -> byteswap 32 bit words + //pack + byteswap -> byteswap 16 bit words __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); @@ -145,6 +202,43 @@ static UHD_INLINE fc32_t item32_to_fc32(item32_t item){ ); } +//////////////////////////////////// +// none-swap +//////////////////////////////////// +#if defined(USE_EMMINTRIN_H) +static UHD_INLINE void item32_to_fc32_nswap( + const item32_t *input, fc32_t *output, size_t nsamps +){ + __m128 scalar = _mm_set_ps1(floats_per_short/(1 << 16)); + __m128i zeroi = _mm_setzero_si128(); + + //convert blocks of samples with intrinsics + size_t i = 0; for (; i < (nsamps & ~0x3); i+=4){ + //load from input + __m128i tmpi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(input+i)); + + //unpack + swap 16-bit pairs + tmpi = _mm_shufflelo_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); + tmpi = _mm_shufflehi_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); + __m128i tmpilo = _mm_unpacklo_epi16(zeroi, tmpi); //value in upper 16 bits + __m128i tmpihi = _mm_unpackhi_epi16(zeroi, tmpi); + + //convert and scale + __m128 tmplo = _mm_mul_ps(_mm_cvtepi32_ps(tmpilo), scalar); + __m128 tmphi = _mm_mul_ps(_mm_cvtepi32_ps(tmpihi), scalar); + + //store to output + _mm_storeu_ps(reinterpret_cast<float *>(output+i+0), tmplo); + _mm_storeu_ps(reinterpret_cast<float *>(output+i+2), tmphi); + } + + //convert remainder + for (; i < nsamps; i++){ + output[i] = item32_to_fc32(input[i]); + } +} + +#else static UHD_INLINE void item32_to_fc32_nswap( const item32_t *input, fc32_t *output, size_t nsamps ){ @@ -152,10 +246,12 @@ static UHD_INLINE void item32_to_fc32_nswap( output[i] = item32_to_fc32(input[i]); } } +#endif +//////////////////////////////////// +// byte-swap +//////////////////////////////////// #if defined(USE_EMMINTRIN_H) -#include <emmintrin.h> - static UHD_INLINE void item32_to_fc32_bswap( const item32_t *input, fc32_t *output, size_t nsamps ){ @@ -167,7 +263,7 @@ static UHD_INLINE void item32_to_fc32_bswap( //load from input __m128i tmpi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(input+i)); - //byteswap + unpack -> byteswap 32 bit words + //byteswap + unpack -> byteswap 16 bit words tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); __m128i tmpilo = _mm_unpacklo_epi16(zeroi, tmpi); //value in upper 16 bits __m128i tmpihi = _mm_unpackhi_epi16(zeroi, tmpi); diff --git a/host/lib/transport/gen_convert_types.py b/host/lib/transport/gen_convert_types.py index 951b634d9..adbd22868 100755 --- a/host/lib/transport/gen_convert_types.py +++ b/host/lib/transport/gen_convert_types.py @@ -36,7 +36,8 @@ using namespace uhd; **********************************************************************/ UHD_INLINE boost::uint8_t get_pred( const io_type_t &io_type, - const otw_type_t &otw_type + const otw_type_t &otw_type, + size_t num_chans ){ boost::uint8_t pred = 0; @@ -63,6 +64,14 @@ UHD_INLINE boost::uint8_t get_pred( default: throw std::runtime_error("unhandled io type id"); } + switch(num_chans){ + case 1: pred |= $ph.chan1_p; break; + case 2: pred |= $ph.chan2_p; break; + case 3: pred |= $ph.chan3_p; break; + case 4: pred |= $ph.chan4_p; break; + default: throw std::runtime_error("unhandled number of channels"); + } + return pred; } @@ -70,17 +79,37 @@ UHD_INLINE boost::uint8_t get_pred( * Convert host type to device type **********************************************************************/ void transport::convert_io_type_to_otw_type( - const void *io_buff, const io_type_t &io_type, - void *otw_buff, const otw_type_t &otw_type, - size_t num_samps + const std::vector<const void *> &io_buffs, + const io_type_t &io_type, + void *otw_buff, + const otw_type_t &otw_type, + size_t nsamps_per_io_buff ){ - switch(get_pred(io_type, otw_type)){ + switch(get_pred(io_type, otw_type, io_buffs.size())){ #for $pred in range(2**$ph.nbits) case $pred: #set $out_type = $ph.get_dev_type($pred) #set $in_type = $ph.get_host_type($pred) - #set $converter = '_'.join([$in_type, 'to', $out_type, $ph.get_swap_type($pred)]) - $(converter)((const $(in_type)_t *)io_buff, ($(out_type)_t *)otw_buff, num_samps); + #set $num_chans = $ph.get_num_chans($pred) + #set $converter = '_'.join([$in_type, 'to', $out_type]) + #if $num_chans == 1 + $(converter)_$ph.get_swap_type($pred)( + reinterpret_cast<const $(in_type)_t *>(io_buffs.front()), + reinterpret_cast<$(out_type)_t *>(otw_buff), + nsamps_per_io_buff + ); + #else + for (size_t i = 0; i < nsamps_per_io_buff; i++){ + #for $j in range($num_chans) + reinterpret_cast<$(out_type)_t *>(otw_buff)[i*$num_chans + $j] = + #if $ph.get_swap_type($pred) == 'bswap' + uhd::byteswap($(converter)(reinterpret_cast<const $(in_type)_t *>(io_buffs[$j])[i])); + #else + $(converter)(reinterpret_cast<const $(in_type)_t *>(io_buffs[$j])[i]); + #end if + #end for + } + #end if break; #end for } @@ -90,17 +119,37 @@ void transport::convert_io_type_to_otw_type( * Convert device type to host type **********************************************************************/ void transport::convert_otw_type_to_io_type( - const void *otw_buff, const otw_type_t &otw_type, - void *io_buff, const io_type_t &io_type, - size_t num_samps + const void *otw_buff, + const otw_type_t &otw_type, + std::vector<void *> &io_buffs, + const io_type_t &io_type, + size_t nsamps_per_io_buff ){ - switch(get_pred(io_type, otw_type)){ - #for $pred in range(4) + switch(get_pred(io_type, otw_type, io_buffs.size())){ + #for $pred in range(2**$ph.nbits) case $pred: #set $out_type = $ph.get_host_type($pred) #set $in_type = $ph.get_dev_type($pred) - #set $converter = '_'.join([$in_type, 'to', $out_type, $ph.get_swap_type($pred)]) - $(converter)((const $(in_type)_t *)otw_buff, ($(out_type)_t *)io_buff, num_samps); + #set $num_chans = $ph.get_num_chans($pred) + #set $converter = '_'.join([$in_type, 'to', $out_type]) + #if $num_chans == 1 + $(converter)_$ph.get_swap_type($pred)( + reinterpret_cast<const $(in_type)_t *>(otw_buff), + reinterpret_cast<$(out_type)_t *>(io_buffs.front()), + nsamps_per_io_buff + ); + #else + for (size_t i = 0; i < nsamps_per_io_buff; i++){ + #for $j in range($num_chans) + reinterpret_cast<$(out_type)_t *>(io_buffs[$j])[i] = + #if $ph.get_swap_type($pred) == 'bswap' + $(converter)(uhd::byteswap(reinterpret_cast<const $(in_type)_t *>(otw_buff)[i*$num_chans + $j])); + #else + $(converter)(reinterpret_cast<const $(in_type)_t *>(otw_buff)[i*$num_chans + $j]); + #end if + #end for + } + #end if break; #end for } @@ -118,27 +167,43 @@ class ph: item32_p = 0b00000 sc16_p = 0b00010 fc32_p = 0b00000 + chan1_p = 0b00000 + chan2_p = 0b00100 + chan3_p = 0b01000 + chan4_p = 0b01100 - nbits = 2 #see above + nbits = 4 #see above @staticmethod - def has(pred, flag): return (pred & flag) == flag + def has(pred, mask, flag): return (pred & mask) == flag @staticmethod def get_swap_type(pred): - if ph.has(pred, ph.bswap_p): return 'bswap' - if ph.has(pred, ph.nswap_p): return 'nswap' + mask = 0b1 + if ph.has(pred, mask, ph.bswap_p): return 'bswap' + if ph.has(pred, mask, ph.nswap_p): return 'nswap' raise NotImplementedError @staticmethod def get_dev_type(pred): - if ph.has(pred, ph.item32_p): return 'item32' + mask = 0b0 + if ph.has(pred, mask, ph.item32_p): return 'item32' raise NotImplementedError @staticmethod def get_host_type(pred): - if ph.has(pred, ph.sc16_p): return 'sc16' - if ph.has(pred, ph.fc32_p): return 'fc32' + mask = 0b10 + if ph.has(pred, mask, ph.sc16_p): return 'sc16' + if ph.has(pred, mask, ph.fc32_p): return 'fc32' + raise NotImplementedError + + @staticmethod + def get_num_chans(pred): + mask = 0b1100 + if ph.has(pred, mask, ph.chan1_p): return 1 + if ph.has(pred, mask, ph.chan2_p): return 2 + if ph.has(pred, mask, ph.chan3_p): return 3 + if ph.has(pred, mask, ph.chan4_p): return 4 raise NotImplementedError if __name__ == '__main__': diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp new file mode 100644 index 000000000..e21c39aa3 --- /dev/null +++ b/host/lib/transport/libusb1_base.cpp @@ -0,0 +1,126 @@ +// +// 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; + +/********************************************************** + * Helper Methods + **********************************************************/ + +/********************************************************** + * libusb namespace + **********************************************************/ +void libusb::init(libusb_context **ctx, int debug_level) +{ + if (libusb_init(ctx) < 0) + std::cerr << "error: libusb_init" << std::endl; + + libusb_set_debug(*ctx, debug_level); +} + +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; + } + + libusb_unref_device(dev); + } + + return dev_handle; +} + +//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::uint8_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; +} + + +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; + } + else { + return true; + } +} + + +std::string libusb::get_serial(libusb_device *dev) +{ + unsigned char buff[32]; + + libusb_device_descriptor desc; + if (libusb_get_device_descriptor(dev, &desc) < 0) + return ""; + + if (desc.iSerialNumber == 0) + return ""; + + //open the device because we have to + libusb_device_handle *dev_handle; + if (libusb_open(dev, &dev_handle) < 0) + return ""; + + if (libusb_get_string_descriptor_ascii(dev_handle, desc.iSerialNumber, + buff, sizeof(buff)) < 0) { + return ""; + } + + libusb_close(dev_handle); + + return (char*) buff; +} diff --git a/host/lib/transport/libusb1_base.hpp b/host/lib/transport/libusb1_base.hpp new file mode 100644 index 000000000..abe5e22a2 --- /dev/null +++ b/host/lib/transport/libusb1_base.hpp @@ -0,0 +1,94 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_TRANSPORT_LIBUSB_HPP +#define INCLUDED_TRANSPORT_LIBUSB_HPP + +#include <uhd/config.hpp> +#include <uhd/transport/usb_device_handle.hpp> +#include <libusb-1.0/libusb.h> + +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 + */ + 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 + */ + 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 + */ + 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 + */ + 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 + */ + std::string get_serial(libusb_device *dev); +} + +}} //namespace + +#endif /* INCLUDED_TRANSPORT_LIBUSB_HPP */ diff --git a/host/lib/transport/libusb1_control.cpp b/host/lib/transport/libusb1_control.cpp new file mode 100644 index 000000000..3531128b2 --- /dev/null +++ b/host/lib/transport/libusb1_control.cpp @@ -0,0 +1,95 @@ +// +// 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/transport/usb_control.hpp> + +using namespace uhd::transport; + +const int libusb_debug_level = 0; +const int libusb_timeout = 0; + +/*********************************************************************** + * libusb-1.0 implementation of USB control transport + **********************************************************************/ +class libusb_control_impl : public usb_control { +public: + libusb_control_impl(usb_device_handle::sptr handle); + ~libusb_control_impl(); + + size_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); + +private: + libusb_context *_ctx; + libusb_device_handle *_dev_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)); +} diff --git a/host/lib/transport/libusb1_device_handle.cpp b/host/lib/transport/libusb1_device_handle.cpp new file mode 100644 index 000000000..43d0f0e26 --- /dev/null +++ b/host/lib/transport/libusb1_device_handle.cpp @@ -0,0 +1,117 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "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::uint32_t product_id, + boost::uint32_t vendor_id, + boost::uint32_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::uint32_t _product_id; + boost::uint32_t _vendor_id; + boost::uint32_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::uint32_t product_id = desc.idProduct; + boost::uint32_t vendor_id = desc.idVendor; + boost::uint32_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 new file mode 100644 index 000000000..b890a87f9 --- /dev/null +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -0,0 +1,767 @@ +// +// 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/transport/usb_zero_copy.hpp> +#include <uhd/utils/assert.hpp> +#include <boost/asio.hpp> +#include <boost/format.hpp> +#include <iostream> +#include <iomanip> + +using namespace uhd::transport; + +const int libusb_debug_level = 0; +const int libusb_timeout = 0; + +/*********************************************************************** + * Helper functions + ***********************************************************************/ +/* + * Print the values of a libusb_transfer struct + * http://libusb.sourceforge.net/api-1.0/structlibusb__transfer.html + */ +void pp_transfer(libusb_transfer *lut) +{ + std::cout << "Libusb transfer" << std::endl; + std::cout << " flags: 0x" << std::hex << (unsigned int) lut->flags << std::endl; + std::cout << " endpoint: 0x" << std::hex << (unsigned int) lut->endpoint << std::endl; + std::cout << " type: 0x" << std::hex << (unsigned int) lut->type << std::endl; + std::cout << " timeout: " << std::dec << lut->timeout << std::endl; + std::cout << " status: 0x" << std::hex << lut->status << std::endl; + std::cout << " length: " << std::dec << lut->length << std::endl; + std::cout << " actual_length: " << std::dec << lut->actual_length << std::endl; +} + +/*********************************************************************** + * USB asynchronous zero_copy endpoint + * This endpoint implementation provides asynchronous I/O to libusb-1.0 + * devices. Each endpoint is directional and two can be combined to + * 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. + **********************************************************************/ +class usb_endpoint { +private: + libusb_device_handle *_dev_handle; + libusb_context *_ctx; + 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); + + ~usb_endpoint(); + + // Exposed interface for submitting / retrieving transfer buffers + bool submit(libusb_transfer *lut); + libusb_transfer *get_completed_transfer(); + libusb_transfer *get_free_transfer(); + + //Callback use only + void callback_handle_transfer(libusb_transfer *lut); +}; + + +/* + * Callback function called when submitted transfers complete. + * The endpoint upon which the transfer is part of is recovered + * and the transfer moved from pending to completed state. + * Callbacks occur during the reaping calls where libusb_handle_events() + * is used. The callback only modifies the transfer state by moving + * 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; + endpoint->callback_handle_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); +} + + +/* + * 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. + */ +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) +{ + unsigned int i; + for (i = 0; i < _num_transfers; i++) { + free_list_add(allocate_transfer(_transfer_size)); + + if (_input) + submit(free_list_get()); + } +} + + +/* + * Destructor + * Make sure all the memory is freed. Cancel any pending transfers. + * When all completed transfers are moved to the free list, release + * 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; + } + + while (!_completed_list.empty()) { + if (!reap_completed_list()) + std::cerr << "error: destructor failed to reap" << std::endl; + } + + while (!_free_list.empty()) { + libusb_free_transfer(free_list_get()); + } +} + + +/* + * 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 *lut = libusb_alloc_transfer(0); + + unsigned char *buff = new unsigned char[buff_len]; + + unsigned int endpoint = ((_endpoint & 0x7f) | (_input ? 0x80 : 0)); + + libusb_fill_bulk_transfer(lut, // transfer + _dev_handle, // dev_handle + endpoint, // endpoint + buff, // buffer + buff_len, // length + callback, // callback + this, // user_data + 0); // timeout + return lut; +} + + +/* + * 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. + */ +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; +} + + +/* + * Print status errors of a completed transfer + * \param lut pointer to an libusb_transfer + */ +void usb_endpoint::print_transfer_status(libusb_transfer *lut) +{ + switch (lut->status) { + case LIBUSB_TRANSFER_COMPLETED: + if (lut->actual_length < lut->length) { + std::cerr << "USB: transfer completed with short write," + << " length = " << lut->length + << " actual = " << lut->actual_length << std::endl; + } + + if ((lut->actual_length < 0) || (lut->length < 0)) { + std::cerr << "USB: transfer completed with invalid response" + << std::endl; + } + break; + case LIBUSB_TRANSFER_CANCELLED: + break; + case LIBUSB_TRANSFER_NO_DEVICE: + std::cerr << "USB: device was disconnected" << std::endl; + break; + case LIBUSB_TRANSFER_OVERFLOW: + std::cerr << "USB: device sent more data than requested" << std::endl; + break; + case LIBUSB_TRANSFER_TIMED_OUT: + std::cerr << "USB: transfer timed out" << std::endl; + break; + case LIBUSB_TRANSFER_STALL: + std::cerr << "USB: halt condition detected (stalled)" << std::endl; + break; + case LIBUSB_TRANSFER_ERROR: + std::cerr << "USB: transfer failed" << std::endl; + break; + default: + std::cerr << "USB: received unknown transfer status" << std::endl; + } +} + + +/* + * 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 *lut; + + if (_free_list.size() == 0) { + return NULL; + } + else { + lut = _free_list.front(); + _free_list.pop_front(); + return lut; + } +} + + +/* + * 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 + **********************************************************************/ +/* + * 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. + */ +class libusb_managed_recv_buffer_impl : public managed_recv_buffer { +public: + libusb_managed_recv_buffer_impl(libusb_transfer *lut, + usb_endpoint *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; + } + +private: + const boost::asio::const_buffer &get() const + { + return _buff; + } + + libusb_transfer *_lut; + usb_endpoint *_endpoint; + const boost::asio::const_buffer _buff; +}; + +/* + * Libusb managed send buffer + * Construct a send buffer from a libusb transfer. The memory held by + * the libusb transfer is exposed through the managed buffer interface. + * Committing the buffer will set the data length and submit the buffer + * to the endpoint. Submitting a buffer multiple times or destroying + * the buffer before committing is an error. For the latter, the transfer + * is returned to the endpoint with no data for reuse. + */ +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) + { + _lut = lut; + _endpoint = endpoint; + } + + ~libusb_managed_send_buffer_impl() + { + if (!_committed) { + _lut->length = 0; + _lut->actual_length = 0; + _endpoint->submit(_lut); + } + } + + ssize_t commit(size_t num_bytes) + { + if (_committed) { + 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)) { + _committed = true; + return num_bytes; + } + else { + return 0; + } + } + +private: + const boost::asio::mutable_buffer &get() const + { + return _buff; + } + + libusb_transfer *_lut; + usb_endpoint *_endpoint; + const boost::asio::mutable_buffer _buff; + bool _committed; +}; + + +/*********************************************************************** + * USB zero_copy device class + **********************************************************************/ +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; + +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(); + + managed_recv_buffer::sptr get_recv_buff(void); + 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; } +}; + +/* + * Constructor + * Initializes libusb, opens devices, and sets up interfaces for 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 + true, // IN endpoint + _recv_buff_size, // buffer size per transfer + _num_frames); // number of libusb transfers + + _tx_ep = new usb_endpoint(_tx_dev_handle, // libusb device_handle + _tx_ctx, // libusb context + tx_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); +} + + +/* + * 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 + */ +managed_recv_buffer::sptr libusb_zero_copy_impl::get_recv_buff() +{ + libusb_transfer *lut = _rx_ep->get_completed_transfer(); + if (lut == NULL) { + return managed_recv_buffer::sptr(); + } + else { + return managed_recv_buffer::sptr( + new libusb_managed_recv_buffer_impl(lut, + _rx_ep)); + } +} + + +/* + * 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 + */ +managed_send_buffer::sptr libusb_zero_copy_impl::get_send_buff() +{ + libusb_transfer *lut = _tx_ep->get_free_transfer(); + 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)); + } +} + + +/*********************************************************************** + * 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)); +} + + + |