1 /*
2  * Copyright 2024 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/modem/ubx.h>
8 #include <string.h>
9 
10 #include <zephyr/logging/log.h>
11 LOG_MODULE_REGISTER(modem_ubx, CONFIG_MODEM_MODULES_LOG_LEVEL);
12 
13 #define MODEM_UBX_STATE_ATTACHED_BIT		0
14 
modem_ubx_validate_frame_size(uint16_t ubx_frame_size,uint8_t msg_cls,uint8_t msg_id,uint16_t payload_size)15 static int modem_ubx_validate_frame_size(uint16_t ubx_frame_size, uint8_t msg_cls, uint8_t msg_id,
16 					 uint16_t payload_size)
17 {
18 	if (ubx_frame_size > UBX_FRM_SZ_MAX ||
19 	    ubx_frame_size < UBX_FRM_SZ_WITHOUT_PAYLOAD ||
20 	    ubx_frame_size < UBX_FRM_SZ_WITHOUT_PAYLOAD + payload_size) {
21 		return -1;
22 	}
23 
24 	return 0;
25 }
26 
modem_ubx_create_frame(uint8_t * ubx_frame,uint16_t ubx_frame_size,uint8_t msg_cls,uint8_t msg_id,const void * payload,uint16_t payload_size)27 int modem_ubx_create_frame(uint8_t *ubx_frame, uint16_t ubx_frame_size, uint8_t msg_cls,
28 			   uint8_t msg_id, const void *payload, uint16_t payload_size)
29 {
30 	if (modem_ubx_validate_frame_size(ubx_frame_size, msg_cls, msg_id, payload_size)) {
31 		return -1;
32 	}
33 
34 	struct ubx_frame *frame = (struct ubx_frame *) ubx_frame;
35 
36 	frame->preamble_sync_char_1 = UBX_PREAMBLE_SYNC_CHAR_1;
37 	frame->preamble_sync_char_2 = UBX_PREAMBLE_SYNC_CHAR_2;
38 	frame->message_class = msg_cls;
39 	frame->message_id = msg_id;
40 	frame->payload_size_low = payload_size;
41 	frame->payload_size_high = payload_size >> 8;
42 
43 	memcpy(frame->payload_and_checksum, payload, payload_size);
44 
45 	uint16_t ubx_frame_len = payload_size + UBX_FRM_SZ_WITHOUT_PAYLOAD;
46 
47 	uint8_t ckA = 0, ckB = 0;
48 
49 	for (unsigned int i = UBX_FRM_CHECKSUM_START_IDX;
50 	     i < (UBX_FRM_CHECKSUM_STOP_IDX(ubx_frame_len)); i++) {
51 		ckA += ubx_frame[i];
52 		ckB += ckA;
53 	}
54 
55 	frame->payload_and_checksum[payload_size] = ckA;
56 	frame->payload_and_checksum[payload_size + 1] = ckB;
57 
58 	return ubx_frame_len;
59 }
60 
modem_ubx_reset_received_ubx_preamble_sync_chars(struct modem_ubx * ubx)61 static void modem_ubx_reset_received_ubx_preamble_sync_chars(struct modem_ubx *ubx)
62 {
63 	ubx->ubx_preamble_sync_chars_received = false;
64 }
65 
modem_ubx_reset_parser(struct modem_ubx * ubx)66 static void modem_ubx_reset_parser(struct modem_ubx *ubx)
67 {
68 	modem_ubx_reset_received_ubx_preamble_sync_chars(ubx);
69 }
70 
modem_ubx_get_payload_length(struct ubx_frame * frame)71 static int modem_ubx_get_payload_length(struct ubx_frame *frame)
72 {
73 	uint16_t payload_len = frame->payload_size_high;
74 
75 	payload_len = payload_len << 8;
76 
77 	return payload_len | frame->payload_size_low;
78 }
79 
modem_ubx_get_frame_length(struct ubx_frame * frame)80 static int modem_ubx_get_frame_length(struct ubx_frame *frame)
81 {
82 	return modem_ubx_get_payload_length(frame) + UBX_FRM_SZ_WITHOUT_PAYLOAD;
83 }
84 
modem_ubx_match_frame_type(struct ubx_frame * frame_1,struct ubx_frame * frame_2)85 static bool modem_ubx_match_frame_type(struct ubx_frame *frame_1, struct ubx_frame *frame_2)
86 {
87 	if (frame_1->message_class == frame_2->message_class
88 	    && frame_1->message_id == frame_2->message_id) {
89 		return true;
90 	} else {
91 		return false;
92 	}
93 }
94 
modem_ubx_match_frame_full(struct ubx_frame * frame_1,struct ubx_frame * frame_2)95 static bool modem_ubx_match_frame_full(struct ubx_frame *frame_1, struct ubx_frame *frame_2)
96 {
97 	if (modem_ubx_get_frame_length(frame_1) != modem_ubx_get_frame_length(frame_2)) {
98 		return false;
99 	}
100 
101 	if (memcmp(frame_1, frame_2, modem_ubx_get_frame_length(frame_1)) == 0) {
102 		return true;
103 	} else {
104 		return false;
105 	}
106 }
107 
modem_ubx_script_init(struct modem_ubx * ubx,const struct modem_ubx_script * script)108 static void modem_ubx_script_init(struct modem_ubx *ubx, const struct modem_ubx_script *script)
109 {
110 	ubx->script = script;
111 }
112 
modem_ubx_run_script_helper(struct modem_ubx * ubx,const struct modem_ubx_script * script)113 static int modem_ubx_run_script_helper(struct modem_ubx *ubx, const struct modem_ubx_script *script)
114 {
115 	int ret;
116 
117 	if (ubx->pipe == NULL) {
118 		return -EPERM;
119 	}
120 
121 	k_sem_reset(&ubx->script_stopped_sem);
122 
123 	modem_ubx_reset_parser(ubx);
124 
125 	k_work_submit(&ubx->send_work);
126 
127 	if (ubx->script->match == NULL) {
128 		return 0;
129 	}
130 
131 	ret = k_sem_take(&ubx->script_stopped_sem, script->timeout);
132 	if (ret < 0) {
133 		return ret;
134 	}
135 
136 	return 0;
137 }
138 
modem_ubx_run_script(struct modem_ubx * ubx,const struct modem_ubx_script * script)139 int modem_ubx_run_script(struct modem_ubx *ubx, const struct modem_ubx_script *script)
140 {
141 	int ret, attempt;
142 
143 	if (modem_ubx_get_frame_length(script->request) > UBX_FRM_SZ_MAX) {
144 		return -EFBIG;
145 	}
146 
147 	if (atomic_test_bit(&ubx->state, MODEM_UBX_STATE_ATTACHED_BIT) == false) {
148 		return -EPERM;
149 	}
150 
151 	ret = k_sem_take(&ubx->script_running_sem, K_FOREVER);
152 	if (ret < 0) {
153 		return ret;
154 	}
155 
156 	modem_ubx_script_init(ubx, script);
157 
158 	for (attempt = 0; attempt < script->retry_count; ++attempt) {
159 		ret = modem_ubx_run_script_helper(ubx, script);
160 		if (ret > -1) {
161 			LOG_INF("Successfully executed script on attempt: %d.", attempt);
162 			break;
163 		} else if (ret == -EPERM) {
164 			break;
165 		}
166 	}
167 
168 	if (ret < 0) {
169 		LOG_ERR("Failed to execute script successfully. Attempts: %d.", attempt);
170 		goto unlock;
171 	}
172 
173 unlock:
174 	k_sem_give(&ubx->script_running_sem);
175 
176 	return ret;
177 }
178 
modem_ubx_pipe_callback(struct modem_pipe * pipe,enum modem_pipe_event event,void * user_data)179 static void modem_ubx_pipe_callback(struct modem_pipe *pipe, enum modem_pipe_event event,
180 				    void *user_data)
181 {
182 	struct modem_ubx *ubx = (struct modem_ubx *)user_data;
183 
184 	if (event == MODEM_PIPE_EVENT_RECEIVE_READY) {
185 		k_work_submit(&ubx->process_work);
186 	}
187 }
188 
modem_ubx_send_handler(struct k_work * item)189 static void modem_ubx_send_handler(struct k_work *item)
190 {
191 	struct modem_ubx *ubx = CONTAINER_OF(item, struct modem_ubx, send_work);
192 	int ret, tx_frame_len;
193 
194 	tx_frame_len = modem_ubx_get_frame_length(ubx->script->request);
195 	ret = modem_pipe_transmit(ubx->pipe, (const uint8_t *) ubx->script->request, tx_frame_len);
196 	if (ret < tx_frame_len) {
197 		LOG_ERR("Ubx frame transmission failed. Returned %d.", ret);
198 		return;
199 	}
200 }
201 
modem_ubx_process_received_ubx_frame(struct modem_ubx * ubx)202 static int modem_ubx_process_received_ubx_frame(struct modem_ubx *ubx)
203 {
204 	int ret;
205 	struct ubx_frame *received = (struct ubx_frame *) ubx->work_buf;
206 
207 	if (modem_ubx_match_frame_full(received, ubx->script->match) == true) {
208 		/* Frame matched successfully. Terminate the script. */
209 		k_sem_give(&ubx->script_stopped_sem);
210 		ret = 0;
211 	} else if (modem_ubx_match_frame_type(received, ubx->script->request) == true) {
212 		/* Response received successfully. Script not ended. */
213 		memcpy(ubx->script->response, ubx->work_buf, ubx->work_buf_len);
214 		ret = -1;
215 	} else {
216 		/* Ignore the received frame. The device may automatically send periodic frames.
217 		 * These frames are not relevant for our script's execution and must be ignored.
218 		 */
219 		ret = -1;
220 	}
221 
222 	modem_ubx_reset_parser(ubx);
223 
224 	return ret;
225 }
226 
modem_ubx_process_received_byte(struct modem_ubx * ubx,uint8_t byte)227 static int modem_ubx_process_received_byte(struct modem_ubx *ubx, uint8_t byte)
228 {
229 	static uint8_t prev_byte;
230 	static uint16_t rx_ubx_frame_len;
231 
232 	if (ubx->ubx_preamble_sync_chars_received == false) {
233 		if (prev_byte == UBX_PREAMBLE_SYNC_CHAR_1 && byte == UBX_PREAMBLE_SYNC_CHAR_2) {
234 			ubx->ubx_preamble_sync_chars_received = true;
235 			ubx->work_buf[0] = UBX_PREAMBLE_SYNC_CHAR_1;
236 			ubx->work_buf[1] = UBX_PREAMBLE_SYNC_CHAR_2;
237 			ubx->work_buf_len = 2;
238 		}
239 	} else {
240 		ubx->work_buf[ubx->work_buf_len] = byte;
241 		++ubx->work_buf_len;
242 
243 		if (ubx->work_buf_len == UBX_FRM_HEADER_SZ) {
244 			uint16_t rx_ubx_payload_len = ubx->work_buf[UBX_FRM_PAYLOAD_SZ_H_IDX];
245 
246 			rx_ubx_payload_len = ubx->work_buf[UBX_FRM_PAYLOAD_SZ_H_IDX] << 8;
247 			rx_ubx_payload_len |= ubx->work_buf[UBX_FRM_PAYLOAD_SZ_L_IDX];
248 
249 			rx_ubx_frame_len = rx_ubx_payload_len + UBX_FRM_SZ_WITHOUT_PAYLOAD;
250 		}
251 
252 		if (ubx->work_buf_len == rx_ubx_frame_len) {
253 			return modem_ubx_process_received_ubx_frame(ubx);
254 		}
255 	}
256 
257 	prev_byte = byte;
258 
259 	return -1;
260 }
261 
modem_ubx_process_handler(struct k_work * item)262 static void modem_ubx_process_handler(struct k_work *item)
263 {
264 	struct modem_ubx *ubx = CONTAINER_OF(item, struct modem_ubx, process_work);
265 	int ret;
266 
267 	ret = modem_pipe_receive(ubx->pipe, ubx->receive_buf, ubx->receive_buf_size);
268 	if (ret < 1) {
269 		return;
270 	}
271 
272 	const size_t length = ret;
273 
274 	for (int i = 0; i < length; i++) {
275 		ret = modem_ubx_process_received_byte(ubx, ubx->receive_buf[i]);
276 		if (ret == 0) { /* Frame matched successfully. Terminate the script. */
277 			break;
278 		}
279 	}
280 
281 	k_work_submit(&ubx->process_work);
282 }
283 
modem_ubx_attach(struct modem_ubx * ubx,struct modem_pipe * pipe)284 int modem_ubx_attach(struct modem_ubx *ubx, struct modem_pipe *pipe)
285 {
286 	if (atomic_test_and_set_bit(&ubx->state, MODEM_UBX_STATE_ATTACHED_BIT) == true) {
287 		return 0;
288 	}
289 
290 	ubx->pipe = pipe;
291 	modem_pipe_attach(ubx->pipe, modem_ubx_pipe_callback, ubx);
292 	k_sem_give(&ubx->script_running_sem);
293 
294 	return 0;
295 }
296 
modem_ubx_release(struct modem_ubx * ubx)297 void modem_ubx_release(struct modem_ubx *ubx)
298 {
299 	struct k_work_sync sync;
300 
301 	if (atomic_test_and_clear_bit(&ubx->state, MODEM_UBX_STATE_ATTACHED_BIT) == false) {
302 		return;
303 	}
304 
305 	modem_pipe_release(ubx->pipe);
306 	k_work_cancel_sync(&ubx->send_work, &sync);
307 	k_work_cancel_sync(&ubx->process_work, &sync);
308 	k_sem_reset(&ubx->script_stopped_sem);
309 	k_sem_reset(&ubx->script_running_sem);
310 	ubx->work_buf_len = 0;
311 	modem_ubx_reset_parser(ubx);
312 	ubx->pipe = NULL;
313 }
314 
modem_ubx_init(struct modem_ubx * ubx,const struct modem_ubx_config * config)315 int modem_ubx_init(struct modem_ubx *ubx, const struct modem_ubx_config *config)
316 {
317 	__ASSERT_NO_MSG(ubx != NULL);
318 	__ASSERT_NO_MSG(config != NULL);
319 	__ASSERT_NO_MSG(config->receive_buf != NULL);
320 	__ASSERT_NO_MSG(config->receive_buf_size > 0);
321 	__ASSERT_NO_MSG(config->work_buf != NULL);
322 	__ASSERT_NO_MSG(config->work_buf_size > 0);
323 
324 	memset(ubx, 0x00, sizeof(*ubx));
325 	ubx->user_data = config->user_data;
326 
327 	ubx->receive_buf = config->receive_buf;
328 	ubx->receive_buf_size = config->receive_buf_size;
329 	ubx->work_buf = config->work_buf;
330 	ubx->work_buf_size = config->work_buf_size;
331 
332 	ubx->pipe = NULL;
333 
334 	k_work_init(&ubx->send_work, modem_ubx_send_handler);
335 	k_work_init(&ubx->process_work, modem_ubx_process_handler);
336 	k_sem_init(&ubx->script_stopped_sem, 0, 1);
337 	k_sem_init(&ubx->script_running_sem, 1, 1);
338 
339 	return 0;
340 }
341