1 /*
2  * Copyright 2024 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/types.h>
9 #include <zephyr/sys/atomic.h>
10 
11 #include <zephyr/modem/pipe.h>
12 
13 #ifndef ZEPHYR_MODEM_UBX_
14 #define ZEPHYR_MODEM_UBX_
15 
16 #ifdef __cplusplus
17 extern "C" {
18 #endif
19 
20 /**
21  * @brief Modem Ubx
22  * @defgroup modem_ubx Modem Ubx
23  * @ingroup modem
24  * @{
25  */
26 
27 #define UBX_FRM_HEADER_SZ			6
28 #define UBX_FRM_FOOTER_SZ			2
29 #define UBX_FRM_SZ_WITHOUT_PAYLOAD		(UBX_FRM_HEADER_SZ + UBX_FRM_FOOTER_SZ)
30 #define UBX_FRM_SZ(payload_size)		(payload_size + UBX_FRM_SZ_WITHOUT_PAYLOAD)
31 
32 #define UBX_PREAMBLE_SYNC_CHAR_1		0xB5
33 #define UBX_PREAMBLE_SYNC_CHAR_2		0x62
34 
35 #define UBX_FRM_PREAMBLE_SYNC_CHAR_1_IDX	0
36 #define UBX_FRM_PREAMBLE_SYNC_CHAR_2_IDX	1
37 #define UBX_FRM_MSG_CLASS_IDX			2
38 #define UBX_FRM_MSG_ID_IDX			3
39 #define UBX_FRM_PAYLOAD_SZ_L_IDX		4
40 #define UBX_FRM_PAYLOAD_SZ_H_IDX		5
41 #define UBX_FRM_PAYLOAD_IDX			6
42 #define UBX_FRM_CHECKSUM_START_IDX		2
43 #define UBX_FRM_CHECKSUM_STOP_IDX(frame_len)	(frame_len - 2)
44 
45 #define UBX_PAYLOAD_SZ_MAX			256
46 #define UBX_FRM_SZ_MAX				UBX_FRM_SZ(UBX_PAYLOAD_SZ_MAX)
47 
48 struct ubx_frame {
49 	uint8_t preamble_sync_char_1;
50 	uint8_t preamble_sync_char_2;
51 	uint8_t message_class;
52 	uint8_t message_id;
53 	uint8_t payload_size_low;
54 	uint8_t payload_size_high;
55 	uint8_t payload_and_checksum[];
56 };
57 
58 struct modem_ubx_script {
59 	struct ubx_frame *request;
60 	struct ubx_frame *response;
61 	struct ubx_frame *match;
62 
63 	uint16_t retry_count;
64 	k_timeout_t timeout;
65 };
66 
67 struct modem_ubx {
68 	void *user_data;
69 
70 	atomic_t state;
71 
72 	uint8_t *receive_buf;
73 	uint16_t receive_buf_size;
74 
75 	uint8_t *work_buf;
76 	uint16_t work_buf_size;
77 	uint16_t work_buf_len;
78 	bool ubx_preamble_sync_chars_received;
79 
80 	const struct modem_ubx_script *script;
81 
82 	struct modem_pipe *pipe;
83 
84 	struct k_work send_work;
85 	struct k_work process_work;
86 	struct k_sem script_stopped_sem;
87 	struct k_sem script_running_sem;
88 };
89 
90 struct modem_ubx_config {
91 	void *user_data;
92 
93 	uint8_t *receive_buf;
94 	uint16_t receive_buf_size;
95 	uint8_t *work_buf;
96 	uint16_t work_buf_size;
97 };
98 
99 /**
100  * @brief Attach pipe to Modem Ubx
101  *
102  * @param ubx Modem Ubx instance
103  * @param pipe Pipe instance to attach Modem Ubx instance to
104  * @returns 0 if successful
105  * @returns negative errno code if failure
106  * @note Modem Ubx instance is enabled if successful
107  */
108 int modem_ubx_attach(struct modem_ubx *ubx, struct modem_pipe *pipe);
109 
110 /**
111  * @brief Release pipe from Modem Ubx instance
112  *
113  * @param ubx Modem Ubx instance
114  */
115 void modem_ubx_release(struct modem_ubx *ubx);
116 
117 /**
118  * @brief Initialize Modem Ubx instance
119  * @param ubx Modem Ubx instance
120  * @param config Configuration which shall be applied to the Modem Ubx instance
121  * @note Modem Ubx instance must be attached to a pipe instance
122  */
123 int modem_ubx_init(struct modem_ubx *ubx, const struct modem_ubx_config *config);
124 
125 /**
126  * @brief Writes the ubx frame in script.request and reads back its response (if available)
127  * @details For each ubx frame sent, the device responds in 0, 1 or both of the following ways:
128  *	1. The device sends back a UBX-ACK frame to denote 'acknowledge' and 'not-acknowledge'.
129  *		Note: the message id of UBX-ACK frame determines whether the device acknowledged.
130  *		Ex: when we send a UBX-CFG frame, the device responds with a UBX-ACK frame.
131  *	2. The device sends back the same frame that we sent to it, with it's payload populated.
132  *		It's used to get the current configuration corresponding to the frame that we sent.
133  *		Ex: frame types such as "get" or "poll" ubx frames respond this way.
134  *		This response (if received) is written to script.response.
135  *
136  *	This function writes the ubx frame in script.request then reads back it's response.
137  *	If script.match is not NULL, then every ubx frame received from the device is compared with
138  *	script.match to check if a match occurred. This could be used to match UBX-ACK frame sent
139  *	from the device by populating script.match with UBX-ACK that the script expects to receive.
140  *
141  *	The script terminates when either of the following happens:
142  *		1. script.match is successfully received and matched.
143  *		2. timeout (denoted by script.timeout) occurs.
144  * @param ubx Modem Ubx instance
145  * @param script Script to be executed
146  * @note The length of ubx frame in the script.request should not exceed UBX_FRM_SZ_MAX
147  * @note Modem Ubx instance must be attached to a pipe instance
148  * @returns 0 if device acknowledged via UBX-ACK and no "get" response was received
149  * @returns positive integer denoting the length of "get" response that was received
150  * @returns negative errno code if failure
151  */
152 int modem_ubx_run_script(struct modem_ubx *ubx, const struct modem_ubx_script *script);
153 
154 /**
155  * @brief Initialize ubx frame
156  * @param ubx_frame Ubx frame buffer
157  * @param ubx_frame_size Ubx frame buffer size
158  * @param msg_cls Message class
159  * @param msg_id Message id
160  * @param payload Payload buffer
161  * @param payload_size Payload buffer size
162  * @returns positive integer denoting the length of the ubx frame created
163  * @returns negative errno code if failure
164  */
165 int modem_ubx_create_frame(uint8_t *ubx_frame, uint16_t ubx_frame_size, uint8_t msg_cls,
166 			   uint8_t msg_id, const void *payload, uint16_t payload_size);
167 /**
168  * @}
169  */
170 
171 #ifdef __cplusplus
172 }
173 #endif
174 
175 #endif /* ZEPHYR_MODEM_UBX_ */
176