root / host / lib / transport / libusb1_zero_copy.cpp @ 72fc20bc
History | View | Annotate | Download (19.8 kB)
| 1 |
//
|
|---|---|
| 2 |
// Copyright 2010 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/utils/assert.hpp> |
| 21 |
#include <boost/asio.hpp> |
| 22 |
#include <boost/format.hpp> |
| 23 |
#include <iostream> |
| 24 |
#include <iomanip> |
| 25 |
|
| 26 |
using namespace uhd::transport; |
| 27 |
|
| 28 |
const int libusb_debug_level = 0; |
| 29 |
const int libusb_timeout = 0; |
| 30 |
|
| 31 |
/***********************************************************************
|
| 32 |
* Helper functions
|
| 33 |
*
|
| 34 |
* Print to stdout the values of a libusb_transfer struct
|
| 35 |
***********************************************************************/
|
| 36 |
void pp_transfer(libusb_transfer *lut)
|
| 37 |
{
|
| 38 |
std::cout << "Libusb transfer" << std::endl;
|
| 39 |
std::cout << " flags: 0x" << std::hex << (unsigned int) lut->flags << std::endl; |
| 40 |
std::cout << " endpoint: 0x" << std::hex << (unsigned int) lut->endpoint << std::endl; |
| 41 |
std::cout << " type: 0x" << std::hex << (unsigned int) lut->type << std::endl; |
| 42 |
std::cout << " timeout: " << std::dec << lut->timeout << std::endl;
|
| 43 |
std::cout << " status: 0x" << std::hex << lut->status << std::endl;
|
| 44 |
std::cout << " length: " << std::dec << lut->length << std::endl;
|
| 45 |
std::cout << " actual_length: " << std::dec << lut->actual_length << std::endl;
|
| 46 |
} |
| 47 |
|
| 48 |
/***********************************************************************
|
| 49 |
* USB asynchronous phony zero_copy endpoint
|
| 50 |
* This endpoint implementation provides asynchronous I/O to libusb-1.0
|
| 51 |
* devices. Each endpoint is directional and two can be combined to
|
| 52 |
* create a bidirectional interface. It is a zero copy implementation
|
| 53 |
* with respect to libusb, however, each send and recv requires a copy
|
| 54 |
* operation from kernel to userspace; this is due to the usbfs
|
| 55 |
* interface provided by the kernel.
|
| 56 |
**********************************************************************/
|
| 57 |
class usb_endpoint { |
| 58 |
private:
|
| 59 |
libusb_device_handle *_dev_handle; |
| 60 |
libusb_context *_ctx; |
| 61 |
int _endpoint;
|
| 62 |
bool _input;
|
| 63 |
|
| 64 |
size_t _transfer_size; |
| 65 |
size_t _num_transfers; |
| 66 |
|
| 67 |
/*
|
| 68 |
* Transfer state lists (free, pending, or completed)
|
| 69 |
*/
|
| 70 |
std::list<libusb_transfer *> _free_list; |
| 71 |
std::list<libusb_transfer *> _pending_list; |
| 72 |
std::list<libusb_transfer *> _completed_list; |
| 73 |
|
| 74 |
/*
|
| 75 |
* Calls for processing asynchronous I/O
|
| 76 |
*/
|
| 77 |
libusb_transfer *allocate_transfer(int buff_len);
|
| 78 |
bool cancel(libusb_transfer *lut);
|
| 79 |
bool cancel_all();
|
| 80 |
bool reap_pending_list();
|
| 81 |
bool reap_pending_list_timeout();
|
| 82 |
bool reap_completed_list();
|
| 83 |
|
| 84 |
/*
|
| 85 |
* Transfer state manipulators
|
| 86 |
*/
|
| 87 |
void free_list_add(libusb_transfer *lut);
|
| 88 |
void pending_list_add(libusb_transfer *lut);
|
| 89 |
void completed_list_add(libusb_transfer *lut);
|
| 90 |
libusb_transfer *free_list_get(); |
| 91 |
libusb_transfer *completed_list_get(); |
| 92 |
bool pending_list_remove(libusb_transfer *lut);
|
| 93 |
|
| 94 |
/*
|
| 95 |
* Misc
|
| 96 |
*/
|
| 97 |
void print_transfer_status(libusb_transfer *lut);
|
| 98 |
|
| 99 |
public:
|
| 100 |
usb_endpoint(libusb_device_handle *dev_handle, |
| 101 |
libusb_context *ctx, int endpoint, bool input, |
| 102 |
size_t transfer_size, size_t num_transfers); |
| 103 |
|
| 104 |
~usb_endpoint(); |
| 105 |
|
| 106 |
/*
|
| 107 |
* Accessors
|
| 108 |
*/
|
| 109 |
int get_endpoint() const { return _endpoint; } |
| 110 |
bool get_direction() const { return _input; } |
| 111 |
libusb_device_handle *get_dev_handle() const { return _dev_handle; } |
| 112 |
libusb_context *get_ctx() const { return _ctx; } |
| 113 |
|
| 114 |
/*
|
| 115 |
* Exposed interface for submitting / retrieving transfer buffers
|
| 116 |
* used in zero-copy interface
|
| 117 |
*/
|
| 118 |
bool submit(libusb_transfer *lut);
|
| 119 |
libusb_transfer *get_completed_transfer(); |
| 120 |
libusb_transfer *get_free_transfer(); |
| 121 |
|
| 122 |
/*
|
| 123 |
* Callback use only
|
| 124 |
*/
|
| 125 |
void callback_handle_transfer(libusb_transfer *lut);
|
| 126 |
}; |
| 127 |
|
| 128 |
|
| 129 |
/*
|
| 130 |
* Callback function called when submitted transfers complete.
|
| 131 |
* The endpoint upon which the transfer is part of is recovered
|
| 132 |
* and the transfer moved from pending to completed state.
|
| 133 |
*/
|
| 134 |
static void callback(libusb_transfer *lut) |
| 135 |
{
|
| 136 |
usb_endpoint *endpoint = (usb_endpoint *) lut->user_data; |
| 137 |
endpoint->callback_handle_transfer(lut); |
| 138 |
} |
| 139 |
|
| 140 |
|
| 141 |
/*
|
| 142 |
* Accessor call to allow list access from callback space
|
| 143 |
*/
|
| 144 |
void usb_endpoint::callback_handle_transfer(libusb_transfer *lut)
|
| 145 |
{
|
| 146 |
if (!pending_list_remove(lut)) {
|
| 147 |
std::cerr << "USB: pending remove failed" << std::endl;
|
| 148 |
return;
|
| 149 |
} |
| 150 |
|
| 151 |
completed_list_add(lut); |
| 152 |
} |
| 153 |
|
| 154 |
|
| 155 |
/*
|
| 156 |
* Constructor
|
| 157 |
*
|
| 158 |
* Allocate libusb transfers. For IN endpoints, submit the transfers
|
| 159 |
* so that they're ready to return when data is available.
|
| 160 |
*/
|
| 161 |
usb_endpoint::usb_endpoint(libusb_device_handle *dev_handle, |
| 162 |
libusb_context *ctx, int endpoint, bool input, |
| 163 |
size_t transfer_size, size_t num_transfers) |
| 164 |
: _dev_handle(dev_handle), |
| 165 |
_ctx(ctx), _endpoint(endpoint), _input(input), |
| 166 |
_transfer_size(transfer_size), _num_transfers(num_transfers) |
| 167 |
{
|
| 168 |
unsigned int i; |
| 169 |
for (i = 0; i < _num_transfers; i++) { |
| 170 |
free_list_add(allocate_transfer(_transfer_size)); |
| 171 |
|
| 172 |
if (_input)
|
| 173 |
submit(free_list_get()); |
| 174 |
} |
| 175 |
} |
| 176 |
|
| 177 |
|
| 178 |
/*
|
| 179 |
* Destructor
|
| 180 |
*/
|
| 181 |
usb_endpoint::~usb_endpoint() |
| 182 |
{
|
| 183 |
cancel_all(); |
| 184 |
|
| 185 |
while (!_pending_list.empty()) {
|
| 186 |
if (!reap_pending_list())
|
| 187 |
std::cerr << "error: destructor failed to reap" << std::endl;
|
| 188 |
} |
| 189 |
|
| 190 |
while (!_completed_list.empty()) {
|
| 191 |
if (!reap_completed_list())
|
| 192 |
std::cerr << "error: destructor failed to reap" << std::endl;
|
| 193 |
} |
| 194 |
|
| 195 |
while (!_free_list.empty()) {
|
| 196 |
libusb_free_transfer(free_list_get()); |
| 197 |
} |
| 198 |
} |
| 199 |
|
| 200 |
|
| 201 |
/*
|
| 202 |
* Allocate a libusb transfer
|
| 203 |
*
|
| 204 |
* The allocated transfer is continuously reused and should be freed at
|
| 205 |
* shutdown.
|
| 206 |
*/
|
| 207 |
libusb_transfer *usb_endpoint::allocate_transfer(int buff_len)
|
| 208 |
{
|
| 209 |
libusb_transfer *lut = libusb_alloc_transfer(0);
|
| 210 |
|
| 211 |
unsigned char *buff = new unsigned char[buff_len]; |
| 212 |
|
| 213 |
unsigned int endpoint = ((_endpoint & 0x7f) | (_input ? 0x80 : 0)); |
| 214 |
|
| 215 |
libusb_fill_bulk_transfer(lut, // transfer
|
| 216 |
_dev_handle, // dev_handle
|
| 217 |
endpoint, // endpoint
|
| 218 |
buff, // buffer
|
| 219 |
buff_len, // length
|
| 220 |
callback, // callback
|
| 221 |
this, // user_data |
| 222 |
0); // timeout |
| 223 |
return lut;
|
| 224 |
} |
| 225 |
|
| 226 |
|
| 227 |
/*
|
| 228 |
* Asynchonous transfer submission
|
| 229 |
*
|
| 230 |
* Submit and mark transfer as pending.
|
| 231 |
*/
|
| 232 |
bool usb_endpoint::submit(libusb_transfer *lut)
|
| 233 |
{
|
| 234 |
int retval;
|
| 235 |
if ((retval = libusb_submit_transfer(lut)) < 0) { |
| 236 |
std::cerr << "error: libusb_submit_transfer: " << retval << std::endl;
|
| 237 |
return false; |
| 238 |
} |
| 239 |
|
| 240 |
pending_list_add(lut); |
| 241 |
return true; |
| 242 |
} |
| 243 |
|
| 244 |
|
| 245 |
/*
|
| 246 |
* Cancel a pending transfer
|
| 247 |
*
|
| 248 |
* Search the pending list for the transfer and cancel if found.
|
| 249 |
* Returns true on success. False otherwise or on error.
|
| 250 |
*
|
| 251 |
* Note: success only indicates submission of cancelation request.
|
| 252 |
* Sucessful cancelation is not known until the callback occurs.
|
| 253 |
*/
|
| 254 |
bool usb_endpoint::cancel(libusb_transfer *lut)
|
| 255 |
{
|
| 256 |
std::list<libusb_transfer*>::iterator iter; |
| 257 |
for (iter = _pending_list.begin(); iter != _pending_list.end(); iter++) {
|
| 258 |
if (*iter == lut) {
|
| 259 |
libusb_cancel_transfer(lut); |
| 260 |
return true; |
| 261 |
} |
| 262 |
} |
| 263 |
return false; |
| 264 |
} |
| 265 |
|
| 266 |
|
| 267 |
/*
|
| 268 |
* Cancel all pending transfers
|
| 269 |
*
|
| 270 |
* Note: success only indicates submission of cancelation request.
|
| 271 |
* Sucessful cancelation is not known until the callback occurs.
|
| 272 |
*/
|
| 273 |
bool usb_endpoint::cancel_all()
|
| 274 |
{
|
| 275 |
std::list<libusb_transfer*>::iterator iter; |
| 276 |
|
| 277 |
for (iter = _pending_list.begin(); iter != _pending_list.end(); iter++) {
|
| 278 |
if (libusb_cancel_transfer(*iter) < 0) { |
| 279 |
std::cerr << "error: libusb_cancal_transfer() failed" << std::endl;
|
| 280 |
return false; |
| 281 |
} |
| 282 |
} |
| 283 |
|
| 284 |
return true; |
| 285 |
} |
| 286 |
|
| 287 |
|
| 288 |
/*
|
| 289 |
* Reap completed transfers
|
| 290 |
*
|
| 291 |
* return true if at least one transfer was reaped, false otherwise.
|
| 292 |
*
|
| 293 |
* Check completed transfers for errors and mark as free. This is a
|
| 294 |
* blocking call.
|
| 295 |
*/
|
| 296 |
bool usb_endpoint::reap_completed_list()
|
| 297 |
{
|
| 298 |
libusb_transfer *lut; |
| 299 |
|
| 300 |
if (_completed_list.empty()) {
|
| 301 |
if (!reap_pending_list_timeout())
|
| 302 |
return false; |
| 303 |
} |
| 304 |
|
| 305 |
while (!_completed_list.empty()) {
|
| 306 |
lut = completed_list_get(); |
| 307 |
print_transfer_status(lut); |
| 308 |
free_list_add(lut); |
| 309 |
} |
| 310 |
|
| 311 |
return true; |
| 312 |
} |
| 313 |
|
| 314 |
|
| 315 |
/*
|
| 316 |
* Print completed transfer status error(s)
|
| 317 |
*
|
| 318 |
* return true if at least one transfer was reaped, false otherwise.
|
| 319 |
*
|
| 320 |
* Check completed transfers for errors and mark as free. This is a
|
| 321 |
* blocking call.
|
| 322 |
*/
|
| 323 |
void usb_endpoint::print_transfer_status(libusb_transfer *lut)
|
| 324 |
{
|
| 325 |
switch (lut->status) {
|
| 326 |
case LIBUSB_TRANSFER_COMPLETED:
|
| 327 |
if (lut->actual_length < lut->length) {
|
| 328 |
std::cerr << "USB: transfer completed with short write,"
|
| 329 |
<< " length = " << lut->length
|
| 330 |
<< " actual = " << lut->actual_length << std::endl;
|
| 331 |
} |
| 332 |
|
| 333 |
if ((lut->actual_length < 0) || (lut->length < 0)) { |
| 334 |
std::cerr << "USB: transfer completed with invalid response"
|
| 335 |
<< std::endl; |
| 336 |
} |
| 337 |
break;
|
| 338 |
case LIBUSB_TRANSFER_CANCELLED:
|
| 339 |
break;
|
| 340 |
case LIBUSB_TRANSFER_NO_DEVICE:
|
| 341 |
std::cerr << "USB: device was disconnected" << std::endl;
|
| 342 |
break;
|
| 343 |
case LIBUSB_TRANSFER_OVERFLOW:
|
| 344 |
std::cerr << "USB: device sent more data than requested" << std::endl;
|
| 345 |
break;
|
| 346 |
case LIBUSB_TRANSFER_TIMED_OUT:
|
| 347 |
std::cerr << "USB: transfer timed out" << std::endl;
|
| 348 |
break;
|
| 349 |
case LIBUSB_TRANSFER_STALL:
|
| 350 |
std::cerr << "USB: halt condition detected (stalled)" << std::endl;
|
| 351 |
break;
|
| 352 |
case LIBUSB_TRANSFER_ERROR:
|
| 353 |
std::cerr << "USB: transfer failed" << std::endl;
|
| 354 |
break;
|
| 355 |
default:
|
| 356 |
std::cerr << "USB: received unknown transfer status" << std::endl;
|
| 357 |
} |
| 358 |
} |
| 359 |
|
| 360 |
|
| 361 |
/*
|
| 362 |
* Reap pending transfers
|
| 363 |
*
|
| 364 |
* Return true if at least one transfer was reaped, false otherwise. This is
|
| 365 |
* a blocking call.
|
| 366 |
*
|
| 367 |
* Reaping submitted transfers is handled by libusb and the assigned callback
|
| 368 |
* function. Block until at least one transfer is reaped.
|
| 369 |
*/
|
| 370 |
bool usb_endpoint::reap_pending_list()
|
| 371 |
{
|
| 372 |
int retval;
|
| 373 |
|
| 374 |
if ((retval = libusb_handle_events(_ctx)) < 0) { |
| 375 |
std::cerr << "error: libusb_handle_events: " << retval << std::endl;
|
| 376 |
return false; |
| 377 |
} |
| 378 |
|
| 379 |
return true; |
| 380 |
} |
| 381 |
|
| 382 |
|
| 383 |
/*
|
| 384 |
* Reap pending transfers with timeout
|
| 385 |
*
|
| 386 |
* Return true if at least one transfer was reaped, false otherwise. This call
|
| 387 |
* blocks until a transfer is reaped or timeout.
|
| 388 |
*
|
| 389 |
* Reaping submitted transfers is handled by libusb and the assigned callback
|
| 390 |
* function. Block until at least one transfer is reaped or timeout occurs.
|
| 391 |
*/
|
| 392 |
bool usb_endpoint::reap_pending_list_timeout()
|
| 393 |
{
|
| 394 |
int retval;
|
| 395 |
timeval tv; |
| 396 |
|
| 397 |
tv.tv_sec = 0;
|
| 398 |
tv.tv_usec = 100000; //100ms |
| 399 |
|
| 400 |
size_t pending_list_size = _pending_list.size(); |
| 401 |
|
| 402 |
if ((retval = libusb_handle_events_timeout(_ctx, &tv)) < 0) { |
| 403 |
std::cerr << "error: libusb_handle_events: " << retval << std::endl;
|
| 404 |
return false; |
| 405 |
} |
| 406 |
|
| 407 |
if (_pending_list.size() < pending_list_size) {
|
| 408 |
return true; |
| 409 |
} |
| 410 |
else {
|
| 411 |
return false; |
| 412 |
} |
| 413 |
} |
| 414 |
|
| 415 |
|
| 416 |
/*
|
| 417 |
* Returns a free transfer with empty data bufer for OUT requests
|
| 418 |
*/
|
| 419 |
libusb_transfer *usb_endpoint::get_free_transfer() |
| 420 |
{
|
| 421 |
if (_free_list.empty()) {
|
| 422 |
if (!reap_completed_list())
|
| 423 |
return NULL; |
| 424 |
} |
| 425 |
|
| 426 |
return free_list_get();
|
| 427 |
} |
| 428 |
|
| 429 |
|
| 430 |
/*
|
| 431 |
* Returns a transfer containing data for IN requests
|
| 432 |
*/
|
| 433 |
libusb_transfer *usb_endpoint::get_completed_transfer() |
| 434 |
{
|
| 435 |
if (_completed_list.empty()) {
|
| 436 |
if (!reap_pending_list_timeout())
|
| 437 |
return NULL; |
| 438 |
} |
| 439 |
|
| 440 |
return completed_list_get();
|
| 441 |
} |
| 442 |
|
| 443 |
/*
|
| 444 |
* List operations
|
| 445 |
*/
|
| 446 |
void usb_endpoint::free_list_add(libusb_transfer *lut)
|
| 447 |
{
|
| 448 |
_free_list.push_back(lut); |
| 449 |
} |
| 450 |
|
| 451 |
void usb_endpoint::pending_list_add(libusb_transfer *lut)
|
| 452 |
{
|
| 453 |
_pending_list.push_back(lut); |
| 454 |
} |
| 455 |
|
| 456 |
void usb_endpoint::completed_list_add(libusb_transfer *lut)
|
| 457 |
{
|
| 458 |
_completed_list.push_back(lut); |
| 459 |
} |
| 460 |
|
| 461 |
|
| 462 |
/*
|
| 463 |
* Free and completed lists don't have ordered content
|
| 464 |
*
|
| 465 |
* Pop transfers from the front as needed
|
| 466 |
*/
|
| 467 |
libusb_transfer *usb_endpoint::free_list_get() |
| 468 |
{
|
| 469 |
libusb_transfer *lut; |
| 470 |
|
| 471 |
if (_free_list.size() == 0) { |
| 472 |
return NULL; |
| 473 |
} |
| 474 |
else {
|
| 475 |
lut = _free_list.front(); |
| 476 |
_free_list.pop_front(); |
| 477 |
return lut;
|
| 478 |
} |
| 479 |
} |
| 480 |
|
| 481 |
|
| 482 |
/*
|
| 483 |
* Free and completed lists don't have ordered content
|
| 484 |
*
|
| 485 |
* Pop transfers from the front as needed
|
| 486 |
*/
|
| 487 |
libusb_transfer *usb_endpoint::completed_list_get() |
| 488 |
{
|
| 489 |
libusb_transfer *lut; |
| 490 |
|
| 491 |
if (_completed_list.empty()) {
|
| 492 |
return NULL; |
| 493 |
} |
| 494 |
else {
|
| 495 |
lut = _completed_list.front(); |
| 496 |
_completed_list.pop_front(); |
| 497 |
return lut;
|
| 498 |
} |
| 499 |
} |
| 500 |
|
| 501 |
|
| 502 |
/*
|
| 503 |
* Search and remove transfer from pending list
|
| 504 |
*
|
| 505 |
* Assuming that the callbacks occur in order, the front element
|
| 506 |
* should yield the correct transfer. If not, then something else
|
| 507 |
* is going on. If no transfers match, then something went wrong.
|
| 508 |
*/
|
| 509 |
bool usb_endpoint::pending_list_remove(libusb_transfer *lut)
|
| 510 |
{
|
| 511 |
std::list<libusb_transfer*>::iterator iter; |
| 512 |
for (iter = _pending_list.begin(); iter != _pending_list.end(); iter++) {
|
| 513 |
if (*iter == lut) {
|
| 514 |
_pending_list.erase(iter); |
| 515 |
return true; |
| 516 |
} |
| 517 |
} |
| 518 |
return false; |
| 519 |
} |
| 520 |
|
| 521 |
|
| 522 |
/***********************************************************************
|
| 523 |
* Managed buffers
|
| 524 |
**********************************************************************/
|
| 525 |
class libusb_managed_recv_buffer_impl : public managed_recv_buffer { |
| 526 |
public:
|
| 527 |
libusb_managed_recv_buffer_impl(libusb_transfer *lut, |
| 528 |
usb_endpoint *endpoint) |
| 529 |
: _buff(lut->buffer, lut->length) |
| 530 |
{
|
| 531 |
_lut = lut; |
| 532 |
_endpoint = endpoint; |
| 533 |
} |
| 534 |
|
| 535 |
~libusb_managed_recv_buffer_impl() |
| 536 |
{
|
| 537 |
if (!_endpoint->submit(_lut))
|
| 538 |
std::cerr << "USB: failed to submit IN transfer" << std::endl;
|
| 539 |
} |
| 540 |
|
| 541 |
private:
|
| 542 |
const boost::asio::const_buffer &get() const |
| 543 |
{
|
| 544 |
return _buff;
|
| 545 |
} |
| 546 |
|
| 547 |
libusb_transfer *_lut; |
| 548 |
usb_endpoint *_endpoint; |
| 549 |
const boost::asio::const_buffer _buff;
|
| 550 |
}; |
| 551 |
|
| 552 |
|
| 553 |
class libusb_managed_send_buffer_impl : public managed_send_buffer { |
| 554 |
public:
|
| 555 |
libusb_managed_send_buffer_impl(libusb_transfer *lut, |
| 556 |
usb_endpoint *endpoint, |
| 557 |
size_t buff_size) |
| 558 |
: _buff(lut->buffer, buff_size) |
| 559 |
{
|
| 560 |
_lut = lut; |
| 561 |
_endpoint = endpoint; |
| 562 |
} |
| 563 |
|
| 564 |
~libusb_managed_send_buffer_impl() |
| 565 |
{
|
| 566 |
/* NOP */
|
| 567 |
} |
| 568 |
|
| 569 |
ssize_t commit(size_t num_bytes) |
| 570 |
{
|
| 571 |
_lut->length = num_bytes; |
| 572 |
_lut->actual_length = 0;
|
| 573 |
|
| 574 |
if (_endpoint->submit(_lut))
|
| 575 |
return num_bytes;
|
| 576 |
else
|
| 577 |
return 0; |
| 578 |
} |
| 579 |
|
| 580 |
private:
|
| 581 |
const boost::asio::mutable_buffer &get() const |
| 582 |
{
|
| 583 |
return _buff;
|
| 584 |
} |
| 585 |
|
| 586 |
libusb_transfer *_lut; |
| 587 |
usb_endpoint *_endpoint; |
| 588 |
const boost::asio::mutable_buffer _buff;
|
| 589 |
}; |
| 590 |
|
| 591 |
|
| 592 |
/***********************************************************************
|
| 593 |
* USB zero_copy device class
|
| 594 |
**********************************************************************/
|
| 595 |
class libusb_zero_copy_impl : public usb_zero_copy |
| 596 |
{
|
| 597 |
private:
|
| 598 |
usb_endpoint *_rx_ep; |
| 599 |
usb_endpoint *_tx_ep; |
| 600 |
|
| 601 |
/*
|
| 602 |
* Libusb handles
|
| 603 |
*/
|
| 604 |
libusb_context *_rx_ctx; |
| 605 |
libusb_context *_tx_ctx; |
| 606 |
libusb_device_handle *_rx_dev_handle; |
| 607 |
libusb_device_handle *_tx_dev_handle; |
| 608 |
|
| 609 |
size_t _recv_buff_size; |
| 610 |
size_t _send_buff_size; |
| 611 |
size_t _num_frames; |
| 612 |
|
| 613 |
public:
|
| 614 |
typedef boost::shared_ptr<libusb_zero_copy_impl> sptr;
|
| 615 |
|
| 616 |
/*
|
| 617 |
* Structors
|
| 618 |
*/
|
| 619 |
libusb_zero_copy_impl(usb_device_handle::sptr handle, |
| 620 |
unsigned int rx_endpoint, |
| 621 |
unsigned int tx_endpoint, |
| 622 |
size_t recv_buff_size, |
| 623 |
size_t send_buff_size); |
| 624 |
|
| 625 |
~libusb_zero_copy_impl(); |
| 626 |
|
| 627 |
managed_recv_buffer::sptr get_recv_buff(void);
|
| 628 |
managed_send_buffer::sptr get_send_buff(void);
|
| 629 |
|
| 630 |
size_t get_num_recv_frames(void) const { return _num_frames; } |
| 631 |
size_t get_num_send_frames(void) const { return _num_frames; } |
| 632 |
}; |
| 633 |
|
| 634 |
|
| 635 |
libusb_zero_copy_impl::libusb_zero_copy_impl(usb_device_handle::sptr handle, |
| 636 |
unsigned int rx_endpoint, |
| 637 |
unsigned int tx_endpoint, |
| 638 |
size_t buff_size, |
| 639 |
size_t block_size) |
| 640 |
: _rx_ctx(NULL), _tx_ctx(NULL), _rx_dev_handle(NULL), _tx_dev_handle(NULL), |
| 641 |
_recv_buff_size(block_size), _send_buff_size(block_size), |
| 642 |
_num_frames(buff_size / block_size) |
| 643 |
{
|
| 644 |
libusb::init(&_rx_ctx, libusb_debug_level); |
| 645 |
libusb::init(&_tx_ctx, libusb_debug_level); |
| 646 |
|
| 647 |
UHD_ASSERT_THROW((_rx_ctx != NULL) && (_tx_ctx != NULL)); |
| 648 |
|
| 649 |
_rx_dev_handle = libusb::open_device(_rx_ctx, handle); |
| 650 |
_tx_dev_handle = libusb::open_device(_tx_ctx, handle); |
| 651 |
|
| 652 |
libusb::open_interface(_rx_dev_handle, 2);
|
| 653 |
libusb::open_interface(_tx_dev_handle, 1);
|
| 654 |
|
| 655 |
_rx_ep = new usb_endpoint(_rx_dev_handle,
|
| 656 |
_rx_ctx, |
| 657 |
rx_endpoint, |
| 658 |
true,
|
| 659 |
_recv_buff_size, |
| 660 |
_num_frames); |
| 661 |
|
| 662 |
_tx_ep = new usb_endpoint(_tx_dev_handle,
|
| 663 |
_tx_ctx, |
| 664 |
tx_endpoint, |
| 665 |
false,
|
| 666 |
_send_buff_size, |
| 667 |
_num_frames); |
| 668 |
} |
| 669 |
|
| 670 |
|
| 671 |
libusb_zero_copy_impl::~libusb_zero_copy_impl() |
| 672 |
{
|
| 673 |
delete _rx_ep;
|
| 674 |
delete _tx_ep;
|
| 675 |
|
| 676 |
libusb_close(_rx_dev_handle); |
| 677 |
libusb_close(_tx_dev_handle); |
| 678 |
|
| 679 |
libusb_exit(_rx_ctx); |
| 680 |
libusb_exit(_tx_ctx); |
| 681 |
} |
| 682 |
|
| 683 |
|
| 684 |
managed_recv_buffer::sptr libusb_zero_copy_impl::get_recv_buff() |
| 685 |
{
|
| 686 |
libusb_transfer *lut = _rx_ep->get_completed_transfer(); |
| 687 |
if (lut == NULL) { |
| 688 |
return managed_recv_buffer::sptr();
|
| 689 |
} |
| 690 |
else {
|
| 691 |
return managed_recv_buffer::sptr(
|
| 692 |
new libusb_managed_recv_buffer_impl(lut,
|
| 693 |
_rx_ep)); |
| 694 |
} |
| 695 |
} |
| 696 |
|
| 697 |
|
| 698 |
managed_send_buffer::sptr libusb_zero_copy_impl::get_send_buff() |
| 699 |
{
|
| 700 |
libusb_transfer *lut = _tx_ep->get_free_transfer(); |
| 701 |
if (lut == NULL) { |
| 702 |
return managed_send_buffer::sptr();
|
| 703 |
} |
| 704 |
else {
|
| 705 |
return managed_send_buffer::sptr(
|
| 706 |
new libusb_managed_send_buffer_impl(lut,
|
| 707 |
_tx_ep, |
| 708 |
_send_buff_size)); |
| 709 |
} |
| 710 |
} |
| 711 |
|
| 712 |
|
| 713 |
/***********************************************************************
|
| 714 |
* USB zero_copy make functions
|
| 715 |
**********************************************************************/
|
| 716 |
usb_zero_copy::sptr usb_zero_copy::make(usb_device_handle::sptr handle, |
| 717 |
unsigned int rx_endpoint, |
| 718 |
unsigned int tx_endpoint, |
| 719 |
size_t buff_size, |
| 720 |
size_t block_size) |
| 721 |
|
| 722 |
{
|
| 723 |
return sptr(new libusb_zero_copy_impl(handle, |
| 724 |
rx_endpoint, |
| 725 |
tx_endpoint, |
| 726 |
buff_size, |
| 727 |
block_size)); |
| 728 |
} |
| 729 |
|
| 730 |
|
| 731 |
|