Statistics
| Branch: | Tag: | Revision:

root / host / lib / transport / libusb1_zero_copy.cpp @ d18fc6f0

History | View | Annotate | Download (11.7 KB)

1
//
2
// Copyright 2010-2012 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
        //flush the buffers out of the recv endpoint
181
        //limit the flushing to at most one second
182
        for (size_t i = 0; i < 100; i++)
183
        {
184
            unsigned char buff[512];
185
            int transfered = 0;
186
            const int status = libusb_bulk_transfer(
187
                _handle->get(), // dev_handle
188
                (recv_endpoint & 0x7f) | 0x80, // endpoint
189
                static_cast<unsigned char *>(buff),
190
                sizeof(buff),
191
                &transfered, //bytes xfered
192
                10 //timeout ms
193
            );
194
            if (status == LIBUSB_ERROR_TIMEOUT) break;
195
        }
196

    
197
        //allocate libusb transfer structs and managed receive buffers
198
        for (size_t i = 0; i < get_num_recv_frames(); i++){
199

    
200
            libusb_transfer *lut = libusb_alloc_transfer(0);
201
            UHD_ASSERT_THROW(lut != NULL);
202

    
203
            _mrb_pool.push_back(boost::make_shared<libusb_zero_copy_mrb>(lut, this->get_recv_frame_size()));
204

    
205
            libusb_fill_bulk_transfer(
206
                lut,                                                    // transfer
207
                _handle->get(),                                         // dev_handle
208
                (recv_endpoint & 0x7f) | 0x80,                          // endpoint
209
                static_cast<unsigned char *>(_recv_buffer_pool->at(i)), // buffer
210
                this->get_recv_frame_size(),                            // length
211
                libusb_transfer_cb_fn(&libusb_async_cb),                // callback
212
                static_cast<void *>(&_mrb_pool.back()->completed),      // user_data
213
                0                                                       // timeout (ms)
214
            );
215

    
216
            _all_luts.push_back(lut);
217
            _mrb_pool.back()->release();
218
        }
219

    
220
        //allocate libusb transfer structs and managed send buffers
221
        for (size_t i = 0; i < get_num_send_frames(); i++){
222

    
223
            libusb_transfer *lut = libusb_alloc_transfer(0);
224
            UHD_ASSERT_THROW(lut != NULL);
225

    
226
            _msb_pool.push_back(boost::make_shared<libusb_zero_copy_msb>(lut, this->get_send_frame_size()));
227

    
228
            libusb_fill_bulk_transfer(
229
                lut,                                                    // transfer
230
                _handle->get(),                                         // dev_handle
231
                (send_endpoint & 0x7f) | 0x00,                          // endpoint
232
                static_cast<unsigned char *>(_send_buffer_pool->at(i)), // buffer
233
                this->get_send_frame_size(),                            // length
234
                libusb_transfer_cb_fn(&libusb_async_cb),                // callback
235
                static_cast<void *>(&_msb_pool.back()->completed),      // user_data
236
                0                                                       // timeout
237
            );
238

    
239
            _all_luts.push_back(lut);
240
        }
241
    }
242

    
243
    ~libusb_zero_copy_impl(void){
244
        libusb_context *ctx = libusb::session::get_global_session()->get_context();
245

    
246
        //cancel all transfers
247
        BOOST_FOREACH(libusb_transfer *lut, _all_luts){
248
            libusb_cancel_transfer(lut);
249
        }
250

    
251
        //process all transfers until timeout occurs
252
        bool completed = false;
253
        wait_for_completion(ctx, 0.01, completed);
254

    
255
        //free all transfers
256
        BOOST_FOREACH(libusb_transfer *lut, _all_luts){
257
            libusb_free_transfer(lut);
258
        }
259

    
260
    }
261

    
262
    managed_recv_buffer::sptr get_recv_buff(double timeout){
263
        if (_next_recv_buff_index == _num_recv_frames) _next_recv_buff_index = 0;
264
        return _mrb_pool[_next_recv_buff_index]->get_new(timeout, _next_recv_buff_index);
265
    }
266

    
267
    managed_send_buffer::sptr get_send_buff(double timeout){
268
        if (_next_send_buff_index == _num_send_frames) _next_send_buff_index = 0;
269
        return _msb_pool[_next_send_buff_index]->get_new(timeout, _next_send_buff_index);
270
    }
271

    
272
    size_t get_num_recv_frames(void) const { return _num_recv_frames; }
273
    size_t get_num_send_frames(void) const { return _num_send_frames; }
274

    
275
    size_t get_recv_frame_size(void) const { return _recv_frame_size; }
276
    size_t get_send_frame_size(void) const { return _send_frame_size; }
277

    
278
private:
279
    libusb::device_handle::sptr _handle;
280
    const size_t _recv_frame_size, _num_recv_frames;
281
    const size_t _send_frame_size, _num_send_frames;
282

    
283
    //! Storage for transfer related objects
284
    buffer_pool::sptr _recv_buffer_pool, _send_buffer_pool;
285
    std::vector<boost::shared_ptr<libusb_zero_copy_mrb> > _mrb_pool;
286
    std::vector<boost::shared_ptr<libusb_zero_copy_msb> > _msb_pool;
287
    size_t _next_recv_buff_index, _next_send_buff_index;
288

    
289
    //! a list of all transfer structs we allocated
290
    std::list<libusb_transfer *> _all_luts;
291

    
292

    
293
};
294

    
295
/***********************************************************************
296
 * USB zero_copy make functions
297
 **********************************************************************/
298
usb_zero_copy::sptr usb_zero_copy::make(
299
    usb_device_handle::sptr handle,
300
    const size_t recv_interface,
301
    const size_t recv_endpoint,
302
    const size_t send_interface,
303
    const size_t send_endpoint,
304
    const device_addr_t &hints
305
){
306
    libusb::device_handle::sptr dev_handle(libusb::device_handle::get_cached_handle(
307
        boost::static_pointer_cast<libusb::special_handle>(handle)->get_device()
308
    ));
309
    return sptr(new libusb_zero_copy_impl(
310
        dev_handle, recv_interface, recv_endpoint, send_interface, send_endpoint, hints
311
    ));
312
}