1 /*
2 * Copyright (c) 2023 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /*
8 * Backend for the UART(E) which connects to a compatible backend on another device.
9 *
10 * It accounts for the RTS|R/CTS flow control, and byte timing.
11 *
12 * Mismatches in the UART configuration and baudrate, only result in a warning
13 * but the data is accepted without generating error events.
14 *
15 * When selected it *requires* the other end to be connected as soon as the UART HW is enabled.
16 *
17 * Notes:
18 * * This backend is heavy/slow, it is very strongly recommended to NOT use it
19 * in 2 devices which are also connected thru the Phy.
20 * It is also discouraged to use it for anything beyond testing of UART related code.
21 *
22 * * The protocol this backend uses does not follow any standard, and it is not
23 * an API with any stability promise, if you connect your own UART model to its FIFO
24 * you should expect that to break eventually.
25 *
26 * * When both Tx and Rx are off, CTS toggles will not generate the events in the right
27 * time but those events will be generated in bursts (as we are not monitoring constantly the input)
28 * Therefore for the UART(not E), a toggle of CTS will not trigger the StartRx/StopRx
29 * shortcuts in the right time.
30 */
31
32 #include <stdint.h>
33 #include <string.h>
34 #include <signal.h>
35 #include <errno.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <unistd.h>
40 #include <math.h>
41 #include "bs_types.h"
42 #include "bs_tracing.h"
43 #include "bs_oswrap.h"
44 #include "bs_utils.h"
45 #include "bs_cmd_line.h"
46 #include "bs_dynargs.h"
47 #include "bs_pc_base_fifo_user.h"
48 #include "NHW_config.h"
49 #include "NHW_peri_types.h"
50 #include "NHW_UART_backend_if.h"
51 #include "NHW_UART_backend_fifo.h"
52 #include "nsi_hw_scheduler.h"
53 #include "nsi_tasks.h"
54 #include "nsi_hws_models_if.h"
55
56 static bs_time_t Timer_UFIFO = TIME_NEVER;
57
58 /* Factor for the fastest reaction time to propagate an event to the other side
59 * relative to a frame (byte transfer time) size.
60 * This reaction time is frame_time / TX_MIN_DELTA_RATIO.
61 * (Note the smallest byte time is 10 micros at 1Mbps no parity and 1 stop bit,
62 * so for TX_MIN_DELTA_RATIO = 2 that is 5 micros )
63 * The higher this value, the more accurate the flow control timing will be.
64 * Byte timing will always be fully accurate.
65 * But the higher this value the higher the processing overhead.
66 */
67 #define TX_MIN_DELTA_RATIO 1
68
69 /* Relevant bits in CONFIG register from the frame format POV
70 * HWFC does not matter
71 * Neither FRAMETIMEOUT or ENDIAN for 54
72 */
73 #define CONFIG_RELEVANT_MASK 0x3FFE
74
75 struct line_params {
76 uint32_t config;
77 uint32_t baud;
78 };
79
80 struct ufifo_st_t {
81 bs_time_t Rx_timer;
82 bs_time_t Tx_timer;
83
84 char *fifo_Tx_path;
85 char *fifo_Rx_path;
86
87 int fifo_tx; /* File descriptor for tx */
88 int fifo_rx; /* File descriptor for rx */
89
90 bool enabled; /* Has it been chosen for this instance in the command line (and not yet disconnected) */
91 bool tx_on;
92 bool rx_on;
93 bool rx_low_dutyc_mode;
94 bool disconnected;
95
96 struct line_params tx_line_params;
97
98 char last_rx_msg[UFIFO_BIGGEST_MSG];
99 bool last_rx_msg_pending; /* The message in last_rx_msg is between pre and post processing (1)
100 or already processed (0) */
101
102
103 struct line_params rx_line_params;
104 bool cts; /* CTS signal we are getting from other device (starts high => not clear) */
105 uint16_t rx_byte;
106 };
107
108 struct ufifo_st_t ufifo_st[NHW_UARTE_TOTAL_INST];
109
110 static bool uf_dont_terminate_on_disconnect;
111 static double uf_mdt;
112
113 static void nhw_ufifo_update_timer(void);
114 static void nhw_ufifo_create_fifos(uint inst, struct ufifo_st_t *u_el);
115 static void nhw_ufifo_tx_byte(uint inst, uint16_t data);
116 static void nhw_ufifo_RTS_pin_toggle(uint inst, bool new_level);
117 static void nhw_ufifo_enable_notify(uint inst, uint8_t tx_enabled, uint8_t rx_enabled);
118 static void uf_Rx_handle_old_input(uint inst, struct ufifo_st_t *u_el);
119
nhw_ufifo_backend_init(void)120 static void nhw_ufifo_backend_init(void) {
121
122 /* Let's make sure we ignore broken pipe signals even if we are not connected to a Phy*/
123 signal(SIGPIPE, SIG_IGN);
124
125 for (int i = 0; i < NHW_UARTE_TOTAL_INST; i++) {
126 struct ufifo_st_t *u_el = &ufifo_st[i];
127 u_el->Rx_timer = TIME_NEVER;
128 u_el->Tx_timer = TIME_NEVER;
129
130 u_el->fifo_tx = -1;
131 u_el->fifo_rx = -1;
132
133 u_el->tx_line_params.config = UINT32_MAX;
134 u_el->tx_line_params.baud = UINT32_MAX;
135
136 u_el->rx_line_params.config = UINT32_MAX;
137 u_el->rx_line_params.baud = UINT32_MAX;
138
139 u_el->cts = true;
140
141 if (!u_el->enabled) {
142 continue;
143 }
144 nhw_ufifo_create_fifos(i, u_el);
145
146 struct backend_if st;
147 st.tx_byte_f = nhw_ufifo_tx_byte;
148 st.RTS_pin_toggle_f = nhw_ufifo_RTS_pin_toggle;
149 st.uart_enable_notify_f = nhw_ufifo_enable_notify;
150
151 nhw_UARTE_backend_register(i, &st);
152 nhw_UARTE_CTS_raised(i);
153
154 u_el->Tx_timer = 0;
155 u_el->Rx_timer = uf_mdt;
156 u_el->rx_low_dutyc_mode = true;
157 }
158 nhw_ufifo_update_timer();
159 }
160
161 NSI_TASK(nhw_ufifo_backend_init, HW_INIT, 100); /* this must be before the uart itself */
162
write_to_tx_fifo(struct ufifo_st_t * u_el,void * ptr,size_t size)163 static void write_to_tx_fifo(struct ufifo_st_t *u_el, void *ptr, size_t size) {
164 int res = write(u_el->fifo_tx, ptr, size);
165
166 if (res != (int)size) {
167 u_el->disconnected = true;
168 if ((res == -1) && (errno == EPIPE)) {
169 /* The other side disconnected unexpectedly, let's terminate */
170 bs_trace_exit_time_line("UART: Other end disconnected from its Rx FIFO, terminating\n");
171 } else {
172 bs_trace_error_time_line("UART: Unexpected error while writing (%i, %i) (or interrupted). "
173 "Terminating\n", res, errno);
174 }
175 }
176 }
177
tx_set_next_tx_timer(struct ufifo_st_t * u_el,bs_time_t last_act)178 static void tx_set_next_tx_timer(struct ufifo_st_t *u_el, bs_time_t last_act) {
179 u_el->Tx_timer = BS_MAX(last_act, u_el->Tx_timer);
180 nhw_ufifo_update_timer();
181 }
182
tx_sync_line_params(uint inst,struct ufifo_st_t * u_el)183 static void tx_sync_line_params(uint inst, struct ufifo_st_t *u_el) {
184 u_el->tx_line_params.config = NRF_UARTE_regs[inst].CONFIG & CONFIG_RELEVANT_MASK;
185 u_el->tx_line_params.baud = NRF_UARTE_regs[inst].BAUDRATE;
186
187 struct ufifo_msg_mode_change msg;
188 msg.header.time = nsi_hws_get_time();
189 msg.header.size = sizeof(msg);
190 msg.header.msg_type = ufifo_MODE_CHANGE;
191 msg.config = u_el->tx_line_params.config;
192 msg.baudrate = u_el->tx_line_params.baud;
193
194 write_to_tx_fifo(u_el, (void *)&msg, sizeof(msg));
195 tx_set_next_tx_timer(u_el, msg.header.time);
196 }
197
tx_nop(struct ufifo_st_t * u_el,bs_time_t t)198 static void tx_nop(struct ufifo_st_t *u_el, bs_time_t t) {
199 struct ufifo_msg_header msg;
200 msg.time = t;
201 msg.size = sizeof(msg);
202 msg.msg_type = ufifo_NOP;
203
204 write_to_tx_fifo(u_el, (void *)&msg, sizeof(msg));
205 tx_set_next_tx_timer(u_el, msg.time);
206 }
207
tx_disconnect(struct ufifo_st_t * u_el)208 static void tx_disconnect(struct ufifo_st_t *u_el) {
209 struct ufifo_msg_header msg;
210 msg.time = 0;
211 msg.size = sizeof(msg);
212 msg.msg_type = ufifo_DISCONNECT;
213
214 write_to_tx_fifo(u_el, (void *)&msg, sizeof(msg));
215 }
216
nhw_ufifo_tx_byte(uint inst,uint16_t data)217 static void nhw_ufifo_tx_byte(uint inst, uint16_t data) {
218 struct ufifo_st_t *u_el = &ufifo_st[inst];
219
220 if (!u_el->enabled) {
221 bs_trace_error_time_line("Programming error\n");
222 }
223
224 if (((NRF_UARTE_regs[inst].CONFIG & CONFIG_RELEVANT_MASK) != u_el->tx_line_params.config)
225 || (NRF_UARTE_regs[inst].BAUDRATE != u_el->tx_line_params.baud)) {
226 tx_sync_line_params(inst, u_el);
227 }
228
229 struct ufifo_msg_tx msg;
230 msg.header.time = nsi_hws_get_time() + nhw_uarte_one_byte_time(inst);
231 msg.header.size = sizeof(msg);
232 msg.header.msg_type = ufifo_TX_BYTE;
233 msg.data = data;
234
235 write_to_tx_fifo(u_el, (void *)&msg, sizeof(msg));
236 tx_set_next_tx_timer(u_el, msg.header.time);
237 }
238
nhw_ufifo_RTS_pin_toggle(uint inst,bool new_level)239 static void nhw_ufifo_RTS_pin_toggle(uint inst, bool new_level) {
240 struct ufifo_st_t *u_el = &ufifo_st[inst];
241
242 if (!u_el->enabled) {
243 bs_trace_error_time_line("Progamming error\n");
244 }
245
246 struct ufifo_msg_rts_cts msg;
247
248 msg.header.time = nsi_hws_get_time();
249 msg.header.size = sizeof(msg);
250 msg.header.msg_type = ufifo_RTS_CTS_TOGGLE;
251 msg.level = new_level;
252
253 write_to_tx_fifo(u_el, (void *)&msg, sizeof(msg));
254 tx_set_next_tx_timer(u_el, msg.header.time);
255 }
256
nhw_ufifo_enable_notify(uint inst,uint8_t tx_enabled,uint8_t rx_enabled)257 static void nhw_ufifo_enable_notify(uint inst, uint8_t tx_enabled, uint8_t rx_enabled) {
258 struct ufifo_st_t *u_el = &ufifo_st[inst];
259
260 if (!u_el->enabled) {
261 bs_trace_error_time_line("Programming error\n");
262 }
263
264 u_el->tx_on = tx_enabled;
265 u_el->rx_on = rx_enabled;
266
267 if ((u_el->rx_low_dutyc_mode == true) &&
268 ((tx_enabled == true) || (rx_enabled == true)) ) {
269 /* If we are in low duty cycle mode, let's get out of it right away */
270 u_el->Rx_timer = nsi_hws_get_time();
271 nhw_ufifo_update_timer();
272 }
273 }
274
nhw_ufifo_update_timer(void)275 static void nhw_ufifo_update_timer(void) {
276 Timer_UFIFO = TIME_NEVER;
277 for (int i = 0; i < NHW_UARTE_TOTAL_INST; i++) {
278 struct ufifo_st_t * u_el = &ufifo_st[i];
279 if (!u_el->enabled) {
280 continue;
281 }
282 bs_time_t smaller = BS_MIN(u_el->Rx_timer, u_el->Tx_timer);
283 Timer_UFIFO = BS_MIN(Timer_UFIFO, smaller);
284 }
285 nsi_hws_find_next_event();
286 }
287
uf_propage_cts(uint inst,struct ufifo_st_t * u_el)288 static void uf_propage_cts(uint inst, struct ufifo_st_t *u_el) {
289 if (u_el->cts) {
290 nhw_UARTE_CTS_raised(inst);
291 } else {
292 nhw_UARTE_CTS_lowered(inst);
293 }
294 }
295
uf_rx_lowlevel_read(struct ufifo_st_t * u_el,void * buf,size_t size)296 static int uf_rx_lowlevel_read(struct ufifo_st_t *u_el, void *buf, size_t size) {
297 int ret = read(u_el->fifo_rx, buf, size);
298 if (ret != (int)size) {
299 u_el->disconnected = true;
300 if (ret == 0) {
301 bs_trace_error_time_line("UART: Other end disconnected unexpectedly\n");
302 } else {
303 bs_trace_error_time_line("UART: Read interrupted, or other unexpected error => terminating (%i, %s)\n",
304 ret, strerror(errno));
305 }
306 }
307 return ret;
308 }
309
310 /*
311 * Get one message from the Rx FIFO
312 * And for
313 * NOP: do nothing
314 * MODE_CHANGE: update u_el->rx_line_params
315 * TX_BYTE: copy byte to u_el->rx_byte
316 * RTS_CTS_TOGGLE: update u_el->cts
317 * DISCONNECT: Disconnect this FIFO instance (set timers in never, and disconnect from peripheral)
318 *
319 * returns message type
320 */
uf_rx_get_one_msg(uint inst,struct ufifo_st_t * u_el)321 static int uf_rx_get_one_msg(uint inst, struct ufifo_st_t *u_el) {
322 struct ufifo_msg_header *buf = (struct ufifo_msg_header *)u_el->last_rx_msg;
323
324 uf_rx_lowlevel_read(u_el, (void *)buf, UFIFO_MSG_HEADER_SIZE);
325
326 switch (buf->msg_type) {
327 case ufifo_MODE_CHANGE:
328 uf_rx_lowlevel_read(u_el, (char *)buf + UFIFO_MSG_HEADER_SIZE, UFIFO_MSG_MODE_CHANGE_BODY_SIZE);
329 u_el->rx_line_params.baud = ((struct ufifo_msg_mode_change *)buf)->baudrate;
330 u_el->rx_line_params.config = ((struct ufifo_msg_mode_change *)buf)->config;
331 break;
332 case ufifo_DISCONNECT:
333 u_el->disconnected = true;
334 if (!uf_dont_terminate_on_disconnect) {
335 bs_trace_exit_line_time("UART%i: Other end disconnected. Terminating\n", inst);
336 } else {
337 bs_trace_raw_time(3, "UART%i: Other end disconnected, UART%i backend disabled\n", inst, inst);
338 u_el->enabled = false;
339 u_el->Rx_timer = TIME_NEVER;
340 u_el->Tx_timer = TIME_NEVER;
341 nhw_ufifo_update_timer();
342
343 // Unregister from UART peri this instance so we never get another call:
344 struct backend_if st;
345 memset(&st, 0, sizeof(st));
346 nhw_UARTE_backend_register(inst, &st);
347 }
348 break;
349 case ufifo_TX_BYTE:
350 uf_rx_lowlevel_read(u_el, (char *)buf + UFIFO_MSG_HEADER_SIZE, UFIFO_MSG_TXL_BODY_SIZE);
351 u_el->rx_byte = ((struct ufifo_msg_tx *)buf)->data;
352 break;
353 case ufifo_NOP:
354 break;
355 case ufifo_RTS_CTS_TOGGLE:
356 uf_rx_lowlevel_read(u_el, (char *)buf + UFIFO_MSG_HEADER_SIZE, UFIFO_MSG_RTS_CTS_BODY_SIZE);
357 u_el->cts = ((struct ufifo_msg_rts_cts *)buf)->level;
358 break;
359 default:
360 bs_trace_error_time_line("Corrupted stream\n");
361 break;
362 }
363 return buf->msg_type;
364 }
365
366 /*
367 * Process the last received msg
368 * (Which may result in a pended call to nhw_ufifo_handle_RxTimer() )
369 */
uf_rx_process_last_msg_pre(struct ufifo_st_t * u_el)370 static void uf_rx_process_last_msg_pre(struct ufifo_st_t *u_el) {
371 struct ufifo_msg_header *buf = (struct ufifo_msg_header *)u_el->last_rx_msg;
372 switch (buf->msg_type) {
373 case ufifo_DISCONNECT:
374 return; /* Was already processed */
375 case ufifo_MODE_CHANGE:
376 case ufifo_NOP:
377 /* TOLOW: We can do a optimization here, by checking what is {ONT = the next schedueled event in the nsi hws}
378 * And if (ONT > buf->time) get another message from the Rx queue right away, as we anyhow would not do anything in the meanwhile
379 * (doing so would avoid possible abort reevaluations towards the phy, and loops thru the UART itself)
380 */
381 case ufifo_TX_BYTE:
382 case ufifo_RTS_CTS_TOGGLE:
383 u_el->last_rx_msg_pending = true;
384 u_el->Rx_timer = BS_MAX(buf->time, nsi_hws_get_time());
385 nhw_ufifo_update_timer();
386 break;
387 default:
388 bs_trace_error_time_line("Programming error\n");
389 break;
390 }
391 }
392
uf_rx_check_config_match(uint inst,struct ufifo_st_t * u_el)393 static void uf_rx_check_config_match(uint inst, struct ufifo_st_t *u_el) {
394 if (( (NRF_UARTE_regs[inst].CONFIG & CONFIG_RELEVANT_MASK)
395 != (u_el->rx_line_params.config & CONFIG_RELEVANT_MASK)
396 )
397 || (NRF_UARTE_regs[inst].BAUDRATE != u_el->rx_line_params.baud)) {
398 bs_trace_warning_time_line("UART%i: Receiving a byte with mismatched configuration. "
399 "This would result in a line error in a real UART. Here you just get this warning "
400 "(UARTE_regs.CONFIG = %"PRIu32" !=? %"PRIu32 " in other side ;"
401 "(UARTE_regs.BAUDRATE = %"PRIu32" !=? %"PRIu32" in other side\n",
402 inst,
403 NRF_UARTE_regs[inst].CONFIG, u_el->rx_line_params.config,
404 NRF_UARTE_regs[inst].BAUDRATE, u_el->rx_line_params.baud
405 );
406 }
407 }
408
409 /*
410 * Finish processing the last received msg if needed (at the RxTimer time)
411 */
uf_rx_process_last_msg_post(uint inst,struct ufifo_st_t * u_el)412 static void uf_rx_process_last_msg_post(uint inst, struct ufifo_st_t *u_el) {
413 struct ufifo_msg_header *buf = (struct ufifo_msg_header *)u_el->last_rx_msg;
414
415 if (u_el->last_rx_msg_pending == false) {
416 return;
417 }
418 u_el->last_rx_msg_pending = false;
419
420 switch (buf->msg_type) {
421 case ufifo_NOP:
422 case ufifo_MODE_CHANGE:
423 /* Nothing left to be done */
424 break;
425 case ufifo_TX_BYTE:
426 if (u_el->rx_on) {
427 bs_trace_info_time(8, "UART%i: Received byte (0x%02X)\n",
428 inst, ((struct ufifo_msg_tx *)buf)->data);
429 uf_rx_check_config_match(inst, u_el);
430 nhw_UARTE_digest_Rx_byte(inst, u_el->rx_byte);
431 } else {
432 bs_trace_info_time(3, "UART%i: Received byte (0x%02X) while Rx was off => ignored\n",
433 inst, ((struct ufifo_msg_tx *)buf)->data);
434 }
435 break;
436 case ufifo_RTS_CTS_TOGGLE:
437 uf_propage_cts(inst, u_el);
438 break;
439 case ufifo_DISCONNECT:
440 /* We should not have reached this point */
441 default:
442 bs_trace_error_time_line("Programming error\n");
443 break;
444 }
445 }
446
447 /*
448 * Dequeue all old input in the Rx FIFO until now
449 */
uf_Rx_handle_old_input(uint inst,struct ufifo_st_t * u_el)450 static void uf_Rx_handle_old_input(uint inst, struct ufifo_st_t *u_el) {
451 struct ufifo_msg_header *buf = (struct ufifo_msg_header *)u_el->last_rx_msg;
452 bs_time_t now = nsi_hws_get_time();
453
454 do {
455 uf_rx_get_one_msg(inst, u_el);
456 if (buf->time < now) {
457 switch (buf->msg_type) {
458 case ufifo_NOP: //We just ignore them while clearing
459 case ufifo_MODE_CHANGE:
460 break;
461 case ufifo_TX_BYTE:
462 if ((buf->time < now)) {
463 char byte = ((struct ufifo_msg_tx *)buf)->data;
464 bs_trace_info_time(3, "UART%i: Received byte (0x%02X) while Rx was off => ignored\n", inst, byte);
465 }
466 break;
467 case ufifo_RTS_CTS_TOGGLE:
468 /* We propagate past toggles right away to the UART */
469 uf_propage_cts(inst, u_el); //Note both Tx and Rx are disabled right now
470 break;
471 case ufifo_DISCONNECT:
472 return;
473 default:
474 bs_trace_error_time_line("Programming error\n");
475 break;
476 }
477 } else {
478 break;
479 }
480 } while (true);
481
482 uf_rx_process_last_msg_pre(u_el);
483 }
484
nhw_ufifo_handle_RxTimer(int inst,struct ufifo_st_t * u_el)485 static void nhw_ufifo_handle_RxTimer(int inst, struct ufifo_st_t *u_el) {
486
487 if (u_el->rx_low_dutyc_mode) {
488 /*
489 * Low duty cycle:
490 * (1) We pick all old messages, and the first that points at now or the future
491 * And we schedule nhw_ufifo_handle_RxTimer() to parse that last one normally
492 * (we get out of low duty cycle mode, and maybe we get in again after)
493 */
494 u_el->rx_low_dutyc_mode = false;
495 uf_Rx_handle_old_input(inst, u_el);
496 return;
497 }
498
499 uf_rx_process_last_msg_post(inst, u_el);
500
501 if (u_el->tx_on || u_el->rx_on) {
502 uf_rx_get_one_msg(inst, u_el);
503 uf_rx_process_last_msg_pre(u_el);
504 } else {
505 /* Low duty cycle (2):
506 * If the Tx and Rx are off, we go into low duty cycle mode:
507 * Instead of picking the next Rx msg now, we postpone picking it for uf_mdt (10ms)
508 * => when we come back to nhw_ufifo_handle_RxTimer() thru (1)
509 *
510 * Low dutycycle mode may be sped up at any point by setting the Rx_timer to a earlier value
511 */
512 u_el->rx_low_dutyc_mode = true;
513 u_el->Rx_timer = nsi_hws_get_time() + uf_mdt;
514 nhw_ufifo_update_timer();
515 }
516 }
517
nhw_ufifo_handle_TxTimer(int inst,struct ufifo_st_t * u_el)518 static void nhw_ufifo_handle_TxTimer(int inst, struct ufifo_st_t *u_el) {
519 bs_time_t t;
520
521 u_el->Tx_timer = TIME_NEVER;
522 nsi_hws_find_next_event();
523 t = nsi_hws_get_next_event_time() + nhw_uarte_one_byte_time(inst)/TX_MIN_DELTA_RATIO;
524 u_el->Tx_timer = t;
525
526 tx_nop(u_el, t);
527 }
528
nhw_ufifo_timer_triggered(void)529 static void nhw_ufifo_timer_triggered(void) {
530 bs_time_t current_time = Timer_UFIFO;
531 for (int i = 0; i < NHW_UARTE_TOTAL_INST; i++) {
532 struct ufifo_st_t *u_el = &ufifo_st[i];
533 if (u_el->Tx_timer == current_time) {
534 nhw_ufifo_handle_TxTimer(i, u_el);
535 }
536 if (u_el->Rx_timer == current_time) {
537 nhw_ufifo_handle_RxTimer(i, u_el);
538 }
539 }
540 nhw_ufifo_update_timer();
541 }
542
543 NSI_HW_EVENT(Timer_UFIFO, nhw_ufifo_timer_triggered, 900); /* Let's let as many timers as possible evaluate before this one */
544
uf_parse_mdt(char * argv,int offset)545 static void uf_parse_mdt(char *argv, int offset) {
546 (void) offset;
547 if (uf_mdt < 1 || uf_mdt > 1e6) {
548 bs_trace_error_line("uart_fifob_mdt must be set to a value between 1 and 1e6 (%s)\n", argv);
549 }
550 }
551
nhw_ufifo_backend_register_cmdline(void)552 static void nhw_ufifo_backend_register_cmdline(void) {
553 static bs_args_struct_t args2[2*NHW_UARTE_TOTAL_INST + 1 /* End marker */];
554 static char descr_tx[] = "Path to the FIFO to be used for Tx (it will be created automatically). "
555 "Remember to cross them between devices. "
556 "Setting this option enables the FIFO backend for this UART";
557 static char descr_rx[] = "Path to the FIFO to be used for Rx (it will be created automatically)";
558 #define OPTION_LEN (4 + 2 + 13 + 1)
559 static char options[NHW_UARTE_TOTAL_INST][2][OPTION_LEN];
560 static char opt_name[]= "path";
561
562 for (int i = 0 ; i < NHW_UARTE_TOTAL_INST; i++) {
563 snprintf(options[i][0], OPTION_LEN, "uart%i_fifob_txfile", i);
564 snprintf(options[i][1], OPTION_LEN, "uart%i_fifob_rxfile", i);
565
566 args2[2*i].option = options[i][0];
567 args2[2*i].name = opt_name;
568 args2[2*i].type = 's';
569 args2[2*i].dest = &ufifo_st[i].fifo_Tx_path;
570 args2[2*i].descript = descr_tx;
571
572 args2[2*i + 1].option = options[i][1];
573 args2[2*i + 1].name = opt_name;
574 args2[2*i + 1].type = 's';
575 args2[2*i + 1].dest = &ufifo_st[i].fifo_Rx_path;
576 args2[2*i + 1].descript = descr_rx;
577 }
578
579 bs_add_extra_dynargs(args2);
580
581 static bs_args_struct_t args1[] = {
582 { .is_switch = true,
583 .option = "uart_fifob_no_terminate",
584 .type = 'b',
585 .dest = (void *)&uf_dont_terminate_on_disconnect,
586 .descript = "Do NOT terminate execution of this device when the other end disconnects "
587 "gracefully"
588 },
589 { .option = "uart_fifob_mdt",
590 .type = 'd',
591 .call_when_found = uf_parse_mdt,
592 .dest = (void *)&uf_mdt,
593 .descript = "(By default 10e3=10ms) Maximum amount of time the backend will spend without dequeuing "
594 "the Rx FIFO when the UART is not running in Tx or Rx mode. "
595 "This needs to be small enough to avoid the pipe from filling up and causing "
596 "a deadlock. It can also be set to a very small value (order of 10micros) if you want to "
597 "ensure the UART keeps registering the CTS signal toggles even when it is not running "
598 "(This only makes sense if you have registered a short or a PPI event in the CTS signal "
599 "events). The smaller the value the greater the overhead."
600 },
601 ARG_TABLE_ENDMARKER
602 };
603
604 bs_add_extra_dynargs(args1);
605 }
606
607 NSI_TASK(nhw_ufifo_backend_register_cmdline, PRE_BOOT_1, 200);
608
nhw_ufifo_backend_post_cmdline(void)609 static void nhw_ufifo_backend_post_cmdline(void) {
610 for (int i = 0; i < NHW_UARTE_TOTAL_INST; i++) {
611 struct ufifo_st_t *u_el = &ufifo_st[i];
612 if ((u_el->fifo_Rx_path == NULL) && (u_el->fifo_Rx_path == NULL)) {
613 continue;
614 }
615 if (u_el->fifo_Tx_path == NULL) {
616 bs_trace_error_line("UART%i: uart_fifob_rxfile was provided but not uart_fifob_txfile. "
617 "If you want to use the FIFO backend you must provide both\n",i);
618 }
619 if (u_el->fifo_Rx_path == NULL) {
620 bs_trace_error_line("UART%i: uart_fifob_txfile was provided but not uart_fifob_rxfile. "
621 "If you want to use the FIFO backend you must provide both\n",i);
622 }
623 u_el->enabled = true;
624 }
625 if (isnan(uf_mdt)) {
626 uf_mdt = 10000;
627 }
628 }
629
630 NSI_TASK(nhw_ufifo_backend_post_cmdline, PRE_BOOT_2, 200);
631
632 /*
633 * Create FIFOs towards the other device and open both from our end.
634 * But do not block. The Tx FIFO is as nonblocking, and the Rx
635 * FIFO is reconfigured as blocking.
636 */
nhw_ufifo_create_fifos(uint inst,struct ufifo_st_t * u_el)637 static void nhw_ufifo_create_fifos(uint inst, struct ufifo_st_t *u_el) {
638 bs_trace_raw_time(9, "Creating UART%i backend FIFOs, and connecting Tx end\n", inst);
639
640 bs_create_folders_in_path(u_el->fifo_Tx_path);
641 bs_create_folders_in_path(u_el->fifo_Rx_path);
642
643 if (pb_create_fifo_if_not_there(u_el->fifo_Tx_path) != 0) {
644 bs_trace_error_line("Couldn't create UART backend Tx FIFOs\n");
645 }
646 if (pb_create_fifo_if_not_there(u_el->fifo_Rx_path) != 0) {
647 bs_trace_error_line("Couldn't create UART backend Rx FIFOs\n");
648 }
649
650 /* 1) We first open our read side non-blocking to avoid a deadlock.
651 * (This open read side, even if provisional, allows the other side to safely start writing to the
652 * pipe even if it overtakes our step 3) )
653 */
654 int prov_descr = open(u_el->fifo_Rx_path, O_RDONLY | O_NONBLOCK);
655 if (prov_descr == -1) {
656 bs_trace_error_line("Couldn't open UART backend Rx FIFO (%i, %s)\n", errno, strerror(errno));
657 }
658
659 /* 2) We block opening our Tx side until the other side has reached 1) */
660 u_el->fifo_tx = open(u_el->fifo_Tx_path, O_WRONLY);
661 if (u_el->fifo_tx == -1) {
662 bs_trace_error_line("Couldn't open UART backend Tx FIFO (%i, %s)\n", errno, strerror(errno));
663 }
664
665 /* 3) And now that we know the other side reached 1), we can block until it reaches 2)
666 * while creating the descriptor we will actually use */
667 u_el->fifo_rx = open(u_el->fifo_Rx_path, O_RDONLY);
668 if (u_el->fifo_rx == -1) {
669 bs_trace_error_line("Couldn't open UART backend Rx FIFO (%i, %s)\n", errno, strerror(errno));
670 }
671
672 /* At this point we know both pipes are open in both sides.
673 * We can now close our previous Rx descriptor as we won't use it for anything anymore. */
674 (void)close(prov_descr);
675
676 }
677
nhw_ufifo_backend_cleanup(void)678 static void nhw_ufifo_backend_cleanup(void) {
679 bs_trace_raw_time(9, "Cleaning up UART backend FIFOs\n");
680 for (int i = 0; i < NHW_UARTE_TOTAL_INST; i++) {
681 struct ufifo_st_t *u_el = &ufifo_st[i];
682 if ((u_el->fifo_Tx_path) && (u_el->fifo_tx != -1)) {
683 if (!u_el->disconnected) {
684 tx_disconnect(u_el);
685 }
686 (void)close(u_el->fifo_tx);
687 u_el->fifo_tx = -1;
688 (void)remove(u_el->fifo_Tx_path); /* The last one closing manages to remove it (so we don't check for success) */
689 u_el->fifo_Tx_path = NULL;
690 }
691 if ((u_el->fifo_Rx_path) && (u_el->fifo_rx != -1)) {
692 (void)close(u_el->fifo_rx);
693 u_el->fifo_rx = -1;
694 (void)remove(u_el->fifo_Rx_path); /* The last one closing manages to remove it (so we don't check for success) */
695 u_el->fifo_Rx_path = NULL;
696 }
697 }
698 }
699
700 NSI_TASK(nhw_ufifo_backend_cleanup, ON_EXIT_PRE, 100);
701