remove special last_line adjustment from ethernet port
[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   sm->last_line_adj = 1;
67
68   buffer_state[sm->buf0] = BS_EMPTY;
69   buffer_state[sm->buf0 ^ 1] = BS_EMPTY;
70
71   sm->precomputed_receive_to_buf_ctrl_word[0] =
72     (BPC_READ
73      | BPC_BUFFER(sm->buf0)
74      | BPC_PORT(sm->recv_args.port)
75      | BPC_STEP(1)
76      | BPC_FIRST_LINE(sm->recv_args.first_line)
77      | BPC_LAST_LINE(sm->recv_args.last_line));
78     
79   sm->precomputed_receive_to_buf_ctrl_word[1] =
80     (BPC_READ
81      | BPC_BUFFER(sm->buf0 ^ 1)
82      | BPC_PORT(sm->recv_args.port)
83      | BPC_STEP(1)
84      | BPC_FIRST_LINE(sm->recv_args.first_line)
85      | BPC_LAST_LINE(sm->recv_args.last_line));
86     
87   sm->precomputed_send_from_buf_ctrl_word[0] =
88     (BPC_WRITE
89      | BPC_BUFFER(sm->buf0)
90      | BPC_PORT(sm->send_args.port)
91      | BPC_STEP(1)
92      | BPC_FIRST_LINE(sm->send_args.first_line)
93      | BPC_LAST_LINE(0));               // last line filled in at runtime
94     
95   sm->precomputed_send_from_buf_ctrl_word[1] =
96     (BPC_WRITE
97      | BPC_BUFFER(sm->buf0 ^ 1)
98      | BPC_PORT(sm->send_args.port)
99      | BPC_STEP(1)
100      | BPC_FIRST_LINE(sm->send_args.first_line)
101      | BPC_LAST_LINE(0));               // last line filled in at runtime
102     
103 }
104
105 static inline void
106 dbsm_receive_to_buf(dbsm_t *sm, int bufno)
107 {
108   buffer_pool_ctrl->ctrl = sm->precomputed_receive_to_buf_ctrl_word[bufno & 1];
109 }
110
111 static inline void
112 dbsm_send_from_buf(dbsm_t *sm, int bufno)
113 {
114   buffer_pool_ctrl->ctrl =
115     (sm->precomputed_send_from_buf_ctrl_word[bufno & 1]
116      | BPC_LAST_LINE(buffer_pool_status->last_line[bufno] - sm->last_line_adj));
117 }
118
119 void
120 dbsm_start(dbsm_t *sm)
121 {
122   // printf("dbsm_start: buf0 = %d, recv_port = %d\n", sm->buf0, sm->recv_args.port);
123
124   sm->running = true;
125
126   buffer_state[sm->buf0] = BS_EMPTY;
127   buffer_state[sm->buf0 ^ 1] = BS_EMPTY;
128
129   bp_clear_buf(sm->buf0);
130   bp_clear_buf(sm->buf0 ^ 1);
131
132   sm->tx_idle = true;
133   sm->rx_idle = false;
134   dbsm_receive_to_buf(sm, sm->buf0);
135   buffer_state[sm->buf0] = BS_FILLING;
136
137 }
138
139
140 void
141 dbsm_stop(dbsm_t *sm)
142 {
143   sm->running = false;
144   bp_clear_buf(sm->buf0);
145   bp_clear_buf(sm->buf0 ^ 1);
146   buffer_state[sm->buf0] = BS_EMPTY;
147   buffer_state[sm->buf0 ^ 1] = BS_EMPTY;
148 }
149
150 static void dbsm_process_helper(dbsm_t *sm, int buf_this);
151 static void dbsm_error_helper(dbsm_t *sm, int buf_this);
152
153 void
154 dbsm_process_status(dbsm_t *sm, uint32_t status)
155 {
156   if (!sm->running)
157     return;
158
159   if (status & (BPS_ERROR(sm->buf0) | BPS_ERROR(sm->buf0 ^ 1))){
160     putchar('E');
161     // Most likely an ethernet Rx error.  We just restart the transfer.
162     if (status & (BPS_ERROR(sm->buf0)))
163       dbsm_error_helper(sm, sm->buf0);
164
165     if (status & (BPS_ERROR(sm->buf0 ^ 1)))
166       dbsm_error_helper(sm, sm->buf0 ^ 1);
167   }
168
169   if (status & BPS_DONE(sm->buf0))
170     dbsm_process_helper(sm, sm->buf0);
171
172   if (status & BPS_DONE(sm->buf0 ^ 1))
173     dbsm_process_helper(sm, sm->buf0 ^ 1);
174 }
175
176 static void
177 dbsm_process_helper(dbsm_t *sm, int buf_this)
178 {
179   int buf_other = buf_this ^ 1;
180
181   bp_clear_buf(buf_this);
182
183   if (buffer_state[buf_this] == BS_FILLING){
184     buffer_state[buf_this] = BS_FULL;
185     //
186     // does s/w handle this packet?
187     //
188     if (sm->inspect(sm, buf_this)){
189       // s/w handled the packet; refill the buffer
190       dbsm_receive_to_buf(sm, buf_this);
191       buffer_state[buf_this] = BS_FILLING;
192     }
193
194     else {      // s/w didn't handle this; pass it on
195
196       if(buffer_state[buf_other] == BS_EMPTY){
197         dbsm_receive_to_buf(sm, buf_other);
198         buffer_state[buf_other] = BS_FILLING;
199       }
200       else
201         sm->rx_idle = true;
202
203       if (sm->tx_idle){
204         sm->tx_idle = false;
205         dbsm_send_from_buf(sm, buf_this);
206         buffer_state[buf_this] = BS_EMPTYING;
207       }
208     }
209   }
210   else {  // buffer was emptying
211     buffer_state[buf_this] = BS_EMPTY;
212     if (sm->rx_idle){
213       sm->rx_idle = false;
214       dbsm_receive_to_buf(sm, buf_this);
215       buffer_state[buf_this] = BS_FILLING;
216     }
217     if (buffer_state[buf_other] == BS_FULL){
218       dbsm_send_from_buf(sm, buf_other);
219       buffer_state[buf_other] = BS_EMPTYING;
220     }
221     else
222       sm->tx_idle = true;
223   }
224 }
225
226 static void
227 dbsm_error_helper(dbsm_t *sm, int buf_this)
228 {
229   bp_clear_buf(buf_this);               // clears ERROR flag
230
231   if (buffer_state[buf_this] == BS_FILLING){
232     dbsm_receive_to_buf(sm, buf_this);    // restart the xfer
233   }
234   else { // buffer was emptying
235     dbsm_send_from_buf(sm, buf_this);     // restart the xfer
236   }
237 }
238
239 /*
240  * Handle DSP Tx underrun
241  */
242 void
243 dbsm_handle_tx_underrun(dbsm_t *sm)
244 {
245   // clear the DSP Tx state machine
246   dsp_tx_regs->clear_state = 1;
247
248   // If there's a buffer that's empyting, clear it & flush xfer
249
250   if (buffer_state[sm->buf0] == BS_EMPTYING){
251     bp_clear_buf(sm->buf0);
252     dsp_tx_regs->clear_state = 1;       // flush partial packet
253     // drop frame in progress on ground.  Pretend it finished
254     dbsm_process_helper(sm, sm->buf0);
255   }
256   else if (buffer_state[sm->buf0 ^ 1] == BS_EMPTYING){
257     bp_clear_buf(sm->buf0 ^ 1);
258     dsp_tx_regs->clear_state = 1;       // flush partial packet
259     // drop frame in progress on ground.  Pretend it finished
260     dbsm_process_helper(sm, sm->buf0 ^ 1);
261   }
262 }
263
264 /*
265  * Handle DSP Rx overrun
266  */
267 void
268 dbsm_handle_rx_overrun(dbsm_t *sm)
269 {
270   dsp_rx_regs->clear_state = 1;
271
272   // If there's a buffer that's filling, clear it.
273   // Any restart will be the job of the caller.
274   
275   if (buffer_state[sm->buf0] == BS_FILLING)
276     bp_clear_buf(sm->buf0);
277
278   if (buffer_state[sm->buf0 ^1] == BS_FILLING)
279     bp_clear_buf(sm->buf0 ^ 1);
280 }
281
282 void 
283 dbsm_wait_for_opening(dbsm_t *sm)
284 {
285   if (buffer_state[sm->buf0] == BS_EMPTYING){
286     // wait for xfer to complete
287     int mask = BPS_DONE(sm->buf0) | BPS_ERROR(sm->buf0) | BPS_IDLE(sm->buf0);
288     while ((buffer_pool_status->status & mask) == 0)
289       ;
290   }
291   else if (buffer_state[sm->buf0 ^ 1] == BS_EMPTYING){
292     // wait for xfer to complete
293     int mask = BPS_DONE(sm->buf0 ^ 1) | BPS_ERROR(sm->buf0 ^ 1) | BPS_IDLE(sm->buf0 ^ 1);
294     while ((buffer_pool_status->status & mask) == 0)
295       ;
296   }
297 }