1 /*
2 * Copyright (C)2021-2022 Intel Corporation.
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <stdbool.h>
11 #include <string.h>
12 #include <termios.h>
13 #include <unistd.h>
14 #include <sys/types.h>
15 #include <sys/queue.h>
16 #include <pthread.h>
17 #include <limits.h>
18 #include <stdint.h>
19 #include "uart_channel.h"
20 #include "log.h"
21 #include "list.h"
22 #include "config.h"
23 #include "command.h"
24
25 #define SYNC_FMT "sync:%s"
26
parse_channel_dev_id(struct channel_dev * c_dev)27 static void parse_channel_dev_id(struct channel_dev *c_dev)
28 {
29 char *saveptr;
30
31 (void) strtok_r(c_dev->buf, ":", &saveptr);
32 if (strlen(saveptr) > 0) {
33 strncpy(c_dev->name, saveptr, CHANNEL_DEV_NAME_MAX - 1U);
34 LOG_PRINTF("Device fd:%d, VM name:%s\n",
35 get_uart_dev_fd(c_dev->uart_device), c_dev->name);
36 }
37 }
add_uart_channel_dev_connection_list(struct channel_dev * c_dev)38 static void add_uart_channel_dev_connection_list(struct channel_dev *c_dev)
39 {
40 struct uart_channel *c = c_dev->channel;
41
42 pthread_mutex_lock(&c->tty_conn_list_lock);
43 LIST_INSERT_HEAD(&c->tty_conn_head, c_dev, list);
44 pthread_mutex_unlock(&c->tty_conn_list_lock);
45 }
46 /**
47 * @brief Wait to connect device in uart channel
48 *
49 * Wait sync message from slave channel device, parse slave channel device
50 * indentifier from sync message, then add channel device into uart channel
51 * device connection list.
52 */
listen_uart_channel_dev(void * arg)53 void *listen_uart_channel_dev(void *arg)
54 {
55 ssize_t num;
56 struct channel_dev *c_dev = (struct channel_dev *)arg;
57
58 LOG_PRINTF("Lifecycle manager in service VM fd=%d tty node=%s\n",
59 get_uart_dev_fd(c_dev->uart_device), get_uart_dev_path(c_dev->uart_device));
60 memset(c_dev->buf, 0, sizeof(c_dev->buf));
61 while (c_dev->listening) {
62 num = receive_message_by_uart(c_dev->uart_device, (void *)c_dev->buf,
63 sizeof(c_dev->buf));
64 if (num == 0) {
65 usleep(LISTEN_INTERVAL);
66 continue;
67 }
68 parse_channel_dev_id(c_dev);
69 if (strncmp(SYNC_CMD, c_dev->buf, sizeof(SYNC_CMD)) == 0) {
70 /** Add channel device instance into UART connection list */
71 add_uart_channel_dev_connection_list(c_dev);
72
73 c_dev->listening = false;
74 LOG_PRINTF("Receive sync message from user VM (%s), start to talk.\n",
75 c_dev->name);
76 usleep(2 * WAIT_RECV);
77 (void)send_message_by_uart(c_dev->uart_device, ACK_SYNC, strlen(ACK_SYNC));
78 sem_post(&c_dev->dev_sem);
79 }
80 }
81
82 LOG_PRINTF("Lifecycle manager stops to listen device:%s\n",
83 get_uart_dev_path(c_dev->uart_device));
84 return NULL;
85 }
86 /**
87 * @brief Wait to connect device in the uart channel
88 * and poll message
89 *
90 * Send sync message every 5 second and wait acked sync message,
91 * if acked sync message is received, add uart channel device instance
92 * into uart connection list. It acked sync message is not received, the lifecycle
93 * manager will exit.
94 */
connect_uart_channel_dev(void * arg)95 void *connect_uart_channel_dev(void *arg)
96 {
97 ssize_t ret;
98 struct channel_dev *c_dev = (struct channel_dev *)arg;
99 struct uart_channel *c = c_dev->channel;
100 char buf[CHANNEL_DEV_NAME_MAX + SYNC_LEN];
101
102 snprintf(buf, sizeof(buf), SYNC_FMT, c->conf.identifier);
103 /* TODO: will add SYNC resending */
104 LOG_PRINTF("Send sync command:%s identifier=%s\n", buf, c->conf.identifier);
105 ret = send_message_by_uart(c_dev->uart_device, (void *)buf, strlen(buf));
106 if (ret < 0) {
107 LOG_WRITE("Send sync command to service VM fail\n");
108 } else {
109 usleep(LISTEN_INTERVAL);
110 memset(c_dev->buf, 0, sizeof(c_dev->buf));
111 (void) receive_message_by_uart(c_dev->uart_device, (void *)c_dev->buf, sizeof(c_dev->buf));
112 if (strncmp(ACK_SYNC, c_dev->buf, sizeof(ACK_SYNC)) == 0) {
113 /** Add channel device instance into UART connection list */
114 add_uart_channel_dev_connection_list(c_dev);
115 LOG_WRITE("Lifecycle manager: connected\n");
116 } else {
117 ret = -1;
118 LOG_PRINTF("Device in the (%s): failed to connect\n", c->conf.identifier);
119 }
120 }
121 if (ret < 0)
122 c_dev->polling = false;
123 c_dev->listening = false;
124 sem_post(&c_dev->dev_sem);
125 return NULL;
126 }
127
poll_and_dispatch_uart_channel_events(void * arg)128 void *poll_and_dispatch_uart_channel_events(void *arg)
129 {
130 struct channel_dev *c_dev = (struct channel_dev *)arg;
131 ssize_t num, ret;
132 struct uart_channel *c;
133
134 c = c_dev->channel;
135 sem_wait(&c_dev->dev_sem);
136 LOG_PRINTF("UART polling fd=%d...\n", get_uart_dev_fd(c_dev->uart_device));
137 while (c_dev->polling) {
138 memset(c_dev->buf, 0, sizeof(c_dev->buf));
139 num = receive_message_by_uart(c_dev->uart_device, (void *)c_dev->buf,
140 sizeof(c_dev->buf));
141 /**
142 * Resend message if resend_time is set.
143 */
144 if (num == 0) {
145 if (c_dev->resend_time > 1) {
146 usleep(LISTEN_INTERVAL + SECOND_TO_US);
147 LOG_PRINTF("Resend (%s) to (%s)\n", c_dev->resend_buf, c_dev->name);
148 ret = send_message_by_uart(c_dev->uart_device, (void *)c_dev->resend_buf,
149 strlen(c_dev->resend_buf));
150 if (ret < 0)
151 LOG_WRITE("Send poweroff message to user VM fail\n");
152 c_dev->resend_time--;
153 } else if (c_dev->resend_time == 1) {
154 memcpy(c_dev->buf, ACK_TIMEOUT, strlen(ACK_TIMEOUT));
155 num = strlen(ACK_TIMEOUT);
156 } else {
157 /* No action if resend_time is 0 */
158 }
159 }
160 if (num > 0) {
161 parse_channel_dev_id(c_dev);
162 c->data_handler((const char *)c_dev->buf, get_uart_dev_fd(c_dev->uart_device));
163 }
164 }
165 LOG_PRINTF("Lifecycle manager stops to poll device:%s\n",
166 get_uart_dev_path(c_dev->uart_device));
167 return NULL;
168 }
find_uart_channel_dev(struct uart_channel * c,int fd)169 struct channel_dev *find_uart_channel_dev(struct uart_channel *c, int fd)
170 {
171 struct channel_dev *c_dev = NULL;
172
173 LIST_FOREACH(c_dev, &c->tty_conn_head, list) {
174 if (get_uart_dev_fd(c_dev->uart_device) == fd)
175 break;
176 }
177 return c_dev;
178 }
find_uart_channel_dev_by_name(struct uart_channel * c,char * name)179 struct channel_dev *find_uart_channel_dev_by_name(struct uart_channel *c, char *name)
180 {
181 struct channel_dev *c_dev = NULL;
182
183 LIST_FOREACH(c_dev, &c->tty_conn_head, list) {
184 if (strncmp(name, c_dev->name, sizeof(c_dev->name)) == 0)
185 break;
186 }
187 return c_dev;
188 }
189 /**
190 * @brief Set message polling loop flag as flase and remove channel device instance from
191 * the connection list
192 *
193 * @param c_dev point to uart channel device instance
194 * @param c point to uart channel instance
195 */
disconnect_uart_channel_dev(struct channel_dev * c_dev,struct uart_channel * c)196 void disconnect_uart_channel_dev(struct channel_dev *c_dev, struct uart_channel *c)
197 {
198 c_dev->listening = false;
199 c_dev->polling = false;
200
201 pthread_mutex_lock(&c->tty_conn_list_lock);
202 LIST_REMOVE(c_dev, list);
203 pthread_mutex_unlock(&c->tty_conn_list_lock);
204 }
205 /**
206 * @brief Traverse uart channel open list to set listening loop flag and polling loop flag
207 * to false for each channel device which is in listening state.
208 *
209 * @param c point to uart channel instance
210 */
stop_listen_uart_channel_dev(struct uart_channel * c)211 void stop_listen_uart_channel_dev(struct uart_channel *c)
212 {
213 struct channel_dev *c_dev;
214
215 LIST_FOREACH(c_dev, &c->tty_open_head, open_list) {
216 if (c_dev->listening) {
217 LOG_PRINTF("Stop to listen uart device (%s)\n",
218 get_uart_dev_path(c_dev->uart_device));
219 c_dev->listening = false;
220 c_dev->polling = false;
221 sem_post(&c_dev->dev_sem);
222 }
223 }
224 }
start_uart_channel_dev_resend(struct channel_dev * c_dev,char * resend_buf,unsigned int resend_time)225 void start_uart_channel_dev_resend(struct channel_dev *c_dev, char *resend_buf, unsigned int resend_time)
226 {
227 if (resend_time < MIN_RESEND_TIME)
228 resend_time = MIN_RESEND_TIME;
229 strncpy(c_dev->resend_buf, resend_buf, CHANNEL_DEV_BUF_LEN - 1);
230 c_dev->resend_time = resend_time + 1;
231 }
start_all_uart_channel_dev_resend(struct uart_channel * c,char * msg,unsigned int resend_time)232 void start_all_uart_channel_dev_resend(struct uart_channel *c, char *msg, unsigned int resend_time)
233 {
234 struct channel_dev *c_dev;
235
236 /* Enable resend for all connected uart channel devices */
237 pthread_mutex_lock(&c->tty_conn_list_lock);
238 LIST_FOREACH(c_dev, &c->tty_conn_head, list) {
239 start_uart_channel_dev_resend(c_dev, msg, resend_time);
240 }
241 pthread_mutex_unlock(&c->tty_conn_list_lock);
242 }
stop_uart_channel_dev_resend(struct channel_dev * c_dev)243 void stop_uart_channel_dev_resend(struct channel_dev *c_dev)
244 {
245 if (c_dev->resend_time == 1U)
246 LOG_PRINTF("Timeout of receiving ACK message from (%s)\n", c_dev->name);
247 c_dev->resend_time = 0U;
248 memset(c_dev->resend_buf, 0x0, CHANNEL_DEV_BUF_LEN);
249 }
250 /**
251 * @brief Send message to each connected uart channel device
252 *
253 * @param c uart channel instance
254 * @param msg pointer which points to the message to be sent
255 */
notify_all_connected_uart_channel_dev(struct uart_channel * c,char * msg)256 void notify_all_connected_uart_channel_dev(struct uart_channel *c, char *msg)
257 {
258 struct channel_dev *c_dev;
259
260 /* Send message to all tty connected devices*/
261 pthread_mutex_lock(&c->tty_conn_list_lock);
262 LIST_FOREACH(c_dev, &c->tty_conn_head, list) {
263 LOG_PRINTF("Send (%s) to (%s)\n", msg, c_dev->name);
264 (void) send_message_by_uart(c_dev->uart_device, msg, strlen(msg));
265 }
266 pthread_mutex_unlock(&c->tty_conn_list_lock);
267 }
is_uart_channel_connection_list_empty(struct uart_channel * c)268 bool is_uart_channel_connection_list_empty(struct uart_channel *c)
269 {
270 bool ret = false;
271
272 pthread_mutex_lock(&c->tty_conn_list_lock);
273 if (LIST_EMPTY(&c->tty_conn_head))
274 ret = true;
275 pthread_mutex_unlock(&c->tty_conn_list_lock);
276 return ret;
277 }
create_uart_channel_dev(struct uart_channel * c,char * path,data_handler_f * fn)278 struct channel_dev *create_uart_channel_dev(struct uart_channel *c, char *path, data_handler_f *fn)
279 {
280 struct uart_dev *dev;
281 struct channel_dev *c_dev;
282
283 if (c == NULL || path == NULL || fn == NULL)
284 return NULL;
285 c->data_handler = fn;
286 dev = init_uart_dev(path);
287 if (dev == NULL) {
288 LOG_PRINTF("Failed to initialize UART device %s\n", path);
289 return NULL;
290 }
291
292 c_dev = calloc(1, sizeof(*c_dev));
293 if (!c_dev) {
294 LOG_PRINTF("%s: Failed to allocate memory for UART channel device\n", __func__);
295 deinit_uart_dev(dev);
296 return NULL;
297 }
298 memset(c_dev, 0x0, sizeof(*c_dev));
299 c_dev->uart_device = dev;
300 c_dev->channel = c;
301 c_dev->listening = true;
302 c_dev->polling = true;
303 sem_init(&c_dev->dev_sem, 0, 0);
304 /** Add channel device instance into open list */
305 pthread_mutex_lock(&c->tty_conn_list_lock);
306 LIST_INSERT_HEAD(&c->tty_open_head, c_dev, open_list);
307 pthread_mutex_unlock(&c->tty_conn_list_lock);
308 return c_dev;
309 }
destroy_uart_channel_devs(struct uart_channel * c)310 static void destroy_uart_channel_devs(struct uart_channel *c)
311 {
312 struct channel_dev *c_dev, *tc_dev;
313
314 list_foreach_safe(c_dev, &c->tty_open_head, open_list, tc_dev) {
315 pthread_mutex_lock(&c->tty_conn_list_lock);
316 LIST_REMOVE(c_dev, open_list);
317 pthread_mutex_unlock(&c->tty_conn_list_lock);
318
319 deinit_uart_dev(c_dev->uart_device);
320 free(c_dev);
321 }
322 }
wait_uart_channel_devs_threads(struct uart_channel * c)323 void wait_uart_channel_devs_threads(struct uart_channel *c)
324 {
325 struct channel_dev *c_dev;
326
327 LIST_FOREACH(c_dev, &c->tty_open_head, open_list) {
328 pthread_join(c_dev->listen_thread, NULL);
329 pthread_join(c_dev->pool_thread, NULL);
330 }
331 }
init_uart_channel(char * id)332 struct uart_channel *init_uart_channel(char *id)
333 {
334 struct uart_channel *c;
335
336 if (id == NULL) {
337 LOG_PRINTF("%s:invlid parameter\n", __func__);
338 return NULL;
339 }
340 c = calloc(1, sizeof(*c));
341 if (!c) {
342 LOG_PRINTF("%s: Failed to allocate memory for UART channel\n", __func__);
343 return NULL;
344 }
345 c->data_handler = NULL;
346 LIST_INIT(&c->tty_conn_head);
347 LIST_INIT(&c->tty_open_head);
348 pthread_mutex_init(&c->tty_conn_list_lock, NULL);
349 memcpy(c->conf.identifier, id, strlen(id));
350 return c;
351 }
deinit_uart_channel(struct uart_channel * c)352 void deinit_uart_channel(struct uart_channel *c)
353 {
354 if (c != NULL) {
355 destroy_uart_channel_devs(c);
356 free(c);
357 }
358 }
359