Imported Upstream version 3.2.2
[debian/gnuradio] / usrp2 / firmware / lib / dbsm.c
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2007,2008 Free Software Foundation, Inc.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 /*
20  * Double Buffering State Machine
21  */
22
23 #include "dbsm.h"
24 #include "memory_map.h"
25 #include "buffer_pool.h"
26 #include "bool.h"
27 #include "nonstdio.h"
28 #include <stdlib.h>
29
30 typedef enum {
31   BS_EMPTY,
32   BS_FILLING,
33   BS_FULL,
34   BS_EMPTYING,
35 } buffer_state_t;
36
37 static buffer_state_t buffer_state[NBUFFERS];
38
39 bool
40 dbsm_nop_inspector(dbsm_t *sm, int buf_this)
41 {
42   return false;
43 }
44
45 void
46 dbsm_init(dbsm_t *sm, int buf0,
47           const buf_cmd_args_t *recv, const buf_cmd_args_t *send,
48           inspector_t inspect)
49 {
50   if (buf0 & 0x1)       // must be even
51     abort();
52
53   sm->buf0 = buf0;
54   sm->running = false;
55   sm->recv_args = *recv;
56   sm->send_args = *send;
57
58   sm->rx_idle = true;
59   sm->tx_idle = true;
60
61   sm->inspect = inspect;
62
63   // How much to adjust the last_line register.
64   // It's 1 for everything but the ethernet.
65   sm->last_line_adj = recv->port == PORT_ETH ? 3 : 1;
66
67   buffer_state[sm->buf0] = BS_EMPTY;
68   buffer_state[sm->buf0 ^ 1] = BS_EMPTY;
69
70   sm->precomputed_receive_to_buf_ctrl_word[0] =
71     (BPC_READ
72      | BPC_BUFFER(sm->buf0)
73      | BPC_PORT(sm->recv_args.port)
74      | BPC_STEP(1)
75      | BPC_FIRST_LINE(sm->recv_args.first_line)
76      | BPC_LAST_LINE(sm->recv_args.last_line));
77     
78   sm->precomputed_receive_to_buf_ctrl_word[1] =
79     (BPC_READ
80      | BPC_BUFFER(sm->buf0 ^ 1)
81      | BPC_PORT(sm->recv_args.port)
82      | BPC_STEP(1)
83      | BPC_FIRST_LINE(sm->recv_args.first_line)
84      | BPC_LAST_LINE(sm->recv_args.last_line));
85     
86   sm->precomputed_send_from_buf_ctrl_word[0] =
87     (BPC_WRITE
88      | BPC_BUFFER(sm->buf0)
89      | BPC_PORT(sm->send_args.port)
90      | BPC_STEP(1)
91      | BPC_FIRST_LINE(sm->send_args.first_line)
92      | BPC_LAST_LINE(0));               // last line filled in at runtime
93     
94   sm->precomputed_send_from_buf_ctrl_word[1] =
95     (BPC_WRITE
96      | BPC_BUFFER(sm->buf0 ^ 1)
97      | BPC_PORT(sm->send_args.port)
98      | BPC_STEP(1)
99      | BPC_FIRST_LINE(sm->send_args.first_line)
100      | BPC_LAST_LINE(0));               // last line filled in at runtime
101     
102 }
103
104 static inline void
105 dbsm_receive_to_buf(dbsm_t *sm, int bufno)
106 {
107   buffer_pool_ctrl->ctrl = sm->precomputed_receive_to_buf_ctrl_word[bufno & 1];
108 }
109
110 static inline void
111 dbsm_send_from_buf(dbsm_t *sm, int bufno)
112 {
113   buffer_pool_ctrl->ctrl =
114     (sm->precomputed_send_from_buf_ctrl_word[bufno & 1]
115      | BPC_LAST_LINE(buffer_pool_status->last_line[bufno] - sm->last_line_adj));
116 }
117
118 void
119 dbsm_start(dbsm_t *sm)
120 {
121   // printf("dbsm_start: buf0 = %d, recv_port = %d\n", sm->buf0, sm->recv_args.port);
122
123   sm->running = true;
124
125   buffer_state[sm->buf0] = BS_EMPTY;
126   buffer_state[sm->buf0 ^ 1] = BS_EMPTY;
127
128   bp_clear_buf(sm->buf0);
129   bp_clear_buf(sm->buf0 ^ 1);
130
131   sm->tx_idle = true;
132   sm->rx_idle = false;
133   dbsm_receive_to_buf(sm, sm->buf0);
134   buffer_state[sm->buf0] = BS_FILLING;
135
136 }
137
138
139 void
140 dbsm_stop(dbsm_t *sm)
141 {
142   sm->running = false;
143   bp_clear_buf(sm->buf0);
144   bp_clear_buf(sm->buf0 ^ 1);
145   buffer_state[sm->buf0] = BS_EMPTY;
146   buffer_state[sm->buf0 ^ 1] = BS_EMPTY;
147 }
148
149 static void dbsm_process_helper(dbsm_t *sm, int buf_this);
150 static void dbsm_error_helper(dbsm_t *sm, int buf_this);
151
152 void
153 dbsm_process_status(dbsm_t *sm, uint32_t status)
154 {
155   if (!sm->running)
156     return;
157
158   if (status & (BPS_ERROR(sm->buf0) | BPS_ERROR(sm->buf0 ^ 1))){
159     putchar('E');
160     // Most likely an ethernet Rx error.  We just restart the transfer.
161     if (status & (BPS_ERROR(sm->buf0)))
162       dbsm_error_helper(sm, sm->buf0);
163
164     if (status & (BPS_ERROR(sm->buf0 ^ 1)))
165       dbsm_error_helper(sm, sm->buf0 ^ 1);
166   }
167
168   if (status & BPS_DONE(sm->buf0))
169     dbsm_process_helper(sm, sm->buf0);
170
171   if (status & BPS_DONE(sm->buf0 ^ 1))
172     dbsm_process_helper(sm, sm->buf0 ^ 1);
173 }
174
175 static void
176 dbsm_process_helper(dbsm_t *sm, int buf_this)
177 {
178   int buf_other = buf_this ^ 1;
179
180   bp_clear_buf(buf_this);
181
182   if (buffer_state[buf_this] == BS_FILLING){
183     buffer_state[buf_this] = BS_FULL;
184     //
185     // does s/w handle this packet?
186     //
187     if (sm->inspect(sm, buf_this)){
188       // s/w handled the packet; refill the buffer
189       dbsm_receive_to_buf(sm, buf_this);
190       buffer_state[buf_this] = BS_FILLING;
191     }
192
193     else {      // s/w didn't handle this; pass it on
194
195       if(buffer_state[buf_other] == BS_EMPTY){
196         dbsm_receive_to_buf(sm, buf_other);
197         buffer_state[buf_other] = BS_FILLING;
198       }
199       else
200         sm->rx_idle = true;
201
202       if (sm->tx_idle){
203         sm->tx_idle = false;
204         dbsm_send_from_buf(sm, buf_this);
205         buffer_state[buf_this] = BS_EMPTYING;
206       }
207     }
208   }
209   else {  // buffer was emptying
210     buffer_state[buf_this] = BS_EMPTY;
211     if (sm->rx_idle){
212       sm->rx_idle = false;
213       dbsm_receive_to_buf(sm, buf_this);
214       buffer_state[buf_this] = BS_FILLING;
215     }
216     if (buffer_state[buf_other] == BS_FULL){
217       dbsm_send_from_buf(sm, buf_other);
218       buffer_state[buf_other] = BS_EMPTYING;
219     }
220     else
221       sm->tx_idle = true;
222   }
223 }
224
225 static void
226 dbsm_error_helper(dbsm_t *sm, int buf_this)
227 {
228   bp_clear_buf(buf_this);               // clears ERROR flag
229
230   if (buffer_state[buf_this] == BS_FILLING){
231     dbsm_receive_to_buf(sm, buf_this);    // restart the xfer
232   }
233   else { // buffer was emptying
234     dbsm_send_from_buf(sm, buf_this);     // restart the xfer
235   }
236 }
237
238 /*
239  * Handle DSP Tx underrun
240  */
241 void
242 dbsm_handle_tx_underrun(dbsm_t *sm)
243 {
244   // clear the DSP Tx state machine
245   dsp_tx_regs->clear_state = 1;
246
247   // If there's a buffer that's empyting, clear it & flush xfer
248
249   if (buffer_state[sm->buf0] == BS_EMPTYING){
250     bp_clear_buf(sm->buf0);
251     dsp_tx_regs->clear_state = 1;       // flush partial packet
252     // drop frame in progress on ground.  Pretend it finished
253     dbsm_process_helper(sm, sm->buf0);
254   }
255   else if (buffer_state[sm->buf0 ^ 1] == BS_EMPTYING){
256     bp_clear_buf(sm->buf0 ^ 1);
257     dsp_tx_regs->clear_state = 1;       // flush partial packet
258     // drop frame in progress on ground.  Pretend it finished
259     dbsm_process_helper(sm, sm->buf0 ^ 1);
260   }
261 }
262
263 /*
264  * Handle DSP Rx overrun
265  */
266 void
267 dbsm_handle_rx_overrun(dbsm_t *sm)
268 {
269   dsp_rx_regs->clear_state = 1;
270
271   // If there's a buffer that's filling, clear it.
272   // Any restart will be the job of the caller.
273   
274   if (buffer_state[sm->buf0] == BS_FILLING)
275     bp_clear_buf(sm->buf0);
276
277   if (buffer_state[sm->buf0 ^1] == BS_FILLING)
278     bp_clear_buf(sm->buf0 ^ 1);
279 }
280
281 void 
282 dbsm_wait_for_opening(dbsm_t *sm)
283 {
284   if (buffer_state[sm->buf0] == BS_EMPTYING){
285     // wait for xfer to complete
286     int mask = BPS_DONE(sm->buf0) | BPS_ERROR(sm->buf0) | BPS_IDLE(sm->buf0);
287     while ((buffer_pool_status->status & mask) == 0)
288       ;
289   }
290   else if (buffer_state[sm->buf0 ^ 1] == BS_EMPTYING){
291     // wait for xfer to complete
292     int mask = BPS_DONE(sm->buf0 ^ 1) | BPS_ERROR(sm->buf0 ^ 1) | BPS_IDLE(sm->buf0 ^ 1);
293     while ((buffer_pool_status->status & mask) == 0)
294       ;
295   }
296 }