Statistics
| Branch: | Tag: | Revision:

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
}