Zephyr API Documentation 4.4.99
A Scalable Open Source RTOS
Loading...
Searching...
No Matches
modem_cellular.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2026 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
11
12 #ifndef ZEPHYR_INCLUDE_DRIVERS_CELLULAR_INTERNAL_H_
13 #define ZEPHYR_INCLUDE_DRIVERS_CELLULAR_INTERNAL_H_
14
17#include <zephyr/drivers/uart.h>
18#include <zephyr/drivers/gpio.h>
19#include <zephyr/pm/device.h>
20#include <zephyr/modem/chat.h>
21#include <zephyr/modem/cmux.h>
22#include <zephyr/modem/pipe.h>
24#include <zephyr/modem/ppp.h>
25
31
32#ifdef __cplusplus
33extern "C" {
34#endif
35
45
46#define MODEM_CELLULAR_DATA_IMEI_LEN (16)
47#define MODEM_CELLULAR_DATA_MODEL_ID_LEN (65)
48#define MODEM_CELLULAR_DATA_IMSI_LEN (23)
49#define MODEM_CELLULAR_DATA_ICCID_LEN (22)
50#define MODEM_CELLULAR_DATA_MANUFACTURER_LEN (65)
51#define MODEM_CELLULAR_DATA_FW_VERSION_LEN (65)
52#define MODEM_CELLULAR_DATA_APN_LEN (32)
53#define MODEM_CELLULAR_MAX_APN_CMDS (2)
54#define MODEM_CELLULAR_APN_BUF_SIZE (64)
55
56/* Zephyr networking interface states:
57 * NET_IF_LOWER_UP: Carrier is on in AWAIT_REGISTERED and REGISTERED
58 * NET_IF_DORMANT: Interface is dormant in every state except REGISTERED
59 */
60enum modem_cellular_state {
61 MODEM_CELLULAR_STATE_IDLE = 0,
62 MODEM_CELLULAR_STATE_RESET_PULSE,
63 MODEM_CELLULAR_STATE_AWAIT_RESET,
64 MODEM_CELLULAR_STATE_POWER_ON_PULSE,
65 MODEM_CELLULAR_STATE_AWAIT_POWER_ON,
66 MODEM_CELLULAR_STATE_SET_BAUDRATE,
67 MODEM_CELLULAR_STATE_RUN_INIT_SCRIPT,
68 MODEM_CELLULAR_STATE_CONNECT_CMUX,
69 MODEM_CELLULAR_STATE_OPEN_DLCI1,
70 MODEM_CELLULAR_STATE_OPEN_DLCI2,
71 MODEM_CELLULAR_STATE_WAIT_FOR_APN,
72 MODEM_CELLULAR_STATE_RUN_APN_SCRIPT,
73 MODEM_CELLULAR_STATE_RUN_DIAL_SCRIPT,
74 MODEM_CELLULAR_STATE_AWAIT_REGISTERED,
75 MODEM_CELLULAR_STATE_REGISTERED,
76 MODEM_CELLULAR_STATE_AWAIT_PPP_DEAD,
77 MODEM_CELLULAR_STATE_INIT_POWER_OFF,
78 MODEM_CELLULAR_STATE_RUN_SHUTDOWN_SCRIPT,
79 MODEM_CELLULAR_STATE_POWER_OFF_PULSE,
80 MODEM_CELLULAR_STATE_AWAIT_POWER_OFF,
81};
82
83enum modem_cellular_event {
84 MODEM_CELLULAR_EVENT_RESUME = 0,
85 MODEM_CELLULAR_EVENT_SUSPEND,
86 MODEM_CELLULAR_EVENT_SCRIPT_SUCCESS,
87 MODEM_CELLULAR_EVENT_SCRIPT_FAILED,
88 MODEM_CELLULAR_EVENT_CMUX_CONNECTED,
89 MODEM_CELLULAR_EVENT_CMUX_DISCONNECTED,
90 MODEM_CELLULAR_EVENT_DLCI1_OPENED,
91 MODEM_CELLULAR_EVENT_DLCI2_OPENED,
92 MODEM_CELLULAR_EVENT_TIMEOUT,
93 MODEM_CELLULAR_EVENT_REGISTERED,
94 MODEM_CELLULAR_EVENT_DEREGISTERED,
95 MODEM_CELLULAR_EVENT_BUS_OPENED,
96 MODEM_CELLULAR_EVENT_BUS_CLOSED,
97 MODEM_CELLULAR_EVENT_PPP_DEAD,
98 MODEM_CELLULAR_EVENT_MODEM_READY,
99 MODEM_CELLULAR_EVENT_APN_SET,
100 MODEM_CELLULAR_EVENT_RING,
101};
102
103struct modem_cellular_event_cb {
106 void *user_data;
107};
108
109struct modem_cellular_data {
110 /* UART backend */
111 struct modem_pipe *uart_pipe;
112 struct modem_backend_uart uart_backend;
113 uint8_t uart_backend_receive_buf[CONFIG_MODEM_CELLULAR_UART_BUFFER_SIZES];
114 uint8_t uart_backend_transmit_buf[CONFIG_MODEM_CELLULAR_UART_BUFFER_SIZES];
115 uint32_t original_baudrate;
116
117 /* CMUX */
118 struct modem_cmux cmux;
119 uint8_t cmux_receive_buf[MODEM_CMUX_WORK_BUFFER_SIZE];
120 uint8_t cmux_transmit_buf[MODEM_CMUX_WORK_BUFFER_SIZE];
121
122 struct modem_cmux_dlci dlci1;
123 struct modem_cmux_dlci dlci2;
124 struct modem_pipe *dlci1_pipe;
125 struct modem_pipe *dlci2_pipe;
126 /* Points to dlci1_pipe or NULL. Used for shutdown script if not NULL */
127 struct modem_pipe *cmd_pipe;
128 uint8_t dlci1_receive_buf[MODEM_CMUX_WORK_BUFFER_SIZE];
129 /* DLCI 2 is only used for chat scripts. */
130 uint8_t dlci2_receive_buf[MODEM_CMUX_WORK_BUFFER_SIZE];
131
132 /* Modem chat */
133 struct modem_chat chat;
134 uint8_t chat_receive_buf[CONFIG_MODEM_CELLULAR_CHAT_BUFFER_SIZE];
135 uint8_t *chat_delimiter;
136 uint8_t *chat_filter;
137 uint8_t *chat_argv[32];
138 uint8_t script_failure_counter;
139
140 /* Status */
141 enum cellular_registration_status registration_status_gsm;
142 enum cellular_registration_status registration_status_gprs;
143 enum cellular_registration_status registration_status_lte;
144 uint8_t rssi;
145 uint8_t rsrp;
146 uint8_t rsrq;
147 uint8_t imei[MODEM_CELLULAR_DATA_IMEI_LEN];
148 uint8_t model_id[MODEM_CELLULAR_DATA_MODEL_ID_LEN];
149 uint8_t imsi[MODEM_CELLULAR_DATA_IMSI_LEN];
150 uint8_t iccid[MODEM_CELLULAR_DATA_ICCID_LEN];
151 uint8_t manufacturer[MODEM_CELLULAR_DATA_MANUFACTURER_LEN];
152 uint8_t fw_version[MODEM_CELLULAR_DATA_FW_VERSION_LEN];
153 uint8_t apn[MODEM_CELLULAR_DATA_APN_LEN];
154
155 struct modem_chat_script_chat apn_chats[MODEM_CELLULAR_MAX_APN_CMDS];
156 struct modem_chat_script apn_script;
157 char apn_buf[MODEM_CELLULAR_MAX_APN_CMDS][MODEM_CELLULAR_APN_BUF_SIZE];
158
159 /* PPP */
160 struct modem_ppp *ppp;
161 struct net_mgmt_event_callback net_mgmt_event_callback;
162
163 enum modem_cellular_state state;
164 const struct device *dev;
165 struct k_work_delayable timeout_work;
166
167 /* Power management */
168 struct k_sem suspended_sem;
169
170 /* Event dispatcher */
171 struct k_work event_dispatch_work;
172 uint8_t event_buf[8];
173 struct k_pipe event_pipe;
174
175 struct k_mutex api_lock;
176 struct modem_cellular_event_cb cb;
177
178 /* Ring interrupt */
179 struct gpio_callback ring_gpio_cb;
180};
181
182struct modem_cellular_user_pipe {
183 struct modem_cmux_dlci dlci;
184 uint8_t dlci_address;
185 uint8_t *dlci_receive_buf;
186 uint16_t dlci_receive_buf_size;
187 struct modem_pipe *pipe;
188 struct modem_pipelink *pipelink;
189};
190
191struct modem_cellular_config_scripts {
192 const struct modem_chat_script *init;
193 const struct modem_chat_script *dial;
194 const struct modem_chat_script *periodic;
195 const struct modem_chat_script *shutdown;
196 const struct modem_chat_script *set_baudrate;
197};
198
199struct modem_cellular_config {
200 const struct device *uart;
201 struct gpio_dt_spec power_gpio;
202 struct gpio_dt_spec reset_gpio;
203 struct gpio_dt_spec wake_gpio;
204 struct gpio_dt_spec ring_gpio;
205 struct gpio_dt_spec dtr_gpio;
206 uint16_t power_pulse_duration_ms;
207 uint16_t reset_pulse_duration_ms;
208 uint16_t startup_time_ms;
209 uint16_t shutdown_time_ms;
210 bool autostarts;
211 bool hold_reset_on_suspend;
212 bool reset_on_resume;
213 bool reset_on_recovery;
214 bool cmux_enable_runtime_power_save;
215 bool cmux_close_pipe_on_power_save;
216 bool use_default_pdp_context;
217 bool use_default_apn;
218 k_timeout_t cmux_idle_timeout;
219 const struct modem_cellular_config_scripts *scripts;
220 struct modem_cellular_user_pipe *user_pipes;
221 uint8_t user_pipes_size;
222};
223
227int modem_cellular_init(const struct device *dev);
228
229int modem_cellular_pm_action(const struct device *dev, enum pm_device_action action);
230
231extern const struct cellular_driver_api modem_cellular_api;
232
233void modem_cellular_chat_callback_handler(struct modem_chat *chat,
234 enum modem_chat_script_result result,
235 void *user_data);
236
237void modem_cellular_chat_on_imei(struct modem_chat *chat, char **argv, uint16_t argc,
238 void *user_data);
239void modem_cellular_chat_on_cgmm(struct modem_chat *chat, char **argv, uint16_t argc,
240 void *user_data);
241void modem_cellular_chat_on_csq(struct modem_chat *chat, char **argv, uint16_t argc,
242 void *user_data);
243void modem_cellular_chat_on_cesq(struct modem_chat *chat, char **argv, uint16_t argc,
244 void *user_data);
245void modem_cellular_chat_on_iccid(struct modem_chat *chat, char **argv, uint16_t argc,
246 void *user_data);
247void modem_cellular_chat_on_imsi(struct modem_chat *chat, char **argv, uint16_t argc,
248 void *user_data);
249void modem_cellular_chat_on_cgmi(struct modem_chat *chat, char **argv, uint16_t argc,
250 void *user_data);
251void modem_cellular_chat_on_cgmr(struct modem_chat *chat, char **argv, uint16_t argc,
252 void *user_data);
253void modem_cellular_chat_on_cxreg(struct modem_chat *chat, char **argv, uint16_t argc,
254 void *user_data);
255void modem_cellular_chat_on_cgev(struct modem_chat *chat, char **argv, uint16_t argc,
256 void *user_data);
257void modem_cellular_chat_on_modem_ready(struct modem_chat *chat, char **argv, uint16_t argc,
258 void *user_data);
259
261
270
271#define MODEM_CELLULAR_INST_NAME(name, inst) \
272 CONCAT(name, _, DT_DRV_COMPAT, inst)
273
274#define MODEM_CELLULAR_DEFINE_USER_PIPE_DATA(inst, name, size) \
275 MODEM_PIPELINK_DT_INST_DEFINE(inst, name); \
276 static uint8_t MODEM_CELLULAR_INST_NAME(name, inst)[size] \
277
278#define MODEM_CELLULAR_INIT_USER_PIPE(_inst, _name, _dlci_address) \
279 { \
280 .dlci_address = _dlci_address, \
281 .dlci_receive_buf = MODEM_CELLULAR_INST_NAME(_name, _inst), \
282 .dlci_receive_buf_size = sizeof(MODEM_CELLULAR_INST_NAME(_name, _inst)), \
283 .pipelink = MODEM_PIPELINK_DT_INST_GET(_inst, _name), \
284 }
285
286#define MODEM_CELLULAR_DEFINE_USER_PIPES(inst, ...) \
287 static struct modem_cellular_user_pipe MODEM_CELLULAR_INST_NAME(user_pipes, inst)[] = { \
288 __VA_ARGS__ \
289 }
290
291#define MODEM_CELLULAR_GET_USER_PIPES(inst) \
292 MODEM_CELLULAR_INST_NAME(user_pipes, inst)
293
294/* Extract the first argument (pipe name) from a pair */
295#define MODEM_CELLULAR_GET_PIPE_NAME_ARG(arg1, ...) arg1
296
297/* Extract the second argument (DLCI address) from a pair */
298#define MODEM_CELLULAR_GET_DLCI_ADDRESS_ARG(arg1, arg2, ...) arg2
299
300/* Define user pipe data using instance and extracted pipe name */
301#define MODEM_CELLULAR_DEFINE_USER_PIPE_DATA_HELPER(_args, inst) \
302 MODEM_CELLULAR_DEFINE_USER_PIPE_DATA(inst, \
303 MODEM_CELLULAR_GET_PIPE_NAME_ARG _args, \
304 CONFIG_MODEM_CELLULAR_USER_PIPE_BUFFER_SIZES)
305
306/* Initialize user pipe using instance, extracted pipe name, and DLCI address */
307#define MODEM_CELLULAR_INIT_USER_PIPE_HELPER(_args, inst) \
308 MODEM_CELLULAR_INIT_USER_PIPE(inst, \
309 MODEM_CELLULAR_GET_PIPE_NAME_ARG _args, \
310 MODEM_CELLULAR_GET_DLCI_ADDRESS_ARG _args)
311
312/*
313 * Define and initialize user pipes dynamically
314 * Takes an instance and pairs of (pipe name, DLCI address)
315 */
316#define MODEM_CELLULAR_DEFINE_AND_INIT_USER_PIPES(inst, ...) \
317 FOR_EACH_FIXED_ARG(MODEM_CELLULAR_DEFINE_USER_PIPE_DATA_HELPER, \
318 (;), inst, __VA_ARGS__); \
319 MODEM_CELLULAR_DEFINE_USER_PIPES( \
320 inst, \
321 FOR_EACH_FIXED_ARG(MODEM_CELLULAR_INIT_USER_PIPE_HELPER, \
322 (,), inst, __VA_ARGS__) \
323 );
324
325/* Define common chat matches for cellular modems to reduce copy-pasting */
326#define MODEM_CELLULAR_COMMON_CHAT_MATCHES() \
327 MODEM_CHAT_MATCH_DEFINE(ok_match, "OK", "", NULL); \
328 MODEM_CHAT_MATCHES_DEFINE(__maybe_unused allow_match, \
329 MODEM_CHAT_MATCH("OK", "", NULL), \
330 MODEM_CHAT_MATCH("ERROR", "", NULL)); \
331 MODEM_CHAT_MATCH_DEFINE(imei_match __maybe_unused, \
332 "", "", modem_cellular_chat_on_imei); \
333 MODEM_CHAT_MATCH_DEFINE(cgmm_match __maybe_unused, \
334 "", "", modem_cellular_chat_on_cgmm); \
335 MODEM_CHAT_MATCH_DEFINE(csq_match __maybe_unused, \
336 "+CSQ: ", ",", modem_cellular_chat_on_csq); \
337 MODEM_CHAT_MATCH_DEFINE(cesq_match __maybe_unused, \
338 "+CESQ: ", ",", modem_cellular_chat_on_cesq); \
339 MODEM_CHAT_MATCH_DEFINE(qccid_match __maybe_unused, \
340 "+QCCID: ", "", modem_cellular_chat_on_iccid); \
341 MODEM_CHAT_MATCH_DEFINE(iccid_match __maybe_unused, \
342 "+ICCID: ", "", modem_cellular_chat_on_iccid); \
343 MODEM_CHAT_MATCH_DEFINE(ccid_match __maybe_unused, \
344 "+CCID: ", "", modem_cellular_chat_on_iccid); \
345 MODEM_CHAT_MATCH_DEFINE(cimi_match __maybe_unused, \
346 "", "", modem_cellular_chat_on_imsi); \
347 MODEM_CHAT_MATCH_DEFINE(cgmi_match __maybe_unused, \
348 "", "", modem_cellular_chat_on_cgmi); \
349 MODEM_CHAT_MATCH_DEFINE(cgmr_match __maybe_unused, \
350 "", "", modem_cellular_chat_on_cgmr); \
351 MODEM_CHAT_MATCH_DEFINE(connect_match __maybe_unused, \
352 "CONNECT", "", NULL); \
353 MODEM_CHAT_MATCHES_DEFINE(__maybe_unused abort_matches, \
354 MODEM_CHAT_MATCH("ERROR", "", NULL)); \
355 MODEM_CHAT_MATCHES_DEFINE(__maybe_unused dial_abort_matches, \
356 MODEM_CHAT_MATCH("ERROR", "", NULL), \
357 MODEM_CHAT_MATCH("BUSY", "", NULL), \
358 MODEM_CHAT_MATCH("NO ANSWER", "", NULL), \
359 MODEM_CHAT_MATCH("NO CARRIER", "", NULL), \
360 MODEM_CHAT_MATCH("NO DIALTONE", "", NULL))
361
362/* Helper to define modem instance */
363#define MODEM_CELLULAR_DEFINE_INSTANCE(inst, power_ms, reset_ms, startup_ms, shutdown_ms, start, \
364 _scripts) \
365 BUILD_ASSERT(_scripts != NULL, "scripts must be non-NULL"); \
366 static const struct modem_cellular_config MODEM_CELLULAR_INST_NAME(config, inst) = { \
367 .uart = DEVICE_DT_GET(DT_INST_BUS(inst)), \
368 .power_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, mdm_power_gpios, {}), \
369 .reset_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, mdm_reset_gpios, {}), \
370 .wake_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, mdm_wake_gpios, {}), \
371 .ring_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, mdm_ring_gpios, {}), \
372 .dtr_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, mdm_dtr_gpios, {}), \
373 .power_pulse_duration_ms = (power_ms), \
374 .reset_pulse_duration_ms = (reset_ms), \
375 .startup_time_ms = (startup_ms), \
376 .shutdown_time_ms = (shutdown_ms), \
377 .autostarts = DT_INST_PROP_OR(inst, autostarts, (start)), \
378 .hold_reset_on_suspend = \
379 DT_INST_ENUM_HAS_VALUE(inst, zephyr_mdm_reset_behavior, hold_on_suspend), \
380 .reset_on_resume = DT_INST_ENUM_HAS_VALUE(inst, zephyr_mdm_reset_behavior, \
381 toggle_on_resume), \
382 .reset_on_recovery = DT_INST_ENUM_HAS_VALUE(inst, zephyr_mdm_reset_behavior, \
383 toggle_on_recovery), \
384 .cmux_enable_runtime_power_save = \
385 DT_INST_PROP_OR(inst, cmux_enable_runtime_power_save, 0), \
386 .cmux_close_pipe_on_power_save = \
387 DT_INST_PROP_OR(inst, cmux_close_pipe_on_power_save, 0), \
388 .use_default_pdp_context = DT_INST_PROP_OR(inst, zephyr_use_default_pdp_ctx, 0), \
389 .use_default_apn = DT_INST_PROP_OR(inst, zephyr_use_default_apn, 0), \
390 .cmux_idle_timeout = K_MSEC(DT_INST_PROP_OR(inst, cmux_idle_timeout_ms, 0)), \
391 .scripts = _scripts, \
392 .user_pipes = MODEM_CELLULAR_GET_USER_PIPES(inst), \
393 .user_pipes_size = ARRAY_SIZE(MODEM_CELLULAR_GET_USER_PIPES(inst)), \
394 }; \
395 \
396 PM_DEVICE_DT_INST_DEFINE(inst, modem_cellular_pm_action); \
397 \
398 DEVICE_DT_INST_DEFINE(inst, modem_cellular_init, PM_DEVICE_DT_INST_GET(inst), \
399 &MODEM_CELLULAR_INST_NAME(data, inst), \
400 &MODEM_CELLULAR_INST_NAME(config, inst), POST_KERNEL, \
401 CONFIG_MODEM_CELLULAR_INIT_PRIORITY, &modem_cellular_api);
402
404
405#ifdef __cplusplus
406}
407#endif
408
410
411#endif /* ZEPHYR_INCLUDE_DRIVERS_CELLULAR_INTERNAL_H_ */
Main header file for cellular modem driver API.
Main header file for GPIO driver API.
Main header file for UART driver API.
cellular_registration_status
Cellular registration status (3GPP TS 27.007).
Definition cellular.h:109
uint32_t cellular_event_mask_t
Definition cellular.h:147
void(* cellular_event_cb_t)(const struct device *dev, enum cellular_event event, const void *payload, void *user_data)
Prototype for cellular event callbacks.
Definition cellular.h:176
modem_chat_script_result
Definition chat.h:156
pm_device_action
Device PM actions.
Definition device.h:144
state
Definition parser_state.h:29
int shutdown(int sock, int how)
__UINT32_TYPE__ uint32_t
Definition stdint.h:90
__UINT8_TYPE__ uint8_t
Definition stdint.h:88
__UINT16_TYPE__ uint16_t
Definition stdint.h:89
Cellular driver API.
Definition cellular.h:211
Runtime device structure (in ROM) per driver instance.
Definition device.h:513
Chat instance internal context.
Definition chat.h:223