Revision 543a6364 host/lib/transport/libusb1_zero_copy.cpp
| b/host/lib/transport/libusb1_zero_copy.cpp | ||
|---|---|---|
| 30 | 30 |
|
| 31 | 31 |
const int libusb_timeout = 0; |
| 32 | 32 |
|
| 33 |
static const size_t DEFAULT_NUM_XFERS = 16; //num xfers |
|
| 34 |
static const size_t DEFAULT_XFER_SIZE = 32*512; //bytes |
|
| 35 |
|
|
| 33 | 36 |
/*********************************************************************** |
| 34 | 37 |
* Helper functions |
| 35 | 38 |
***********************************************************************/ |
| ... | ... | |
| 56 | 59 |
* create a bidirectional interface. It is a zero copy implementation |
| 57 | 60 |
* with respect to libusb, however, each send and recv requires a copy |
| 58 | 61 |
* operation from kernel to userspace; this is due to the usbfs |
| 59 |
* interface provided by the kernel.
|
|
| 62 |
* interface provided by the kernel. |
|
| 60 | 63 |
**********************************************************************/ |
| 61 | 64 |
class usb_endpoint {
|
| 62 | 65 |
public: |
| ... | ... | |
| 107 | 110 |
//! a list of shared arrays for the transfer buffers |
| 108 | 111 |
std::vector<boost::shared_array<boost::uint8_t> > _buffers; |
| 109 | 112 |
|
| 110 |
// Calls for processing asynchronous I/O
|
|
| 113 |
// Calls for processing asynchronous I/O |
|
| 111 | 114 |
libusb_transfer *allocate_transfer(int buff_len); |
| 112 | 115 |
void print_transfer_status(libusb_transfer *lut); |
| 113 | 116 |
}; |
| ... | ... | |
| 142 | 145 |
* Constructor |
| 143 | 146 |
* Allocate libusb transfers and mark as free. For IN endpoints, |
| 144 | 147 |
* submit the transfers so that they're ready to return when |
| 145 |
* data is available.
|
|
| 148 |
* data is available. |
|
| 146 | 149 |
*/ |
| 147 | 150 |
usb_endpoint::usb_endpoint( |
| 148 | 151 |
libusb::device_handle::sptr handle, |
| ... | ... | |
| 194 | 197 |
|
| 195 | 198 |
|
| 196 | 199 |
/* |
| 197 |
* Allocate a libusb transfer
|
|
| 200 |
* Allocate a libusb transfer |
|
| 198 | 201 |
* The allocated transfer - and buffer it contains - is repeatedly |
| 199 | 202 |
* submitted, reaped, and reused and should not be freed until shutdown. |
| 200 | 203 |
* \param buff_len size of the individual buffer held by each transfer |
| ... | ... | |
| 225 | 228 |
* Asynchonous transfer submission |
| 226 | 229 |
* Submit a libusb transfer to libusb add pending status |
| 227 | 230 |
* \param lut pointer to libusb_transfer |
| 228 |
* \return true on success or false on error
|
|
| 231 |
* \return true on success or false on error |
|
| 229 | 232 |
*/ |
| 230 | 233 |
void usb_endpoint::submit(libusb_transfer *lut){
|
| 231 | 234 |
UHD_ASSERT_THROW(libusb_submit_transfer(lut) == 0); |
| ... | ... | |
| 281 | 284 |
} |
| 282 | 285 |
|
| 283 | 286 |
/*********************************************************************** |
| 284 |
* Managed buffers
|
|
| 287 |
* Managed buffers |
|
| 285 | 288 |
**********************************************************************/ |
| 286 | 289 |
/* |
| 287 | 290 |
* Libusb managed receive buffer |
| 288 | 291 |
* Construct a recv buffer from a libusb transfer. The memory held by |
| 289 | 292 |
* the libusb transfer is exposed through the managed buffer interface. |
| 290 | 293 |
* Upon destruction, the transfer and buffer are resubmitted to the |
| 291 |
* endpoint for further use.
|
|
| 294 |
* endpoint for further use. |
|
| 292 | 295 |
*/ |
| 293 | 296 |
class libusb_managed_recv_buffer_impl : public managed_recv_buffer {
|
| 294 | 297 |
public: |
| ... | ... | |
| 307 | 310 |
private: |
| 308 | 311 |
const boost::asio::const_buffer &get() const |
| 309 | 312 |
{
|
| 310 |
return _buff;
|
|
| 313 |
return _buff; |
|
| 311 | 314 |
} |
| 312 | 315 |
|
| 313 | 316 |
libusb_transfer *_lut; |
| ... | ... | |
| 327 | 330 |
class libusb_managed_send_buffer_impl : public managed_send_buffer {
|
| 328 | 331 |
public: |
| 329 | 332 |
libusb_managed_send_buffer_impl(libusb_transfer *lut, |
| 330 |
usb_endpoint::sptr endpoint, |
|
| 331 |
size_t buff_size) |
|
| 332 |
: _buff(lut->buffer, buff_size), _committed(false) |
|
| 333 |
usb_endpoint::sptr endpoint) |
|
| 334 |
: _buff(lut->buffer, lut->length), _committed(false) |
|
| 333 | 335 |
{
|
| 334 | 336 |
_lut = lut; |
| 335 | 337 |
_endpoint = endpoint; |
| ... | ... | |
| 349 | 351 |
std::cerr << "UHD: send buffer already committed" << std::endl; |
| 350 | 352 |
return 0; |
| 351 | 353 |
} |
| 352 |
|
|
| 354 |
|
|
| 353 | 355 |
UHD_ASSERT_THROW(num_bytes <= boost::asio::buffer_size(_buff)); |
| 354 | 356 |
|
| 355 | 357 |
_lut->length = num_bytes; |
| ... | ... | |
| 369 | 371 |
private: |
| 370 | 372 |
const boost::asio::mutable_buffer &get() const |
| 371 | 373 |
{
|
| 372 |
return _buff;
|
|
| 374 |
return _buff; |
|
| 373 | 375 |
} |
| 374 | 376 |
|
| 375 | 377 |
libusb_transfer *_lut; |
| ... | ... | |
| 385 | 387 |
class libusb_zero_copy_impl : public usb_zero_copy |
| 386 | 388 |
{
|
| 387 | 389 |
private: |
| 388 |
usb_endpoint::sptr _rx_ep, _tx_ep; |
|
| 389 |
|
|
| 390 | 390 |
libusb::device_handle::sptr _handle; |
| 391 |
|
|
| 392 |
size_t _recv_buff_size; |
|
| 393 |
size_t _send_buff_size; |
|
| 394 |
size_t _num_frames; |
|
| 391 |
size_t _recv_num_frames, _send_num_frames; |
|
| 392 |
usb_endpoint::sptr _recv_ep, _send_ep; |
|
| 395 | 393 |
|
| 396 | 394 |
public: |
| 397 | 395 |
typedef boost::shared_ptr<libusb_zero_copy_impl> sptr; |
| 398 | 396 |
|
| 399 |
libusb_zero_copy_impl(libusb::device_handle::sptr handle, |
|
| 400 |
unsigned int rx_endpoint, |
|
| 401 |
unsigned int tx_endpoint, |
|
| 402 |
size_t recv_buff_size, |
|
| 403 |
size_t send_buff_size); |
|
| 397 |
libusb_zero_copy_impl( |
|
| 398 |
libusb::device_handle::sptr handle, |
|
| 399 |
unsigned int recv_endpoint, unsigned int send_endpoint, |
|
| 400 |
size_t recv_xfer_size, size_t recv_num_xfers, |
|
| 401 |
size_t send_xfer_size, size_t send_num_xfers |
|
| 402 |
); |
|
| 404 | 403 |
|
| 405 | 404 |
managed_recv_buffer::sptr get_recv_buff(void); |
| 406 | 405 |
managed_send_buffer::sptr get_send_buff(void); |
| 407 | 406 |
|
| 408 |
size_t get_num_recv_frames(void) const { return _num_frames; }
|
|
| 409 |
size_t get_num_send_frames(void) const { return _num_frames; }
|
|
| 407 |
size_t get_num_recv_frames(void) const { return _recv_num_frames; }
|
|
| 408 |
size_t get_num_send_frames(void) const { return _send_num_frames; }
|
|
| 410 | 409 |
}; |
| 411 | 410 |
|
| 412 | 411 |
/* |
| 413 | 412 |
* Constructor |
| 414 | 413 |
* Initializes libusb, opens devices, and sets up interfaces for I/O. |
| 415 |
* Finally, creates endpoints for asynchronous I/O.
|
|
| 414 |
* Finally, creates endpoints for asynchronous I/O. |
|
| 416 | 415 |
*/ |
| 417 |
libusb_zero_copy_impl::libusb_zero_copy_impl(libusb::device_handle::sptr handle, |
|
| 418 |
unsigned int rx_endpoint, |
|
| 419 |
unsigned int tx_endpoint, |
|
| 420 |
size_t buff_size, |
|
| 421 |
size_t block_size) |
|
| 422 |
: _handle(handle), |
|
| 423 |
_recv_buff_size(block_size), _send_buff_size(block_size), |
|
| 424 |
_num_frames(buff_size / block_size) |
|
| 425 |
{
|
|
| 426 |
_handle->claim_interface(2 /*in interface*/); |
|
| 416 |
libusb_zero_copy_impl::libusb_zero_copy_impl( |
|
| 417 |
libusb::device_handle::sptr handle, |
|
| 418 |
unsigned int recv_endpoint, unsigned int send_endpoint, |
|
| 419 |
size_t recv_xfer_size, size_t recv_num_xfers, |
|
| 420 |
size_t send_xfer_size, size_t send_num_xfers |
|
| 421 |
){
|
|
| 422 |
_handle = handle; |
|
| 423 |
|
|
| 424 |
//if the sizes are left at 0 (automatic) -> use the defaults |
|
| 425 |
if (recv_xfer_size == 0) recv_xfer_size = DEFAULT_XFER_SIZE; |
|
| 426 |
if (recv_num_xfers == 0) recv_num_xfers = DEFAULT_NUM_XFERS; |
|
| 427 |
if (send_xfer_size == 0) send_xfer_size = DEFAULT_XFER_SIZE; |
|
| 428 |
if (send_num_xfers == 0) send_num_xfers = DEFAULT_NUM_XFERS; |
|
| 429 |
|
|
| 430 |
//sanity check the transfer sizes |
|
| 431 |
UHD_ASSERT_THROW(recv_xfer_size % 512 == 0); |
|
| 432 |
UHD_ASSERT_THROW(send_xfer_size % 512 == 0); |
|
| 433 |
|
|
| 434 |
//store the num xfers for the num frames count |
|
| 435 |
_recv_num_frames = recv_num_xfers; |
|
| 436 |
_send_num_frames = send_num_xfers; |
|
| 437 |
|
|
| 438 |
_handle->claim_interface(2 /*in interface*/); |
|
| 427 | 439 |
_handle->claim_interface(1 /*out interface*/); |
| 428 | 440 |
|
| 429 |
_rx_ep = usb_endpoint::sptr(new usb_endpoint(
|
|
| 441 |
_recv_ep = usb_endpoint::sptr(new usb_endpoint(
|
|
| 430 | 442 |
_handle, // libusb device_handle |
| 431 |
rx_endpoint, // USB endpoint number
|
|
| 443 |
recv_endpoint, // USB endpoint number
|
|
| 432 | 444 |
true, // IN endpoint |
| 433 |
_recv_buff_size, // buffer size per transfer
|
|
| 434 |
_num_frames // number of libusb transfers
|
|
| 445 |
recv_xfer_size, // buffer size per transfer
|
|
| 446 |
recv_num_xfers // number of libusb transfers
|
|
| 435 | 447 |
)); |
| 436 | 448 |
|
| 437 |
_tx_ep = usb_endpoint::sptr(new usb_endpoint(
|
|
| 449 |
_send_ep = usb_endpoint::sptr(new usb_endpoint(
|
|
| 438 | 450 |
_handle, // libusb device_handle |
| 439 |
tx_endpoint, // USB endpoint number
|
|
| 451 |
send_endpoint, // USB endpoint number
|
|
| 440 | 452 |
false, // OUT endpoint |
| 441 |
_send_buff_size, // buffer size per transfer
|
|
| 442 |
_num_frames // number of libusb transfers
|
|
| 453 |
send_xfer_size, // buffer size per transfer
|
|
| 454 |
send_num_xfers // number of libusb transfers
|
|
| 443 | 455 |
)); |
| 444 | 456 |
} |
| 445 | 457 |
|
| ... | ... | |
| 447 | 459 |
* Construct a managed receive buffer from a completed libusb transfer |
| 448 | 460 |
* (happy with buffer full of data) obtained from the receive endpoint. |
| 449 | 461 |
* Return empty pointer if no transfer is available (timeout or error). |
| 450 |
* \return pointer to a managed receive buffer
|
|
| 462 |
* \return pointer to a managed receive buffer |
|
| 451 | 463 |
*/ |
| 452 | 464 |
managed_recv_buffer::sptr libusb_zero_copy_impl::get_recv_buff(void){
|
| 453 |
libusb_transfer *lut = _rx_ep->get_lut_with_wait(/* TODO timeout API */);
|
|
| 465 |
libusb_transfer *lut = _recv_ep->get_lut_with_wait(/* TODO timeout API */);
|
|
| 454 | 466 |
if (lut == NULL) {
|
| 455 | 467 |
return managed_recv_buffer::sptr(); |
| 456 | 468 |
} |
| 457 | 469 |
else {
|
| 458 | 470 |
return managed_recv_buffer::sptr( |
| 459 | 471 |
new libusb_managed_recv_buffer_impl(lut, |
| 460 |
_rx_ep));
|
|
| 472 |
_recv_ep));
|
|
| 461 | 473 |
} |
| 462 | 474 |
} |
| 463 | 475 |
|
| ... | ... | |
| 466 | 478 |
* Construct a managed send buffer from a free libusb transfer (with |
| 467 | 479 |
* empty buffer). Return empty pointer of no transfer is available |
| 468 | 480 |
* (timeout or error). |
| 469 |
* \return pointer to a managed send buffer
|
|
| 481 |
* \return pointer to a managed send buffer |
|
| 470 | 482 |
*/ |
| 471 | 483 |
managed_send_buffer::sptr libusb_zero_copy_impl::get_send_buff(void){
|
| 472 |
libusb_transfer *lut = _tx_ep->get_lut_with_wait(/* TODO timeout API */);
|
|
| 484 |
libusb_transfer *lut = _send_ep->get_lut_with_wait(/* TODO timeout API */);
|
|
| 473 | 485 |
if (lut == NULL) {
|
| 474 | 486 |
return managed_send_buffer::sptr(); |
| 475 | 487 |
} |
| 476 | 488 |
else {
|
| 477 | 489 |
return managed_send_buffer::sptr( |
| 478 | 490 |
new libusb_managed_send_buffer_impl(lut, |
| 479 |
_tx_ep, |
|
| 480 |
_send_buff_size)); |
|
| 491 |
_send_ep)); |
|
| 481 | 492 |
} |
| 482 | 493 |
} |
| 483 | 494 |
|
| 484 |
|
|
| 485 | 495 |
/*********************************************************************** |
| 486 | 496 |
* USB zero_copy make functions |
| 487 | 497 |
**********************************************************************/ |
| 488 |
usb_zero_copy::sptr usb_zero_copy::make(usb_device_handle::sptr handle, |
|
| 489 |
unsigned int rx_endpoint, |
|
| 490 |
unsigned int tx_endpoint, |
|
| 491 |
size_t buff_size, |
|
| 492 |
size_t block_size) |
|
| 493 |
|
|
| 494 |
{
|
|
| 498 |
usb_zero_copy::sptr usb_zero_copy::make( |
|
| 499 |
usb_device_handle::sptr handle, |
|
| 500 |
unsigned int recv_endpoint, unsigned int send_endpoint, |
|
| 501 |
size_t recv_xfer_size, size_t recv_num_xfers, |
|
| 502 |
size_t send_xfer_size, size_t send_num_xfers |
|
| 503 |
){
|
|
| 495 | 504 |
libusb::device_handle::sptr dev_handle(libusb::device_handle::get_cached_handle( |
| 496 | 505 |
boost::static_pointer_cast<libusb::special_handle>(handle)->get_device() |
| 497 | 506 |
)); |
| 498 |
return sptr(new libusb_zero_copy_impl(dev_handle, |
|
| 499 |
rx_endpoint, |
|
| 500 |
tx_endpoint, |
|
| 501 |
buff_size, |
|
| 502 |
block_size)); |
|
| 507 |
return sptr(new libusb_zero_copy_impl( |
|
| 508 |
dev_handle, |
|
| 509 |
recv_endpoint, send_endpoint, |
|
| 510 |
recv_xfer_size, recv_num_xfers, |
|
| 511 |
send_xfer_size, send_num_xfers |
|
| 512 |
)); |
|
| 503 | 513 |
} |
Also available in: Unified diff