root / host / lib / transport / libusb1_zero_copy.cpp @ 253be8f0
History | View | Annotate | Download (11 KB)
| 1 |
//
|
|---|---|
| 2 |
// Copyright 2010-2011 Ettus Research LLC
|
| 3 |
//
|
| 4 |
// This program is free software: you can redistribute it and/or modify
|
| 5 |
// it under the terms of the GNU General Public License as published by
|
| 6 |
// the Free Software Foundation, either version 3 of the License, or
|
| 7 |
// (at your option) any later version.
|
| 8 |
//
|
| 9 |
// This program is distributed in the hope that it will be useful,
|
| 10 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 11 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 12 |
// GNU General Public License for more details.
|
| 13 |
//
|
| 14 |
// You should have received a copy of the GNU General Public License
|
| 15 |
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
| 16 |
//
|
| 17 |
|
| 18 |
#include "libusb1_base.hpp" |
| 19 |
#include <uhd/transport/usb_zero_copy.hpp> |
| 20 |
#include <uhd/transport/buffer_pool.hpp> |
| 21 |
#include <uhd/utils/msg.hpp> |
| 22 |
#include <uhd/exception.hpp> |
| 23 |
#include <boost/foreach.hpp> |
| 24 |
#include <boost/make_shared.hpp> |
| 25 |
#include <boost/thread/thread.hpp> |
| 26 |
#include <list> |
| 27 |
|
| 28 |
using namespace uhd; |
| 29 |
using namespace uhd::transport; |
| 30 |
|
| 31 |
static const size_t DEFAULT_NUM_XFERS = 16; //num xfers |
| 32 |
static const size_t DEFAULT_XFER_SIZE = 32*512; //bytes |
| 33 |
|
| 34 |
//! Define LIBUSB_CALL when its missing (non-windows)
|
| 35 |
#ifndef LIBUSB_CALL
|
| 36 |
#define LIBUSB_CALL
|
| 37 |
#endif /*LIBUSB_CALL*/ |
| 38 |
|
| 39 |
/*!
|
| 40 |
* All libusb callback functions should be marked with the LIBUSB_CALL macro
|
| 41 |
* to ensure that they are compiled with the same calling convention as libusb.
|
| 42 |
*/
|
| 43 |
|
| 44 |
//! helper function: handles all async callbacks
|
| 45 |
static void LIBUSB_CALL libusb_async_cb(libusb_transfer *lut){ |
| 46 |
*(static_cast<bool *>(lut->user_data)) = true; |
| 47 |
} |
| 48 |
|
| 49 |
/*!
|
| 50 |
* Wait for a managed buffer to become complete.
|
| 51 |
*
|
| 52 |
* This routine processes async events until the transaction completes.
|
| 53 |
* We must call the libusb handle events in a loop because the handler
|
| 54 |
* may complete managed buffers other than the one we are waiting on.
|
| 55 |
*
|
| 56 |
* We cannot determine if handle events timed out or processed an event.
|
| 57 |
* Therefore, the timeout condition is handled by using boost system time.
|
| 58 |
*
|
| 59 |
* \param ctx the libusb context structure
|
| 60 |
* \param timeout the wait timeout in seconds
|
| 61 |
* \param completed a reference to the completed flag
|
| 62 |
* \return true for completion, false for timeout
|
| 63 |
*/
|
| 64 |
UHD_INLINE bool wait_for_completion(libusb_context *ctx, const double timeout, bool &completed){ |
| 65 |
//already completed by a previous call?
|
| 66 |
if (completed) return true; |
| 67 |
|
| 68 |
//perform a non-blocking event handle
|
| 69 |
timeval tv; |
| 70 |
tv.tv_sec = 0;
|
| 71 |
tv.tv_usec = 0;
|
| 72 |
libusb_handle_events_timeout(ctx, &tv); |
| 73 |
if (completed) return true; |
| 74 |
|
| 75 |
//finish the rest with a timeout loop
|
| 76 |
const boost::system_time timeout_time = boost::get_system_time() + boost::posix_time::microseconds(long(timeout*1000000)); |
| 77 |
while (not completed and (boost::get_system_time() < timeout_time)){ |
| 78 |
timeval tv; |
| 79 |
tv.tv_sec = 0;
|
| 80 |
tv.tv_usec = 10000; /*10ms*/ |
| 81 |
libusb_handle_events_timeout(ctx, &tv); |
| 82 |
} |
| 83 |
|
| 84 |
return completed;
|
| 85 |
} |
| 86 |
|
| 87 |
/***********************************************************************
|
| 88 |
* Reusable managed receiver buffer:
|
| 89 |
* - Associated with a particular libusb transfer struct.
|
| 90 |
* - Submits the transfer to libusb in the release method.
|
| 91 |
**********************************************************************/
|
| 92 |
class libusb_zero_copy_mrb : public managed_recv_buffer{ |
| 93 |
public:
|
| 94 |
libusb_zero_copy_mrb(libusb_transfer *lut, const size_t frame_size):
|
| 95 |
_ctx(libusb::session::get_global_session()->get_context()), |
| 96 |
_lut(lut), _frame_size(frame_size) { /* NOP */ }
|
| 97 |
|
| 98 |
void release(void){ |
| 99 |
completed = false;
|
| 100 |
_lut->length = _frame_size; //always reset length
|
| 101 |
UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0);
|
| 102 |
} |
| 103 |
|
| 104 |
sptr get_new(const double timeout, size_t &index){ |
| 105 |
if (wait_for_completion(_ctx, timeout, completed)){
|
| 106 |
index++; |
| 107 |
return make(this, _lut->buffer, _lut->actual_length); |
| 108 |
} |
| 109 |
return managed_recv_buffer::sptr();
|
| 110 |
} |
| 111 |
|
| 112 |
bool completed;
|
| 113 |
|
| 114 |
private:
|
| 115 |
libusb_context *_ctx; |
| 116 |
libusb_transfer *_lut; |
| 117 |
const size_t _frame_size;
|
| 118 |
}; |
| 119 |
|
| 120 |
/***********************************************************************
|
| 121 |
* Reusable managed send buffer:
|
| 122 |
* - Associated with a particular libusb transfer struct.
|
| 123 |
* - Submits the transfer to libusb in the commit method.
|
| 124 |
**********************************************************************/
|
| 125 |
class libusb_zero_copy_msb : public managed_send_buffer{ |
| 126 |
public:
|
| 127 |
libusb_zero_copy_msb(libusb_transfer *lut, const size_t frame_size):
|
| 128 |
_ctx(libusb::session::get_global_session()->get_context()), |
| 129 |
_lut(lut), _frame_size(frame_size) { completed = true; }
|
| 130 |
|
| 131 |
void release(void){ |
| 132 |
completed = false;
|
| 133 |
_lut->length = size(); |
| 134 |
UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0);
|
| 135 |
} |
| 136 |
|
| 137 |
sptr get_new(const double timeout, size_t &index){ |
| 138 |
if (wait_for_completion(_ctx, timeout, completed)){
|
| 139 |
index++; |
| 140 |
return make(this, _lut->buffer, _frame_size); |
| 141 |
} |
| 142 |
return managed_send_buffer::sptr();
|
| 143 |
} |
| 144 |
|
| 145 |
bool completed;
|
| 146 |
|
| 147 |
private:
|
| 148 |
libusb_context *_ctx; |
| 149 |
libusb_transfer *_lut; |
| 150 |
const size_t _frame_size;
|
| 151 |
}; |
| 152 |
|
| 153 |
/***********************************************************************
|
| 154 |
* USB zero_copy device class
|
| 155 |
**********************************************************************/
|
| 156 |
class libusb_zero_copy_impl : public usb_zero_copy{ |
| 157 |
public:
|
| 158 |
|
| 159 |
libusb_zero_copy_impl( |
| 160 |
libusb::device_handle::sptr handle, |
| 161 |
const size_t recv_interface,
|
| 162 |
const size_t recv_endpoint,
|
| 163 |
const size_t send_interface,
|
| 164 |
const size_t send_endpoint,
|
| 165 |
const device_addr_t &hints
|
| 166 |
): |
| 167 |
_handle(handle), |
| 168 |
_recv_frame_size(size_t(hints.cast<double>("recv_frame_size", DEFAULT_XFER_SIZE))), |
| 169 |
_num_recv_frames(size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_XFERS))), |
| 170 |
_send_frame_size(size_t(hints.cast<double>("send_frame_size", DEFAULT_XFER_SIZE))), |
| 171 |
_num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_XFERS))), |
| 172 |
_recv_buffer_pool(buffer_pool::make(_num_recv_frames, _recv_frame_size)), |
| 173 |
_send_buffer_pool(buffer_pool::make(_num_send_frames, _send_frame_size)), |
| 174 |
_next_recv_buff_index(0),
|
| 175 |
_next_send_buff_index(0)
|
| 176 |
{
|
| 177 |
_handle->claim_interface(recv_interface); |
| 178 |
_handle->claim_interface(send_interface); |
| 179 |
|
| 180 |
//allocate libusb transfer structs and managed receive buffers
|
| 181 |
for (size_t i = 0; i < get_num_recv_frames(); i++){ |
| 182 |
|
| 183 |
libusb_transfer *lut = libusb_alloc_transfer(0);
|
| 184 |
UHD_ASSERT_THROW(lut != NULL);
|
| 185 |
|
| 186 |
_mrb_pool.push_back(boost::make_shared<libusb_zero_copy_mrb>(lut, this->get_recv_frame_size()));
|
| 187 |
|
| 188 |
libusb_fill_bulk_transfer( |
| 189 |
lut, // transfer
|
| 190 |
_handle->get(), // dev_handle
|
| 191 |
(recv_endpoint & 0x7f) | 0x80, // endpoint |
| 192 |
static_cast<unsigned char *>(_recv_buffer_pool->at(i)), // buffer |
| 193 |
this->get_recv_frame_size(), // length |
| 194 |
libusb_transfer_cb_fn(&libusb_async_cb), // callback
|
| 195 |
static_cast<void *>(&_mrb_pool.back()->completed), // user_data |
| 196 |
0 // timeout (ms) |
| 197 |
); |
| 198 |
|
| 199 |
_all_luts.push_back(lut); |
| 200 |
_mrb_pool.back()->release(); |
| 201 |
} |
| 202 |
|
| 203 |
//allocate libusb transfer structs and managed send buffers
|
| 204 |
for (size_t i = 0; i < get_num_send_frames(); i++){ |
| 205 |
|
| 206 |
libusb_transfer *lut = libusb_alloc_transfer(0);
|
| 207 |
UHD_ASSERT_THROW(lut != NULL);
|
| 208 |
|
| 209 |
_msb_pool.push_back(boost::make_shared<libusb_zero_copy_msb>(lut, this->get_send_frame_size()));
|
| 210 |
|
| 211 |
libusb_fill_bulk_transfer( |
| 212 |
lut, // transfer
|
| 213 |
_handle->get(), // dev_handle
|
| 214 |
(send_endpoint & 0x7f) | 0x00, // endpoint |
| 215 |
static_cast<unsigned char *>(_send_buffer_pool->at(i)), // buffer |
| 216 |
this->get_send_frame_size(), // length |
| 217 |
libusb_transfer_cb_fn(&libusb_async_cb), // callback
|
| 218 |
static_cast<void *>(&_msb_pool.back()->completed), // user_data |
| 219 |
0 // timeout |
| 220 |
); |
| 221 |
|
| 222 |
_all_luts.push_back(lut); |
| 223 |
} |
| 224 |
} |
| 225 |
|
| 226 |
~libusb_zero_copy_impl(void){
|
| 227 |
libusb_context *ctx = libusb::session::get_global_session()->get_context(); |
| 228 |
|
| 229 |
//cancel all transfers
|
| 230 |
BOOST_FOREACH(libusb_transfer *lut, _all_luts){
|
| 231 |
libusb_cancel_transfer(lut); |
| 232 |
} |
| 233 |
|
| 234 |
//process all transfers until timeout occurs
|
| 235 |
bool completed = false; |
| 236 |
wait_for_completion(ctx, 0.01, completed); |
| 237 |
|
| 238 |
//free all transfers
|
| 239 |
BOOST_FOREACH(libusb_transfer *lut, _all_luts){
|
| 240 |
libusb_free_transfer(lut); |
| 241 |
} |
| 242 |
|
| 243 |
} |
| 244 |
|
| 245 |
managed_recv_buffer::sptr get_recv_buff(double timeout){
|
| 246 |
if (_next_recv_buff_index == _num_recv_frames) _next_recv_buff_index = 0; |
| 247 |
return _mrb_pool[_next_recv_buff_index]->get_new(timeout, _next_recv_buff_index);
|
| 248 |
} |
| 249 |
|
| 250 |
managed_send_buffer::sptr get_send_buff(double timeout){
|
| 251 |
if (_next_send_buff_index == _num_send_frames) _next_send_buff_index = 0; |
| 252 |
return _msb_pool[_next_send_buff_index]->get_new(timeout, _next_send_buff_index);
|
| 253 |
} |
| 254 |
|
| 255 |
size_t get_num_recv_frames(void) const { return _num_recv_frames; } |
| 256 |
size_t get_num_send_frames(void) const { return _num_send_frames; } |
| 257 |
|
| 258 |
size_t get_recv_frame_size(void) const { return _recv_frame_size; } |
| 259 |
size_t get_send_frame_size(void) const { return _send_frame_size; } |
| 260 |
|
| 261 |
private:
|
| 262 |
libusb::device_handle::sptr _handle; |
| 263 |
const size_t _recv_frame_size, _num_recv_frames;
|
| 264 |
const size_t _send_frame_size, _num_send_frames;
|
| 265 |
|
| 266 |
//! Storage for transfer related objects
|
| 267 |
buffer_pool::sptr _recv_buffer_pool, _send_buffer_pool; |
| 268 |
std::vector<boost::shared_ptr<libusb_zero_copy_mrb> > _mrb_pool; |
| 269 |
std::vector<boost::shared_ptr<libusb_zero_copy_msb> > _msb_pool; |
| 270 |
size_t _next_recv_buff_index, _next_send_buff_index; |
| 271 |
|
| 272 |
//! a list of all transfer structs we allocated
|
| 273 |
std::list<libusb_transfer *> _all_luts; |
| 274 |
|
| 275 |
|
| 276 |
}; |
| 277 |
|
| 278 |
/***********************************************************************
|
| 279 |
* USB zero_copy make functions
|
| 280 |
**********************************************************************/
|
| 281 |
usb_zero_copy::sptr usb_zero_copy::make( |
| 282 |
usb_device_handle::sptr handle, |
| 283 |
const size_t recv_interface,
|
| 284 |
const size_t recv_endpoint,
|
| 285 |
const size_t send_interface,
|
| 286 |
const size_t send_endpoint,
|
| 287 |
const device_addr_t &hints
|
| 288 |
){
|
| 289 |
libusb::device_handle::sptr dev_handle(libusb::device_handle::get_cached_handle( |
| 290 |
boost::static_pointer_cast<libusb::special_handle>(handle)->get_device() |
| 291 |
)); |
| 292 |
return sptr(new libusb_zero_copy_impl( |
| 293 |
dev_handle, recv_interface, recv_endpoint, send_interface, send_endpoint, hints |
| 294 |
)); |
| 295 |
} |