1 /*
2  * Copyright (c) 2022 Trackunit Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/modem/backend/tty.h>
8 #include "modem_backend_tty_bottom.h"
9 
10 #include <zephyr/logging/log.h>
11 LOG_MODULE_REGISTER(modem_backend_tty, CONFIG_MODEM_MODULES_LOG_LEVEL);
12 
13 #include <nsi_host_trampolines.h>
14 #include <string.h>
15 
16 #define MODEM_BACKEND_TTY_THREAD_PRIO          (10)
17 #define MODEM_BACKEND_TTY_THREAD_RUN_PERIOD_MS (1000)
18 #define MODEM_BACKEND_TTY_THREAD_POLL_DELAY    (100)
19 
20 #define MODEM_BACKEND_TTY_STATE_RUN_BIT (1)
21 
modem_backend_tty_routine(void * p1,void * p2,void * p3)22 static void modem_backend_tty_routine(void *p1, void *p2, void *p3)
23 {
24 	struct modem_backend_tty *backend = (struct modem_backend_tty *)p1;
25 
26 	ARG_UNUSED(p2);
27 	ARG_UNUSED(p3);
28 
29 	/* Run until run flag is cleared. Check every MODEM_BACKEND_TTY_THREAD_RUN_PERIOD_MS */
30 	while (atomic_test_bit(&backend->state, MODEM_BACKEND_TTY_STATE_RUN_BIT)) {
31 
32 		int ret = modem_backend_tty_poll_bottom(backend->tty_fd);
33 
34 		if (ret < 0) {
35 			LOG_ERR("Poll operation failed");
36 			break;
37 		}
38 
39 		if (ret) {
40 			modem_pipe_notify_receive_ready(&backend->pipe);
41 		}
42 
43 		k_sleep(K_MSEC(MODEM_BACKEND_TTY_THREAD_POLL_DELAY));
44 	}
45 }
46 
modem_backend_tty_open(void * data)47 static int modem_backend_tty_open(void *data)
48 {
49 	struct modem_backend_tty *backend = (struct modem_backend_tty *)data;
50 
51 	if (atomic_test_and_set_bit(&backend->state, MODEM_BACKEND_TTY_STATE_RUN_BIT)) {
52 		return -EALREADY;
53 	}
54 
55 	backend->tty_fd = modem_backend_tty_open_bottom(backend->tty_path);
56 	if (backend->tty_fd < 0) {
57 		return -EPERM;
58 	}
59 
60 	k_thread_create(&backend->thread, backend->stack, backend->stack_size,
61 			modem_backend_tty_routine, backend, NULL, NULL,
62 			MODEM_BACKEND_TTY_THREAD_PRIO, 0, K_NO_WAIT);
63 
64 	modem_pipe_notify_opened(&backend->pipe);
65 	return 0;
66 }
67 
modem_backend_tty_transmit(void * data,const uint8_t * buf,size_t size)68 static int modem_backend_tty_transmit(void *data, const uint8_t *buf, size_t size)
69 {
70 	struct modem_backend_tty *backend = (struct modem_backend_tty *)data;
71 	int ret;
72 
73 	ret = nsi_host_write(backend->tty_fd, buf, size);
74 	modem_pipe_notify_transmit_idle(&backend->pipe);
75 	return ret;
76 }
77 
modem_backend_tty_receive(void * data,uint8_t * buf,size_t size)78 static int modem_backend_tty_receive(void *data, uint8_t *buf, size_t size)
79 {
80 	int ret;
81 	struct modem_backend_tty *backend = (struct modem_backend_tty *)data;
82 
83 	ret = nsi_host_read(backend->tty_fd, buf, size);
84 	return (ret < 0) ? 0 : ret;
85 }
86 
modem_backend_tty_close(void * data)87 static int modem_backend_tty_close(void *data)
88 {
89 	struct modem_backend_tty *backend = (struct modem_backend_tty *)data;
90 
91 	if (!atomic_test_and_clear_bit(&backend->state, MODEM_BACKEND_TTY_STATE_RUN_BIT)) {
92 		return -EALREADY;
93 	}
94 
95 	k_thread_join(&backend->thread, K_MSEC(MODEM_BACKEND_TTY_THREAD_RUN_PERIOD_MS * 2));
96 	nsi_host_close(backend->tty_fd);
97 	modem_pipe_notify_closed(&backend->pipe);
98 	return 0;
99 }
100 
101 static const struct modem_pipe_api modem_backend_tty_api = {
102 	.open = modem_backend_tty_open,
103 	.transmit = modem_backend_tty_transmit,
104 	.receive = modem_backend_tty_receive,
105 	.close = modem_backend_tty_close,
106 };
107 
modem_backend_tty_init(struct modem_backend_tty * backend,const struct modem_backend_tty_config * config)108 struct modem_pipe *modem_backend_tty_init(struct modem_backend_tty *backend,
109 					  const struct modem_backend_tty_config *config)
110 {
111 	__ASSERT_NO_MSG(backend != NULL);
112 	__ASSERT_NO_MSG(config != NULL);
113 	__ASSERT_NO_MSG(config->tty_path != NULL);
114 
115 	memset(backend, 0x00, sizeof(*backend));
116 	backend->tty_path = config->tty_path;
117 	backend->stack = config->stack;
118 	backend->stack_size = config->stack_size;
119 	atomic_set(&backend->state, 0);
120 	modem_pipe_init(&backend->pipe, backend, &modem_backend_tty_api);
121 	return &backend->pipe;
122 }
123