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