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