Statistics
| Branch: | Tag: | Revision:

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