3 * Copyright 2009 Free Software Foundation, Inc.
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.
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.
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/>.
20 * buffer state machine: 1 input to two outputs
22 * Typically used to read packets from the ethernet and then after inspecting,
23 * handle the packet in firmware or pass it on to 1 of the 2 buffer destinations.
31 #include "memory_map.h"
32 #include "buffer_pool.h"
44 static buffer_state_t buffer_state[NBUFFERS];
45 static uint32_t last_send_ctrl[NBUFFERS];
46 static int8_t buffer_target[NBUFFERS]; // -1, 0 or 1.
47 static uint8_t buffer_dst[NBUFFERS]; // 0 or 1. Valid only when BF_EMPTYING
52 bsm12_init(bsm12_t *sm, int buf0,
53 const buf_cmd_args_t *recv,
54 const buf_cmd_args_t *send0,
55 const buf_cmd_args_t *send1,
56 bsm12_inspector_t inspect)
58 if (buf0 & 0x3) // precondition: buf0 % 4 == 0
63 sm->recv_args = *recv;
64 sm->send_args[0] = *send0;
65 sm->send_args[1] = *send1;
67 sm->rx_state = ST_IDLE;
68 sm->tx_state[0] = ST_IDLE;
69 sm->tx_state[1] = ST_IDLE;
71 sm->inspect = inspect;
73 sm->bps_error = BPS_ERROR(buf0 + 0) | BPS_ERROR(buf0 + 1) | BPS_ERROR(buf0 + 2);
74 sm->bps_done = BPS_DONE(buf0 + 0) | BPS_DONE(buf0 + 1) | BPS_DONE(buf0 + 2);
75 sm->bps_error_or_done = sm->bps_error | sm->bps_done;
77 // How much to adjust the last_line register.
78 // It's 1 for everything but the ethernet.
79 sm->last_line_adj = recv->port == PORT_ETH ? 3 : 1;
81 buffer_state[sm->buf0 + 0] = BS_EMPTY;
82 buffer_state[sm->buf0 + 1] = BS_EMPTY;
83 buffer_state[sm->buf0 + 2] = BS_EMPTY;
85 buffer_target[sm->buf0 + 0] = -1;
86 buffer_target[sm->buf0 + 1] = -1;
87 buffer_target[sm->buf0 + 2] = -1;
89 for (int i = 0; i < NBUFFERS; i++)
90 sm->next_buf[i] = buf0;
92 sm->next_buf[buf0 + 0] = buf0 + 1;
93 sm->next_buf[buf0 + 1] = buf0 + 2;
94 sm->next_buf[buf0 + 2] = buf0 + 0;
96 for (int i = 0; i < 3; i++){
97 sm->precomputed_receive_to_buf_ctrl_word[i] =
99 | BPC_BUFFER(sm->buf0 + i)
100 | BPC_PORT(sm->recv_args.port)
102 | BPC_FIRST_LINE(sm->recv_args.first_line)
103 | BPC_LAST_LINE(sm->recv_args.last_line));
105 for (int j = 0; j < 2; j++){
106 sm->precomputed_send_from_buf_ctrl_word[i][j] =
108 | BPC_BUFFER(sm->buf0 + i)
109 | BPC_PORT(sm->send_args[j].port)
111 | BPC_FIRST_LINE(sm->send_args[j].first_line)
112 | BPC_LAST_LINE(0)); // last line filled in at runtime
118 bsm12_receive_to_buf(bsm12_t *sm, int bufno)
120 buffer_pool_ctrl->ctrl = sm->precomputed_receive_to_buf_ctrl_word[bufno & 0x3];
124 bsm12_send_from_buf(bsm12_t *sm, int bufno, int dst_idx)
129 (sm->precomputed_send_from_buf_ctrl_word[bufno & 0x3][dst_idx]
130 | BPC_LAST_LINE(buffer_pool_status->last_line[bufno] - sm->last_line_adj));
132 buffer_pool_ctrl->ctrl = t;
133 last_send_ctrl[bufno] = t;
134 buffer_dst[bufno] = dst_idx;
138 bsm12_resend_from_buf(bsm12_t *sm, int bufno)
140 buffer_pool_ctrl->ctrl = last_send_ctrl[bufno];
144 bsm12_start(bsm12_t *sm)
148 buffer_state[sm->buf0 + 0] = BS_EMPTY;
149 buffer_state[sm->buf0 + 1] = BS_EMPTY;
150 buffer_state[sm->buf0 + 2] = BS_EMPTY;
152 buffer_target[sm->buf0 + 0] = -1;
153 buffer_target[sm->buf0 + 1] = -1;
154 buffer_target[sm->buf0 + 2] = -1;
156 bp_clear_buf(sm->buf0 + 0);
157 bp_clear_buf(sm->buf0 + 1);
158 bp_clear_buf(sm->buf0 + 2);
161 sm->tx_state[0] = ST_IDLE;
162 sm->tx_state[1] = ST_IDLE;
163 bsm12_receive_to_buf(sm, sm->buf0);
164 buffer_state[sm->buf0] = BS_FILLING;
168 bsm12_stop(bsm12_t *sm)
171 bp_clear_buf(sm->buf0 + 0);
172 bp_clear_buf(sm->buf0 + 1);
173 bp_clear_buf(sm->buf0 + 2);
174 buffer_state[sm->buf0 + 0] = BS_EMPTY;
175 buffer_state[sm->buf0 + 1] = BS_EMPTY;
176 buffer_state[sm->buf0 + 2] = BS_EMPTY;
179 static void bsm12_process_helper(bsm12_t *sm, int buf_this);
180 static void bsm12_error_helper(bsm12_t *sm, int buf_this);
183 bsm12_process_status(bsm12_t *sm, uint32_t status)
186 if ((status & sm->bps_error_or_done) == 0 || !sm->running)
189 if (status & sm->bps_error){
190 // Most likely an ethernet Rx error. We just restart the transfer.
191 if (status & (BPS_ERROR(sm->buf0 + 0)))
192 bsm12_error_helper(sm, sm->buf0 + 0);
194 if (status & (BPS_ERROR(sm->buf0 + 1)))
195 bsm12_error_helper(sm, sm->buf0 + 1);
197 if (status & (BPS_ERROR(sm->buf0 + 2)))
198 bsm12_error_helper(sm, sm->buf0 + 2);
201 if (status & BPS_DONE(sm->buf0 + 0))
202 bsm12_process_helper(sm, sm->buf0 + 0);
204 if (status & BPS_DONE(sm->buf0 + 1))
205 bsm12_process_helper(sm, sm->buf0 + 1);
207 if (status & BPS_DONE(sm->buf0 + 2))
208 bsm12_process_helper(sm, sm->buf0 + 2);
212 bsm12_process_helper(bsm12_t *sm, int buf_this)
214 bp_clear_buf(buf_this);
216 if (buffer_state[buf_this] == BS_FILLING){
218 buffer_state[buf_this] = BS_FULL;
219 buffer_target[buf_this] = -1;
222 // where does this packet go?
224 int dst = sm->inspect(sm, buf_this);
227 // f/w handled the packet; refill the buffer
229 bsm12_receive_to_buf(sm, buf_this);
230 buffer_state[buf_this] = BS_FILLING;
231 buffer_target[buf_this] = -1;
232 sm->rx_state = buf_this & 0x3;
234 else { // goes to dst 0 or 1
236 // If the next buffer is empty, start a receive on it
238 int t = sm->next_buf[buf_this];
239 if (buffer_state[t] == BS_EMPTY){
240 bsm12_receive_to_buf(sm, t);
241 buffer_state[t] = BS_FILLING;
242 buffer_target[t] = -1;
243 sm->rx_state = t & 0x3;
246 sm->rx_state = ST_IDLE;
249 // If the destination is idle, start the xfer, othewise remember it
251 if (sm->tx_state[dst] == ST_IDLE){
252 bsm12_send_from_buf(sm, buf_this, dst);
253 sm->tx_state[dst] = buf_this & 0x3;
254 buffer_state[buf_this] = BS_EMPTYING;
255 buffer_target[buf_this] = -1;
258 buffer_target[buf_this] = dst;
263 else { // BS_EMPTYING
265 buffer_state[buf_this] = BS_EMPTY;
266 buffer_target[buf_this] = -1;
268 if (sm->rx_state == ST_IDLE){ // fire off another receive
269 sm->rx_state = buf_this & 0x3;
270 bsm12_receive_to_buf(sm, buf_this);
271 buffer_state[buf_this] = BS_FILLING;
272 buffer_target[buf_this] = -1;
275 int dst = buffer_dst[buf_this]; // the dst we were emptying into
276 // is the next buffer full and for us?
277 int t = sm->next_buf[buf_this];
278 if (buffer_target[t] == dst){ // yes,
279 bsm12_send_from_buf(sm, t, dst); // send it
280 buffer_state[t] = BS_EMPTYING;
281 buffer_target[t] = -1;
282 sm->tx_state[dst] = t & 0x3;
284 // how about the one after that?
285 else if (buffer_target[t=sm->next_buf[t]] == dst){ // yes,
286 bsm12_send_from_buf(sm, t, dst); // send it
287 buffer_state[t] = BS_EMPTYING;
288 buffer_target[t] = -1;
289 sm->tx_state[dst] = t & 0x3;
292 sm->tx_state[dst] = ST_IDLE;
298 bsm12_error_helper(bsm12_t *sm, int buf_this)
300 bp_clear_buf(buf_this); // clears ERROR flag
302 if (buffer_state[buf_this] == BS_FILLING){
303 bsm12_receive_to_buf(sm, buf_this); // restart the xfer
305 else { // buffer was emptying
306 bsm12_resend_from_buf(sm, buf_this); // restart the xfer
312 bsm12_handle_tx_underrun(bsm12_t *sm)
317 bsm12_handle_rx_overrun(bsm12_t *sm)