1 /*
2  * Copyright (c) 2021 Linaro Limited
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /*
8  * @addtogroup t_uart_basic
9  * @{
10  * @defgroup t_uart_fifo test_uart_pending
11  * @brief TestPurpose: test UART uart_irq_is_pending()
12  * @details
13  *
14  * Test if uart_irq_is_pending() correctly returns 0 when there are no
15  * more RX and TX pending interrupts.
16  *
17  * The test consists in disabling TX IRQ so no TX interrupts are
18  * generated and the TX IRQ pending flag is never set. At the same time
19  * RX IRQ is enabled to let received data cause a RX IRQ and so set the
20  * RX IRQ pending flag.
21  *
22  * Then a message is sent via serial to inform that the test is ready to
23  * receive serial data, which will trigger a RX IRQ.
24  *
25  * Once a RX IRQ happens RX data is read by uart_fifo_read() until there
26  * is no more RX data to be popped from FIFO and all IRQs are handled.
27  * When that happens uart_irq_is_pending() is called and must return 0,
28  * indicating there are no more pending interrupts to be processed. If 0
29  * is returned the test passes.
30  *
31  * In some cases uart_irq_is_pending() does not correctly use the IRQ
32  * pending flags to determine if there are pending interrupts, hence
33  * even tho there aren't any further RX and TX IRQs to be processed it
34  * wrongly returns 1. If 1 is returned the test fails.
35  *
36  * @}
37  */
38 
39 #include "test_uart.h"
40 
41 #define MAX_NUM_TRIES 512
42 #define NOT_READY 0
43 
44 #define FAILED	0
45 #define PASSED	1
46 #define WAIT	2
47 static int volatile status;
48 
uart_pending_callback(const struct device * dev,void * user_data)49 static void uart_pending_callback(const struct device *dev, void *user_data)
50 {
51 	ARG_UNUSED(user_data);
52 
53 	int num_tries = 0;
54 	char recv_char;
55 
56 	/*
57 	 * If the bug is not present uart_fifo_read() will pop all
58 	 * received data until there is no more RX data, thus
59 	 * uart_irq_is_pending() must correctly return 0 indicating
60 	 * that there are no more RX interrupts to be processed.
61 	 * Otherwise uart_irq_is_pending() never returns 0 even tho
62 	 * there is no more RX data in the RX buffer to be processed,
63 	 * so, in that case, the test fails after MAX_NUM_TRIES attempts.
64 	 */
65 	status = PASSED;
66 	while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
67 		if (uart_irq_rx_ready(dev) == NOT_READY) {
68 			if (num_tries < MAX_NUM_TRIES) {
69 				num_tries++;
70 				continue;
71 			} else {
72 				/*
73 				 * Bug: no more tries; uart_irq_is_pending()
74 				 * always returned 1 in spite of having no more
75 				 * RX data to be read from FIFO and no more TX
76 				 * data in FIFO to be sent via serial line.
77 				 * N.B. uart_irq_update() always returns 1, thus
78 				 * uart_irq_is_pending() got stuck without any
79 				 * real pending interrupt, i.e. no more RX and
80 				 * TX data to be popped or pushed from/to FIFO.
81 				 */
82 				status = FAILED;
83 				break;
84 			}
85 		}
86 
87 		while (uart_fifo_read(dev, &recv_char, 1)) {
88 			/* Echo received char */
89 			TC_PRINT("%c", recv_char);
90 		}
91 	}
92 }
93 
test_pending(void)94 static int test_pending(void)
95 {
96 	const struct device *const uart_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
97 
98 	if (!device_is_ready(uart_dev)) {
99 		TC_PRINT("UART device not ready\n");
100 		return TC_FAIL;
101 	}
102 
103 	/*
104 	 * Set IRQ callback function to handle RX IRQ.
105 	 */
106 	uart_irq_callback_set(uart_dev, uart_pending_callback);
107 
108 	/*
109 	 * Disable TX IRQ since transmitted data is not
110 	 * handled by uart_pending_callback() and we don't
111 	 * want to trigger any TX IRQ for this test.
112 	 */
113 	uart_irq_tx_disable(uart_dev);
114 
115 	/*
116 	 * Enable RX IRQ so uart_pending_callback() can
117 	 * handle input data is available in RX FIFO.
118 	 */
119 	uart_irq_rx_enable(uart_dev);
120 
121 	status = WAIT;
122 
123 	/* Inform test is ready to receive data */
124 	TC_PRINT("Please send characters to serial console\n");
125 
126 	while (status == WAIT) {
127 		/* Allow other thread/workqueue to work. */
128 		k_yield();
129 		/*
130 		 * Wait RX handler change 'status' properly:
131 		 * it will change to PASSED or FAILED after
132 		 * uart_irq_is_pending() is tested by
133 		 * uart_pending_callback() upon data reception.
134 		 */
135 	}
136 
137 	if (status == PASSED) {
138 		return TC_PASS;
139 	} else {
140 		return TC_FAIL;
141 	}
142 }
143 
144 #if CONFIG_SHELL
test_uart_pending(void)145 void test_uart_pending(void)
146 #else
147 ZTEST(uart_basic_api_pending, test_uart_pending)
148 #endif
149 {
150 #ifndef CONFIG_UART_INTERRUPT_DRIVEN
151 	ztest_test_skip();
152 #endif
153 	zassert_true(test_pending() == TC_PASS);
154 }
155