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