aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/transport
diff options
context:
space:
mode:
authorPhilip Balister <philip@opensdr.com>2010-09-09 14:08:20 -0400
committerPhilip Balister <philip@opensdr.com>2010-09-09 14:08:20 -0400
commit262109bc461139448c816f92644a9f748798c4fc (patch)
treeb51367352945b08566ff8501d7ef4b91eb86e4d4 /host/lib/transport
parentd2d5be27b09faee1481a763ce25e7b95460a46c9 (diff)
parent5058145dcc5cb681887773b8245cf89df58399f9 (diff)
downloaduhd-262109bc461139448c816f92644a9f748798c4fc.tar.gz
uhd-262109bc461139448c816f92644a9f748798c4fc.tar.bz2
uhd-262109bc461139448c816f92644a9f748798c4fc.zip
Merge branch 'master' of ettus.sourcerepo.com:ettus/uhdpriv into usrp_e
Diffstat (limited to 'host/lib/transport')
-rw-r--r--host/lib/transport/CMakeLists.txt18
-rw-r--r--host/lib/transport/FindUSB1.cmake38
-rw-r--r--host/lib/transport/convert_types_impl.hpp122
-rwxr-xr-xhost/lib/transport/gen_convert_types.py107
-rw-r--r--host/lib/transport/libusb1_base.cpp126
-rw-r--r--host/lib/transport/libusb1_base.hpp94
-rw-r--r--host/lib/transport/libusb1_control.cpp95
-rw-r--r--host/lib/transport/libusb1_device_handle.cpp117
-rw-r--r--host/lib/transport/libusb1_zero_copy.cpp767
9 files changed, 1450 insertions, 34 deletions
diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt
index bde2b72b9..753fd5e85 100644
--- a/host/lib/transport/CMakeLists.txt
+++ b/host/lib/transport/CMakeLists.txt
@@ -18,6 +18,24 @@
#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)
+
+########################################################################
# 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));
+}
+
+
+