1 /*
2  * Copyright (c) 2022 Trackunit Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /*************************************************************************************************/
8 /*                                        Dependencies                                           */
9 /*************************************************************************************************/
10 #include <zephyr/ztest.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/sys/atomic.h>
13 
14 #include <zephyr/modem/backend/tty.h>
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <string.h>
21 #include <termios.h>
22 
23 #define TEST_MODEM_BACKEND_TTY_PIPE_EVENT_OPENED_BIT (0)
24 #define TEST_MODEM_BACKEND_TTY_PIPE_EVENT_RRDY_BIT   (1)
25 #define TEST_MODEM_BACKEND_TTY_PIPE_EVENT_TIDLE_BIT  (2)
26 #define TEST_MODEM_BACKEND_TTY_PIPE_EVENT_CLOSED_BIT (3)
27 
28 #define TEST_MODEM_BACKEND_TTY_OP_DELAY (K_MSEC(1000))
29 
30 /*************************************************************************************************/
31 /*                                          Mock pipe                                            */
32 /*************************************************************************************************/
33 static struct modem_backend_tty tty_backend;
34 static struct modem_pipe *tty_pipe;
35 
36 /*************************************************************************************************/
37 /*                                          Mock PTY                                             */
38 /*************************************************************************************************/
39 static int primary_fd;
40 
41 /*************************************************************************************************/
42 /*                                          Buffers                                              */
43 /*************************************************************************************************/
44 static uint8_t buffer1[1024];
45 K_KERNEL_STACK_DEFINE(tty_stack, 4096);
46 
47 /*************************************************************************************************/
48 /*                                          Helpers                                              */
49 /*************************************************************************************************/
test_modem_backend_tty_cfmakeraw(struct termios * termios_p)50 static void test_modem_backend_tty_cfmakeraw(struct termios *termios_p)
51 {
52 	termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
53 	termios_p->c_oflag &= ~OPOST;
54 	termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
55 	termios_p->c_cflag &= ~(CSIZE | PARENB);
56 	termios_p->c_cflag |= CS8;
57 }
58 
59 /*************************************************************************************************/
60 /*                                     Modem pipe callback                                       */
61 /*************************************************************************************************/
62 static atomic_t tty_pipe_events;
63 
modem_pipe_callback_handler(struct modem_pipe * pipe,enum modem_pipe_event event,void * user_data)64 static void modem_pipe_callback_handler(struct modem_pipe *pipe, enum modem_pipe_event event,
65 					void *user_data)
66 {
67 	switch (event) {
68 	case MODEM_PIPE_EVENT_OPENED:
69 		atomic_set_bit(&tty_pipe_events, TEST_MODEM_BACKEND_TTY_PIPE_EVENT_OPENED_BIT);
70 		break;
71 
72 	case MODEM_PIPE_EVENT_RECEIVE_READY:
73 		atomic_set_bit(&tty_pipe_events, TEST_MODEM_BACKEND_TTY_PIPE_EVENT_RRDY_BIT);
74 		break;
75 
76 	case MODEM_PIPE_EVENT_TRANSMIT_IDLE:
77 		atomic_set_bit(&tty_pipe_events, TEST_MODEM_BACKEND_TTY_PIPE_EVENT_TIDLE_BIT);
78 		break;
79 
80 	case MODEM_PIPE_EVENT_CLOSED:
81 		atomic_set_bit(&tty_pipe_events, TEST_MODEM_BACKEND_TTY_PIPE_EVENT_CLOSED_BIT);
82 		break;
83 	}
84 }
85 
86 /*************************************************************************************************/
87 /*                                         Test setup                                            */
88 /*************************************************************************************************/
test_modem_backend_tty_setup(void)89 static void *test_modem_backend_tty_setup(void)
90 {
91 	const char *secondary_name;
92 	struct termios tio;
93 
94 	primary_fd = posix_openpt(O_RDWR | O_NOCTTY);
95 	__ASSERT_NO_MSG(primary_fd > -1);
96 	__ASSERT_NO_MSG(grantpt(primary_fd) > -1);
97 	__ASSERT_NO_MSG(unlockpt(primary_fd) > -1);
98 	__ASSERT_NO_MSG(tcgetattr(primary_fd, &tio) > -1);
99 	test_modem_backend_tty_cfmakeraw(&tio);
100 	__ASSERT_NO_MSG(tcsetattr(primary_fd, TCSAFLUSH, &tio) > -1);
101 	secondary_name = ptsname(primary_fd);
102 
103 	struct modem_backend_tty_config config = {
104 		.tty_path = secondary_name,
105 		.stack = tty_stack,
106 		.stack_size = K_KERNEL_STACK_SIZEOF(tty_stack),
107 	};
108 
109 	tty_pipe = modem_backend_tty_init(&tty_backend, &config);
110 	modem_pipe_attach(tty_pipe, modem_pipe_callback_handler, NULL);
111 	__ASSERT_NO_MSG(modem_pipe_open(tty_pipe) == 0);
112 	return NULL;
113 }
114 
test_modem_backend_tty_before(void * f)115 static void test_modem_backend_tty_before(void *f)
116 {
117 	atomic_set(&tty_pipe_events, 0);
118 }
119 
test_modem_backend_tty_teardown(void * f)120 static void test_modem_backend_tty_teardown(void *f)
121 {
122 	modem_pipe_close(tty_pipe);
123 }
124 
125 /*************************************************************************************************/
126 /*                                             Tests                                             */
127 /*************************************************************************************************/
ZTEST(modem_backend_tty_suite,test_close_open)128 ZTEST(modem_backend_tty_suite, test_close_open)
129 {
130 	bool result;
131 
132 	zassert_ok(modem_pipe_close(tty_pipe), "Failed to close pipe");
133 	zassert_ok(modem_pipe_close(tty_pipe), "Pipe should already be closed");
134 	zassert_ok(modem_pipe_open(tty_pipe), "Failed to open pipe");
135 	result = atomic_test_bit(&tty_pipe_events, TEST_MODEM_BACKEND_TTY_PIPE_EVENT_TIDLE_BIT);
136 	zassert_true(result, "Transmit idle event should be set");
137 	zassert_ok(modem_pipe_open(tty_pipe), "Pipe should already be open");
138 }
139 
ZTEST(modem_backend_tty_suite,test_receive_ready_event_not_raised)140 ZTEST(modem_backend_tty_suite, test_receive_ready_event_not_raised)
141 {
142 	bool result;
143 
144 	k_sleep(TEST_MODEM_BACKEND_TTY_OP_DELAY);
145 
146 	result = atomic_test_bit(&tty_pipe_events, TEST_MODEM_BACKEND_TTY_PIPE_EVENT_RRDY_BIT);
147 	zassert_false(result, "Receive ready event should not be set");
148 }
149 
ZTEST(modem_backend_tty_suite,test_receive_ready_event_raised)150 ZTEST(modem_backend_tty_suite, test_receive_ready_event_raised)
151 {
152 	int ret;
153 	bool result;
154 	char msg[] = "Test me buddy";
155 
156 	ret = write(primary_fd, msg, sizeof(msg));
157 	zassert_true(ret == sizeof(msg), "Failed to write to primary FD");
158 
159 	k_sleep(TEST_MODEM_BACKEND_TTY_OP_DELAY);
160 
161 	result = atomic_test_bit(&tty_pipe_events, TEST_MODEM_BACKEND_TTY_PIPE_EVENT_RRDY_BIT);
162 	zassert_true(result == true, "Receive ready evennt not set");
163 }
164 
ZTEST(modem_backend_tty_suite,test_receive)165 ZTEST(modem_backend_tty_suite, test_receive)
166 {
167 	int ret;
168 	char msg[] = "Test me buddy";
169 
170 	ret = write(primary_fd, msg, sizeof(msg));
171 	zassert_true(ret == sizeof(msg), "Failed to write to primary FD");
172 	k_sleep(TEST_MODEM_BACKEND_TTY_OP_DELAY);
173 
174 	ret = modem_pipe_receive(tty_pipe, buffer1, sizeof(buffer1));
175 	zassert_true(ret == sizeof(msg), "Received incorrect number of bytes");
176 	ret = memcmp(msg, buffer1, sizeof(msg));
177 	zassert_true(ret == 0, "Received incorrect bytes");
178 }
179 
ZTEST(modem_backend_tty_suite,test_transmit)180 ZTEST(modem_backend_tty_suite, test_transmit)
181 {
182 	int ret;
183 	char msg[] = "Test me buddy 2";
184 	bool result;
185 
186 	result = atomic_test_bit(&tty_pipe_events, TEST_MODEM_BACKEND_TTY_PIPE_EVENT_TIDLE_BIT);
187 	zassert_false(result, "Transmit idle event should not be set");
188 
189 	ret = modem_pipe_transmit(tty_pipe, msg, sizeof(msg));
190 	zassert_true(ret == sizeof(msg), "Failed to transmit using pipe");
191 
192 	k_sleep(TEST_MODEM_BACKEND_TTY_OP_DELAY);
193 
194 	result = atomic_test_bit(&tty_pipe_events, TEST_MODEM_BACKEND_TTY_PIPE_EVENT_TIDLE_BIT);
195 	zassert_true(result, "Transmit idle event should be set");
196 
197 	ret = read(primary_fd, buffer1, sizeof(buffer1));
198 	zassert_true(ret == sizeof(msg), "Read incorrect number of bytes");
199 	ret = memcmp(msg, buffer1, sizeof(msg));
200 	zassert_true(ret == 0, "Read incorrect bytes");
201 }
202 
203 ZTEST_SUITE(modem_backend_tty_suite, NULL, test_modem_backend_tty_setup,
204 	    test_modem_backend_tty_before, NULL, test_modem_backend_tty_teardown);
205