1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "nrfs_backend_ipc_service.h"
8 
9 #include <internal/nrfs_backend.h>
10 #include <internal/nrfs_dispatcher.h>
11 
12 #include <zephyr/kernel.h>
13 #include <zephyr/sys/reboot.h>
14 #include <zephyr/sys_clock.h>
15 #include <zephyr/logging/log.h>
16 #include <zephyr/init.h>
17 
18 LOG_MODULE_REGISTER(NRFS_BACKEND, CONFIG_NRFS_BACKEND_LOG_LEVEL);
19 
20 #define MAX_PACKET_DATA_SIZE (CONFIG_NRFS_MAX_BACKEND_PACKET_SIZE)
21 
22 K_MSGQ_DEFINE(ipc_transmit_msgq, sizeof(struct ipc_data_packet),
23 							CONFIG_NRFS_BACKEND_TX_MSG_QUEUE_SIZE, 4);
24 
25 static struct k_work backend_send_work;
26 
27 static void ipc_sysctrl_ept_bound(void *priv);
28 static void ipc_sysctrl_ept_recv(const void *data, size_t size, void *priv);
29 
30 static K_EVENT_DEFINE(ipc_connected_event);
31 
32 #define IPC_INIT_DONE_EVENT (0x01)
33 
34 struct ipc_channel_config {
35 	const struct device *ipc_instance;
36 	struct ipc_ept_cfg *endpoint_config;
37 	struct ipc_ept ipc_ept;
38 	atomic_t status;
39 	bool enabled;
40 };
41 
42 static struct ipc_ept_cfg ipc_sysctrl_ept_cfg = {
43 	.name = "ipc_to_sysctrl",
44 	.cb = {
45 		.bound    = ipc_sysctrl_ept_bound,
46 		.received = ipc_sysctrl_ept_recv,
47 	},
48 };
49 
50 static struct ipc_channel_config ipc_cpusys_channel_config = {
51 	.ipc_instance	 = DEVICE_DT_GET(DT_ALIAS(ipc_to_cpusys)),
52 	.endpoint_config = &ipc_sysctrl_ept_cfg,
53 	.status		 = ATOMIC_INIT(NOT_CONNECTED),
54 	.enabled	 = true
55 };
56 
57 /**
58  * @brief nrfs backend error handler
59  *
60  * @param error_id The id of an error to handle.
61  * @param error additional error code if needed, if not needed use 0.
62  * @param fatal true if fatal error and needs special handling
63  */
nrfs_backend_error_handler(enum nrfs_backend_error error_id,int error,bool fatal)64 __weak void nrfs_backend_error_handler(enum nrfs_backend_error error_id, int error, bool fatal)
65 {
66 	switch (error_id) {
67 	case NRFS_ERROR_EPT_RECEIVE_DATA_TOO_LONG:
68 		LOG_ERR("Received data is too long. Config error.");
69 		break;
70 
71 	case NRFS_ERROR_NO_DATA_RECEIVED:
72 		LOG_ERR("No data in received message!");
73 		break;
74 
75 	case NRFS_ERROR_IPC_OPEN_INSTANCE:
76 		LOG_ERR("IPC open instance failure with error: %d", error);
77 		break;
78 
79 	case NRFS_ERROR_IPC_REGISTER_ENDPOINT:
80 		LOG_ERR("IPC register endpoint failure with error: %d", error);
81 		break;
82 
83 	default:
84 		LOG_ERR("Undefined error id: %d, error cause: %d", error_id, error);
85 		break;
86 	}
87 
88 	if (fatal) {
89 		nrfs_backend_fatal_error_handler(error_id);
90 	}
91 }
92 
ipc_sysctrl_ept_bound(void * priv)93 static void ipc_sysctrl_ept_bound(void *priv)
94 {
95 	LOG_DBG("Bound to sysctrl.");
96 	k_event_post(&ipc_connected_event, IPC_INIT_DONE_EVENT);
97 	atomic_set(&ipc_cpusys_channel_config.status, CONNECTED);
98 
99 	if (k_msgq_num_used_get(&ipc_transmit_msgq) > 0) {
100 		k_work_submit(&backend_send_work);
101 	}
102 }
103 
ipc_sysctrl_ept_recv(const void * data,size_t size,void * priv)104 static void ipc_sysctrl_ept_recv(const void *data, size_t size, void *priv)
105 {
106 	struct ipc_data_packet rx_data;
107 
108 	__ASSERT(size <= MAX_PACKET_DATA_SIZE, "Received data is too long. Config error.");
109 	if (size <= MAX_PACKET_DATA_SIZE) {
110 		rx_data.channel_id = IPC_CPUSYS_CHANNEL_ID;
111 		rx_data.size	   = size;
112 		if (data) {
113 			memcpy(rx_data.data, (uint8_t *)data, size);
114 			nrfs_dispatcher_notify(&rx_data.data, rx_data.size);
115 		} else {
116 			nrfs_backend_error_handler(NRFS_ERROR_NO_DATA_RECEIVED, 0, false);
117 		}
118 	} else {
119 		nrfs_backend_error_handler(NRFS_ERROR_EPT_RECEIVE_DATA_TOO_LONG, 0, true);
120 	}
121 }
122 
nrfs_backend_send_work(struct k_work * item)123 static void nrfs_backend_send_work(struct k_work *item)
124 {
125 	struct ipc_data_packet data_to_send;
126 
127 	LOG_DBG("Sending data from workqueue");
128 	while (k_msgq_get(&ipc_transmit_msgq, &data_to_send, K_NO_WAIT) == 0) {
129 		ipc_service_send(&ipc_cpusys_channel_config.ipc_ept, &data_to_send.data,
130 				 data_to_send.size);
131 	}
132 }
133 
134 /**
135  *  @brief Initialize ipc channel
136  *
137  *  @return -EINVAL when instance configuration is invalid.
138  *  @return -EIO when no backend is registered.
139  *  @return -EALREADY when the instance is already opened (or being opened).
140  *  @return -EBUSY when the instance is busy.
141  *  @return 0 on success
142  */
ipc_channel_init(void)143 static int ipc_channel_init(void)
144 {
145 	struct ipc_channel_config *ch_cfg;
146 	int ret = 0;
147 
148 	k_work_init(&backend_send_work, nrfs_backend_send_work);
149 	ch_cfg = &ipc_cpusys_channel_config;
150 
151 	ret = ipc_service_open_instance(ch_cfg->ipc_instance);
152 	if ((ret < 0) && (ret != -EALREADY)) {
153 		nrfs_backend_error_handler(NRFS_ERROR_IPC_OPEN_INSTANCE, ret, false);
154 		return ret;
155 	}
156 
157 	LOG_DBG("ipc_service_open_instance() done.");
158 
159 	ret = ipc_service_register_endpoint(ch_cfg->ipc_instance,
160 					    &ch_cfg->ipc_ept,
161 					    ch_cfg->endpoint_config);
162 	if (ret < 0) {
163 		nrfs_backend_error_handler(NRFS_ERROR_IPC_REGISTER_ENDPOINT, ret, false);
164 		return ret;
165 	}
166 
167 	LOG_DBG("ipc_service_register_endpoint() done.");
168 
169 	return ret;
170 }
171 
nrfs_backend_send(void * message,size_t size)172 nrfs_err_t nrfs_backend_send(void *message, size_t size)
173 {
174 	return nrfs_backend_send_ex(message, size, K_NO_WAIT, false);
175 }
176 
nrfs_backend_send_ex(void * message,size_t size,k_timeout_t timeout,bool high_prio)177 nrfs_err_t nrfs_backend_send_ex(void *message, size_t size, k_timeout_t timeout, bool high_prio)
178 {
179 	if (!k_is_in_isr() && nrfs_backend_connected()) {
180 		return ipc_service_send(&ipc_cpusys_channel_config.ipc_ept, message, size) ?
181 			NRFS_SUCCESS : NRFS_ERR_IPC;
182 	} else if (size <= MAX_PACKET_DATA_SIZE) {
183 		int err;
184 		struct ipc_data_packet tx_data;
185 
186 		tx_data.channel_id = IPC_CPUSYS_CHANNEL_ID;
187 		tx_data.size	   = size;
188 		memcpy(tx_data.data, (uint8_t *)message, size);
189 
190 		err = k_msgq_put(&ipc_transmit_msgq, &tx_data, timeout);
191 		if (err) {
192 			return NRFS_ERR_IPC;
193 		}
194 
195 		if (nrfs_backend_connected()) {
196 			err = k_work_submit(&backend_send_work);
197 		}
198 
199 		return err >= 0 ? 0 : NRFS_ERR_IPC;
200 	}
201 
202 	LOG_ERR("Trying to send %d bytes where max is %d.", size, MAX_PACKET_DATA_SIZE);
203 
204 	return NRFS_ERR_IPC;
205 }
206 
nrfs_backend_connected(void)207 bool nrfs_backend_connected(void)
208 {
209 	return atomic_get(&ipc_cpusys_channel_config.status) == CONNECTED;
210 }
211 
nrfs_backend_wait_for_connection(k_timeout_t timeout)212 int nrfs_backend_wait_for_connection(k_timeout_t timeout)
213 {
214 	uint32_t events;
215 
216 	if (nrfs_backend_connected()) {
217 		return 0;
218 	}
219 
220 	events = k_event_wait(&ipc_connected_event, IPC_INIT_DONE_EVENT, false, timeout);
221 
222 	return (events == IPC_INIT_DONE_EVENT ? 0 : (-EAGAIN));
223 }
224 
nrfs_backend_fatal_error_handler(enum nrfs_backend_error error_id)225 __weak void nrfs_backend_fatal_error_handler(enum nrfs_backend_error error_id)
226 {
227 	LOG_ERR("Fatal error: %d rebooting...", error_id);
228 	sys_reboot(SYS_REBOOT_WARM);
229 }
230 
231 SYS_INIT(ipc_channel_init, POST_KERNEL, CONFIG_NRFS_BACKEND_IPC_SERVICE_INIT_PRIO);
232