Statistics
| Branch: | Tag: | Revision:

root / host / lib / usrp / usrp_e100 / usrp_e100_mmap_zero_copy.cpp @ ba2523fc

History | View | Annotate | Download (8.21 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 "usrp_e100_iface.hpp"
19
#include <uhd/transport/zero_copy.hpp>
20
#include <uhd/utils/assert.hpp>
21
#include <linux/usrp_e.h>
22
#include <sys/mman.h> //mmap
23
#include <unistd.h> //getpagesize
24
#include <poll.h> //poll
25
#include <boost/bind.hpp>
26
#include <iostream>
27

    
28
using namespace uhd;
29
using namespace uhd::transport;
30

    
31
static const bool fp_verbose = false; //fast-path verbose
32
static const bool sp_verbose = false; //slow-path verbose
33
static const size_t poll_breakout = 10; //how many poll timeouts constitute a full timeout
34

    
35
/***********************************************************************
36
 * The zero copy interface implementation
37
 **********************************************************************/
38
class usrp_e100_mmap_zero_copy_impl : public zero_copy_if{
39
public:
40
    usrp_e100_mmap_zero_copy_impl(usrp_e100_iface::sptr iface):
41
        _fd(iface->get_file_descriptor()), _recv_index(0), _send_index(0)
42
    {
43
        //get system sizes
44
        iface->ioctl(USRP_E_GET_RB_INFO, &_rb_size);
45
        size_t page_size = getpagesize();
46
        _frame_size = page_size/2;
47

    
48
        //calculate the memory size
49
        _map_size =
50
            (_rb_size.num_pages_rx_flags + _rb_size.num_pages_tx_flags) * page_size +
51
            (_rb_size.num_rx_frames + _rb_size.num_tx_frames) * _frame_size;
52

    
53
        //print sizes summary
54
        if (sp_verbose){
55
            std::cout << "page_size:          " << page_size                   << std::endl;
56
            std::cout << "frame_size:         " << _frame_size                 << std::endl;
57
            std::cout << "num_pages_rx_flags: " << _rb_size.num_pages_rx_flags << std::endl;
58
            std::cout << "num_rx_frames:      " << _rb_size.num_rx_frames      << std::endl;
59
            std::cout << "num_pages_tx_flags: " << _rb_size.num_pages_tx_flags << std::endl;
60
            std::cout << "num_tx_frames:      " << _rb_size.num_tx_frames      << std::endl;
61
            std::cout << "map_size:           " << _map_size                   << std::endl;
62
        }
63

    
64
        //call mmap to get the memory
65
        _mapped_mem = ::mmap(
66
            NULL, _map_size, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0
67
        );
68
        UHD_ASSERT_THROW(_mapped_mem != MAP_FAILED);
69

    
70
        //calculate the memory offsets for info and buffers
71
        size_t recv_info_off = 0;
72
        size_t recv_buff_off = recv_info_off + (_rb_size.num_pages_rx_flags * page_size);
73
        size_t send_info_off = recv_buff_off + (_rb_size.num_rx_frames * _frame_size);
74
        size_t send_buff_off = send_info_off + (_rb_size.num_pages_tx_flags * page_size);
75

    
76
        //print offset summary
77
        if (sp_verbose){
78
            std::cout << "recv_info_off: " << recv_info_off << std::endl;
79
            std::cout << "recv_buff_off: " << recv_buff_off << std::endl;
80
            std::cout << "send_info_off: " << send_info_off << std::endl;
81
            std::cout << "send_buff_off: " << send_buff_off << std::endl;
82
        }
83

    
84
        //set the internal pointers for info and buffers
85
        typedef ring_buffer_info (*rbi_pta)[];
86
        char *rb_ptr = reinterpret_cast<char *>(_mapped_mem);
87
        _recv_info = reinterpret_cast<rbi_pta>(rb_ptr + recv_info_off);
88
        _recv_buff = rb_ptr + recv_buff_off;
89
        _send_info = reinterpret_cast<rbi_pta>(rb_ptr + send_info_off);
90
        _send_buff = rb_ptr + send_buff_off;
91
    }
92

    
93
    ~usrp_e100_mmap_zero_copy_impl(void){
94
        if (sp_verbose) std::cout << "cleanup: munmap" << std::endl;
95
        ::munmap(_mapped_mem, _map_size);
96
    }
97

    
98
    managed_recv_buffer::sptr get_recv_buff(double timeout){
99
        if (fp_verbose) std::cout << "get_recv_buff: " << _recv_index << std::endl;
100

    
101
        //grab pointers to the info and buffer
102
        ring_buffer_info *info = (*_recv_info) + _recv_index;
103
        void *mem = _recv_buff + _frame_size*_recv_index;
104

    
105
        //poll/wait for a ready frame
106
        if (not (info->flags & RB_USER)){
107
            for (size_t i = 0; i < poll_breakout; i++){
108
                pollfd pfd;
109
                pfd.fd = _fd;
110
                pfd.events = POLLIN;
111
                ssize_t poll_ret = ::poll(&pfd, 1, size_t(timeout*1e3/poll_breakout));
112
                if (fp_verbose) std::cout << "  POLLIN: " << poll_ret << std::endl;
113
                if (poll_ret > 0) goto found_user_frame; //good poll, continue on
114
            }
115
            return managed_recv_buffer::sptr(); //timed-out for real
116
        } found_user_frame:
117

    
118
        //the process has claimed the frame
119
        info->flags = RB_USER_PROCESS;
120

    
121
        //increment the index for the next call
122
        if (++_recv_index == size_t(_rb_size.num_rx_frames)) _recv_index = 0;
123

    
124
        //return the managed buffer for this frame
125
        if (fp_verbose) std::cout << "  make_recv_buff: " << info->len << std::endl;
126
        return managed_recv_buffer::make_safe(
127
            boost::asio::const_buffer(mem, info->len),
128
            boost::bind(&usrp_e100_mmap_zero_copy_impl::release, this, info)
129
        );
130
    }
131

    
132
    size_t get_num_recv_frames(void) const{
133
        return _rb_size.num_rx_frames;
134
    }
135

    
136
    size_t get_recv_frame_size(void) const{
137
        return _frame_size;
138
    }
139

    
140
    managed_send_buffer::sptr get_send_buff(double timeout){
141
        if (fp_verbose) std::cout << "get_send_buff: " << _send_index << std::endl;
142

    
143
        //grab pointers to the info and buffer
144
        ring_buffer_info *info = (*_send_info) + _send_index;
145
        void *mem = _send_buff + _frame_size*_send_index;
146

    
147
        //poll/wait for a ready frame
148
        if (not (info->flags & RB_KERNEL)){
149
            pollfd pfd;
150
            pfd.fd = _fd;
151
            pfd.events = POLLOUT;
152
            ssize_t poll_ret = ::poll(&pfd, 1, size_t(timeout*1e3));
153
            if (fp_verbose) std::cout << "  POLLOUT: " << poll_ret << std::endl;
154
            if (poll_ret <= 0) return managed_send_buffer::sptr();
155
        }
156

    
157
        //increment the index for the next call
158
        if (++_send_index == size_t(_rb_size.num_tx_frames)) _send_index = 0;
159

    
160
        //return the managed buffer for this frame
161
        if (fp_verbose) std::cout << "  make_send_buff: " << _frame_size << std::endl;
162
        return managed_send_buffer::make_safe(
163
            boost::asio::mutable_buffer(mem, _frame_size),
164
            boost::bind(&usrp_e100_mmap_zero_copy_impl::commit, this, info, _1)
165
        );
166
    }
167

    
168
    size_t get_num_send_frames(void) const{
169
        return _rb_size.num_tx_frames;
170
    }
171

    
172
    size_t get_send_frame_size(void) const{
173
        return _frame_size;
174
    }
175

    
176
private:
177

    
178
    void release(ring_buffer_info *info){
179
        if (fp_verbose) std::cout << "recv buff: release" << std::endl;
180
        info->flags = RB_KERNEL;
181
    }
182

    
183
    void commit(ring_buffer_info *info, size_t len){
184
        if (fp_verbose) std::cout << "send buff: commit " << len << std::endl;
185
        info->len = len;
186
        info->flags = RB_USER;
187
        if (::write(_fd, NULL, 0) < 0){
188
            std::cerr << UHD_THROW_SITE_INFO("write error") << std::endl;
189
        }
190
    }
191

    
192
    int _fd;
193

    
194
    //the mapped memory itself
195
    void *_mapped_mem;
196

    
197
    //mapped memory sizes
198
    usrp_e_ring_buffer_size_t _rb_size;
199
    size_t _frame_size, _map_size;
200

    
201
    //pointers to sections in the mapped memory
202
    ring_buffer_info (*_recv_info)[], (*_send_info)[];
203
    char *_recv_buff, *_send_buff;
204

    
205
    //indexes into sub-sections of mapped memory
206
    size_t _recv_index, _send_index;
207
};
208

    
209
/***********************************************************************
210
 * The zero copy interface make function
211
 **********************************************************************/
212
zero_copy_if::sptr usrp_e100_make_mmap_zero_copy(usrp_e100_iface::sptr iface){
213
    return zero_copy_if::sptr(new usrp_e100_mmap_zero_copy_impl(iface));
214
}