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#include <zephyr/sys/atomic.h>
26
32
33#ifdef __cplusplus
34extern "C" {
35#endif
36
46
47#define MODEM_CELLULAR_DATA_IMEI_LEN (16)
48#define MODEM_CELLULAR_DATA_MODEL_ID_LEN (65)
49#define MODEM_CELLULAR_DATA_IMSI_LEN (23)
50#define MODEM_CELLULAR_DATA_ICCID_LEN (22)
51#define MODEM_CELLULAR_DATA_MANUFACTURER_LEN (65)
52#define MODEM_CELLULAR_DATA_FW_VERSION_LEN (65)
53#define MODEM_CELLULAR_DATA_APN_LEN (32)
54#define MODEM_CELLULAR_MAX_APN_CMDS (2)
55#define MODEM_CELLULAR_APN_BUF_SIZE (64)
56
57/* Zephyr networking interface states:
58 * NET_IF_LOWER_UP: Carrier is on in AWAIT_REGISTERED and REGISTERED
59 * NET_IF_DORMANT: Interface is dormant in every state except REGISTERED
60 */
61enum modem_cellular_state {
62 MODEM_CELLULAR_STATE_IDLE = 0,
63 MODEM_CELLULAR_STATE_RESET_PULSE,
64 MODEM_CELLULAR_STATE_AWAIT_RESET,
65 MODEM_CELLULAR_STATE_POWER_ON_PULSE,
66 MODEM_CELLULAR_STATE_AWAIT_POWER_ON,
67 MODEM_CELLULAR_STATE_SET_BAUDRATE,
68 MODEM_CELLULAR_STATE_RUN_INIT_SCRIPT,
69 MODEM_CELLULAR_STATE_CONNECT_CMUX,
70 MODEM_CELLULAR_STATE_OPEN_DLCI1,
71 MODEM_CELLULAR_STATE_OPEN_DLCI2,
72 MODEM_CELLULAR_STATE_WAIT_FOR_APN,
73 MODEM_CELLULAR_STATE_RUN_APN_SCRIPT,
74 MODEM_CELLULAR_STATE_RUN_NETWORK_SCRIPT,
75 MODEM_CELLULAR_STATE_RUN_DIAL_SCRIPT,
76 MODEM_CELLULAR_STATE_AWAIT_REGISTERED,
77 MODEM_CELLULAR_STATE_REGISTERED,
78 MODEM_CELLULAR_STATE_AWAIT_PPP_DEAD,
79 MODEM_CELLULAR_STATE_INIT_POWER_OFF,
80 MODEM_CELLULAR_STATE_RUN_SHUTDOWN_SCRIPT,
81 MODEM_CELLULAR_STATE_POWER_OFF_PULSE,
82 MODEM_CELLULAR_STATE_AWAIT_POWER_OFF,
83};
84
85enum modem_cellular_event {
86 MODEM_CELLULAR_EVENT_RESUME = 0,
87 MODEM_CELLULAR_EVENT_SUSPEND,
88 MODEM_CELLULAR_EVENT_SCRIPT_SUCCESS,
89 MODEM_CELLULAR_EVENT_SCRIPT_FAILED,
90 MODEM_CELLULAR_EVENT_CMUX_CONNECTED,
91 MODEM_CELLULAR_EVENT_CMUX_DISCONNECTED,
92 MODEM_CELLULAR_EVENT_DLCI1_OPENED,
93 MODEM_CELLULAR_EVENT_DLCI2_OPENED,
94 MODEM_CELLULAR_EVENT_TIMEOUT,
95 MODEM_CELLULAR_EVENT_REGISTERED,
96 MODEM_CELLULAR_EVENT_DEREGISTERED,
97 MODEM_CELLULAR_EVENT_BUS_OPENED,
98 MODEM_CELLULAR_EVENT_BUS_CLOSED,
99 MODEM_CELLULAR_EVENT_PPP_DEAD,
100 MODEM_CELLULAR_EVENT_MODEM_READY,
101 MODEM_CELLULAR_EVENT_APN_SET,
102 MODEM_CELLULAR_EVENT_RING,
103 MODEM_CELLULAR_EVENT_PERIODIC_KICK,
104};
105
106struct modem_cellular_event_cb {
109 void *user_data;
110};
111
112struct modem_cellular_data {
113 /* UART backend */
114 struct modem_pipe *uart_pipe;
115 struct modem_backend_uart uart_backend;
116 uint8_t uart_backend_receive_buf[CONFIG_MODEM_CELLULAR_UART_BUFFER_SIZES];
117 uint8_t uart_backend_transmit_buf[CONFIG_MODEM_CELLULAR_UART_BUFFER_SIZES];
118 uint32_t original_baudrate;
119
120 /* CMUX */
121 struct modem_cmux cmux;
122 uint8_t cmux_receive_buf[MODEM_CMUX_WORK_BUFFER_SIZE];
123 uint8_t cmux_transmit_buf[MODEM_CMUX_WORK_BUFFER_SIZE];
124
125 struct modem_cmux_dlci dlci1;
126 struct modem_cmux_dlci dlci2;
127 struct modem_pipe *dlci1_pipe;
128 struct modem_pipe *dlci2_pipe;
129 /* Points to dlci1_pipe or NULL. Used for shutdown script if not NULL */
130 struct modem_pipe *cmd_pipe;
131 uint8_t dlci1_receive_buf[MODEM_CMUX_WORK_BUFFER_SIZE];
132 /* DLCI 2 is only used for chat scripts. */
133 uint8_t dlci2_receive_buf[MODEM_CMUX_WORK_BUFFER_SIZE];
134
135 /* Modem chat */
136 struct modem_chat chat;
137 uint8_t chat_receive_buf[CONFIG_MODEM_CELLULAR_CHAT_BUFFER_SIZE];
138 uint8_t *chat_delimiter;
139 uint8_t *chat_filter;
140 uint8_t *chat_argv[32];
141 uint8_t script_failure_counter;
142
143 /* Status */
144 enum cellular_registration_status registration_status_gsm;
145 enum cellular_registration_status registration_status_gprs;
146 enum cellular_registration_status registration_status_lte;
147 enum cellular_access_technology access_tech;
148 uint8_t rssi;
149 uint8_t rsrp;
150 uint8_t rsrq;
151 uint8_t imei[MODEM_CELLULAR_DATA_IMEI_LEN];
152 uint8_t model_id[MODEM_CELLULAR_DATA_MODEL_ID_LEN];
153 uint8_t imsi[MODEM_CELLULAR_DATA_IMSI_LEN];
154 uint8_t iccid[MODEM_CELLULAR_DATA_ICCID_LEN];
155 uint8_t manufacturer[MODEM_CELLULAR_DATA_MANUFACTURER_LEN];
156 uint8_t fw_version[MODEM_CELLULAR_DATA_FW_VERSION_LEN];
157 uint8_t apn[MODEM_CELLULAR_DATA_APN_LEN];
158
159 struct modem_chat_script_chat apn_chats[MODEM_CELLULAR_MAX_APN_CMDS];
160 struct modem_chat_script apn_script;
161 char apn_buf[MODEM_CELLULAR_MAX_APN_CMDS][MODEM_CELLULAR_APN_BUF_SIZE];
162
163 /* PPP */
164 struct modem_ppp *ppp;
165 struct net_mgmt_event_callback net_mgmt_event_callback;
166
167 enum modem_cellular_state state;
168 const struct device *dev;
169 struct k_work_delayable timeout_work;
170
171 /* Power management */
172 struct k_sem suspended_sem;
173
174 /* Event dispatcher */
175 struct k_work event_dispatch_work;
176 uint8_t event_buf[8];
177 struct k_pipe event_pipe;
178
179 struct k_mutex api_lock;
180 struct modem_cellular_event_cb cb;
181
182 /* Ring interrupt */
183 struct gpio_callback ring_gpio_cb;
184
186 atomic_t periodic_paused;
188 bool periodic_timeout_skipped;
189};
190
191struct modem_cellular_user_pipe {
192 struct modem_cmux_dlci dlci;
193 uint8_t dlci_address;
194 uint8_t *dlci_receive_buf;
195 uint16_t dlci_receive_buf_size;
196 struct modem_pipe *pipe;
197 struct modem_pipelink *pipelink;
198};
199
213struct modem_cellular_config_scripts {
214 const struct modem_chat_script *set_baudrate;
215 const struct modem_chat_script *init;
216 const struct modem_chat_script *network;
217 const struct modem_chat_script *dial;
218 const struct modem_chat_script *periodic;
219 const struct modem_chat_script *shutdown;
220};
221
225struct modem_cellular_vendor_config {
226 struct modem_cellular_config_scripts scripts;
227 struct {
228 const struct modem_chat_match *matches;
229 uint16_t size;
230 } unsol_matches;
231 uint16_t power_pulse_duration_ms;
232 uint16_t reset_pulse_duration_ms;
233 uint16_t startup_time_ms;
234 uint16_t shutdown_time_ms;
235 /* Force the `autostart` property regardless of devicetree */
236 bool force_autostart;
237};
238
239struct modem_cellular_config {
240 const struct device *uart;
241 const struct modem_cellular_vendor_config *vendor;
242 struct gpio_dt_spec power_gpio;
243 struct gpio_dt_spec reset_gpio;
244 struct gpio_dt_spec wake_gpio;
245 struct gpio_dt_spec ring_gpio;
246 struct gpio_dt_spec dtr_gpio;
247 bool autostarts;
248 bool hold_reset_on_suspend;
249 bool reset_on_resume;
250 bool reset_on_recovery;
251 bool cmux_enable_runtime_power_save;
252 bool cmux_close_pipe_on_power_save;
253 bool cmux_no_powersave_handshake;
254 bool use_default_pdp_context;
255 bool use_default_apn;
256 k_timeout_t cmux_idle_timeout;
257 struct modem_cellular_user_pipe *user_pipes;
258 uint8_t user_pipes_size;
259};
260
264int modem_cellular_init(const struct device *dev);
265
266int modem_cellular_pm_action(const struct device *dev, enum pm_device_action action);
267
268extern const struct cellular_driver_api modem_cellular_api;
269
270void modem_cellular_emit_event(struct modem_cellular_data *data, enum cellular_event evt,
271 const void *payload);
272
273void modem_cellular_chat_callback_handler(struct modem_chat *chat,
274 enum modem_chat_script_result result,
275 void *user_data);
276
277void modem_cellular_chat_on_imei(struct modem_chat *chat, char **argv, uint16_t argc,
278 void *user_data);
279void modem_cellular_chat_on_cgmm(struct modem_chat *chat, char **argv, uint16_t argc,
280 void *user_data);
281void modem_cellular_chat_on_csq(struct modem_chat *chat, char **argv, uint16_t argc,
282 void *user_data);
283void modem_cellular_chat_on_cesq(struct modem_chat *chat, char **argv, uint16_t argc,
284 void *user_data);
285void modem_cellular_chat_on_iccid(struct modem_chat *chat, char **argv, uint16_t argc,
286 void *user_data);
287void modem_cellular_chat_on_imsi(struct modem_chat *chat, char **argv, uint16_t argc,
288 void *user_data);
289void modem_cellular_chat_on_cgmi(struct modem_chat *chat, char **argv, uint16_t argc,
290 void *user_data);
291void modem_cellular_chat_on_cgmr(struct modem_chat *chat, char **argv, uint16_t argc,
292 void *user_data);
293void modem_cellular_chat_on_cxreg(struct modem_chat *chat, char **argv, uint16_t argc,
294 void *user_data);
295void modem_cellular_chat_on_cgev(struct modem_chat *chat, char **argv, uint16_t argc,
296 void *user_data);
297void modem_cellular_chat_on_modem_ready(struct modem_chat *chat, char **argv, uint16_t argc,
298 void *user_data);
299
301
310
311#define MODEM_CELLULAR_INST_NAME(name, inst) \
312 CONCAT(name, _, DT_DRV_COMPAT, inst)
313
314#define MODEM_CELLULAR_DEFINE_USER_PIPE_DATA(inst, name, size) \
315 MODEM_PIPELINK_DT_INST_DEFINE(inst, name); \
316 static uint8_t MODEM_CELLULAR_INST_NAME(name, inst)[size] \
317
318#define MODEM_CELLULAR_INIT_USER_PIPE(_inst, _name, _dlci_address) \
319 { \
320 .dlci_address = _dlci_address, \
321 .dlci_receive_buf = MODEM_CELLULAR_INST_NAME(_name, _inst), \
322 .dlci_receive_buf_size = sizeof(MODEM_CELLULAR_INST_NAME(_name, _inst)), \
323 .pipelink = MODEM_PIPELINK_DT_INST_GET(_inst, _name), \
324 }
325
326#define MODEM_CELLULAR_DEFINE_USER_PIPES(inst, ...) \
327 static struct modem_cellular_user_pipe MODEM_CELLULAR_INST_NAME(user_pipes, inst)[] = { \
328 __VA_ARGS__ \
329 }
330
331#define MODEM_CELLULAR_GET_USER_PIPES(inst) \
332 MODEM_CELLULAR_INST_NAME(user_pipes, inst)
333
334/* Extract the first argument (pipe name) from a pair */
335#define MODEM_CELLULAR_GET_PIPE_NAME_ARG(arg1, ...) arg1
336
337/* Extract the second argument (DLCI address) from a pair */
338#define MODEM_CELLULAR_GET_DLCI_ADDRESS_ARG(arg1, arg2, ...) arg2
339
340/* Define user pipe data using instance and extracted pipe name */
341#define MODEM_CELLULAR_DEFINE_USER_PIPE_DATA_HELPER(_args, inst) \
342 MODEM_CELLULAR_DEFINE_USER_PIPE_DATA(inst, \
343 MODEM_CELLULAR_GET_PIPE_NAME_ARG _args, \
344 CONFIG_MODEM_CELLULAR_USER_PIPE_BUFFER_SIZES)
345
346/* Initialize user pipe using instance, extracted pipe name, and DLCI address */
347#define MODEM_CELLULAR_INIT_USER_PIPE_HELPER(_args, inst) \
348 MODEM_CELLULAR_INIT_USER_PIPE(inst, \
349 MODEM_CELLULAR_GET_PIPE_NAME_ARG _args, \
350 MODEM_CELLULAR_GET_DLCI_ADDRESS_ARG _args)
351
352/*
353 * Define and initialize user pipes dynamically
354 * Takes an instance and pairs of (pipe name, DLCI address)
355 */
356#define MODEM_CELLULAR_DEFINE_AND_INIT_USER_PIPES(inst, ...) \
357 FOR_EACH_FIXED_ARG(MODEM_CELLULAR_DEFINE_USER_PIPE_DATA_HELPER, \
358 (;), inst, __VA_ARGS__); \
359 MODEM_CELLULAR_DEFINE_USER_PIPES( \
360 inst, \
361 FOR_EACH_FIXED_ARG(MODEM_CELLULAR_INIT_USER_PIPE_HELPER, \
362 (,), inst, __VA_ARGS__) \
363 );
364
365/* Define common chat matches for cellular modems to reduce copy-pasting */
366#define MODEM_CELLULAR_COMMON_CHAT_MATCHES() \
367 MODEM_CHAT_MATCH_DEFINE(ok_match, "OK", "", NULL); \
368 MODEM_CHAT_MATCHES_DEFINE(__maybe_unused allow_match, \
369 MODEM_CHAT_MATCH("OK", "", NULL), \
370 MODEM_CHAT_MATCH("ERROR", "", NULL)); \
371 MODEM_CHAT_MATCH_DEFINE(imei_match __maybe_unused, \
372 "", "", modem_cellular_chat_on_imei); \
373 MODEM_CHAT_MATCH_DEFINE(cgmm_match __maybe_unused, \
374 "", "", modem_cellular_chat_on_cgmm); \
375 MODEM_CHAT_MATCH_DEFINE(csq_match __maybe_unused, \
376 "+CSQ: ", ",", modem_cellular_chat_on_csq); \
377 MODEM_CHAT_MATCH_DEFINE(cesq_match __maybe_unused, \
378 "+CESQ: ", ",", modem_cellular_chat_on_cesq); \
379 MODEM_CHAT_MATCH_DEFINE(qccid_match __maybe_unused, \
380 "+QCCID: ", "", modem_cellular_chat_on_iccid); \
381 MODEM_CHAT_MATCH_DEFINE(iccid_match __maybe_unused, \
382 "+ICCID: ", "", modem_cellular_chat_on_iccid); \
383 MODEM_CHAT_MATCH_DEFINE(ccid_match __maybe_unused, \
384 "+CCID: ", "", modem_cellular_chat_on_iccid); \
385 MODEM_CHAT_MATCH_DEFINE(cimi_match __maybe_unused, \
386 "", "", modem_cellular_chat_on_imsi); \
387 MODEM_CHAT_MATCH_DEFINE(cgmi_match __maybe_unused, \
388 "", "", modem_cellular_chat_on_cgmi); \
389 MODEM_CHAT_MATCH_DEFINE(cgmr_match __maybe_unused, \
390 "", "", modem_cellular_chat_on_cgmr); \
391 MODEM_CHAT_MATCH_DEFINE(connect_match __maybe_unused, \
392 "CONNECT", "", NULL); \
393 MODEM_CHAT_MATCHES_DEFINE(__maybe_unused abort_matches, \
394 MODEM_CHAT_MATCH("ERROR", "", NULL)); \
395 MODEM_CHAT_MATCHES_DEFINE(__maybe_unused dial_abort_matches, \
396 MODEM_CHAT_MATCH("ERROR", "", NULL), \
397 MODEM_CHAT_MATCH("BUSY", "", NULL), \
398 MODEM_CHAT_MATCH("NO ANSWER", "", NULL), \
399 MODEM_CHAT_MATCH("NO CARRIER", "", NULL), \
400 MODEM_CHAT_MATCH("NO DIALTONE", "", NULL))
401
402/* Common URC match entries shared by every cellular modem driver.
403 * Use as the body of a MODEM_CHAT_MATCHES_DEFINE() expansion so vendor
404 * drivers can extend the table with their own vendor-specific URCs.
405 */
406#define MODEM_CELLULAR_COMMON_UNSOL_MATCHES \
407 MODEM_CHAT_MATCH("+CREG: ", ",", modem_cellular_chat_on_cxreg), \
408 MODEM_CHAT_MATCH("+CEREG: ", ",", modem_cellular_chat_on_cxreg), \
409 MODEM_CHAT_MATCH("+CGREG: ", ",", modem_cellular_chat_on_cxreg), \
410 MODEM_CHAT_MATCH("+CGEV: ", ",", modem_cellular_chat_on_cgev), \
411 MODEM_CHAT_MATCH("APP RDY", "", modem_cellular_chat_on_modem_ready), \
412 MODEM_CHAT_MATCH("Ready", "", modem_cellular_chat_on_modem_ready)
413
414/* Helper to define modem instance */
415#define MODEM_CELLULAR_DEFINE_INSTANCE(inst, vendor_config) \
416 BUILD_ASSERT(vendor_config != NULL, "vendor_config must be non-NULL"); \
417 static const struct modem_cellular_config MODEM_CELLULAR_INST_NAME(config, inst) = { \
418 .uart = DEVICE_DT_GET(DT_INST_BUS(inst)), \
419 .vendor = vendor_config, \
420 .power_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, mdm_power_gpios, {}), \
421 .reset_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, mdm_reset_gpios, {}), \
422 .wake_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, mdm_wake_gpios, {}), \
423 .ring_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, mdm_ring_gpios, {}), \
424 .dtr_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, mdm_dtr_gpios, {}), \
425 .autostarts = DT_INST_PROP(inst, autostarts), \
426 .hold_reset_on_suspend = \
427 DT_INST_ENUM_HAS_VALUE(inst, zephyr_mdm_reset_behavior, hold_on_suspend), \
428 .reset_on_resume = DT_INST_ENUM_HAS_VALUE(inst, zephyr_mdm_reset_behavior, \
429 toggle_on_resume), \
430 .reset_on_recovery = DT_INST_ENUM_HAS_VALUE(inst, zephyr_mdm_reset_behavior, \
431 toggle_on_recovery), \
432 .cmux_enable_runtime_power_save = \
433 DT_INST_PROP_OR(inst, cmux_enable_runtime_power_save, 0), \
434 .cmux_close_pipe_on_power_save = \
435 DT_INST_PROP_OR(inst, cmux_close_pipe_on_power_save, 0), \
436 .cmux_no_powersave_handshake = \
437 DT_INST_PROP_OR(inst, cmux_no_powersave_handshake, 0), \
438 .use_default_pdp_context = DT_INST_PROP_OR(inst, zephyr_use_default_pdp_ctx, 0), \
439 .use_default_apn = DT_INST_PROP_OR(inst, zephyr_use_default_apn, 0), \
440 .cmux_idle_timeout = K_MSEC(DT_INST_PROP_OR(inst, cmux_idle_timeout_ms, 0)), \
441 .user_pipes = MODEM_CELLULAR_GET_USER_PIPES(inst), \
442 .user_pipes_size = ARRAY_SIZE(MODEM_CELLULAR_GET_USER_PIPES(inst)), \
443 }; \
444 \
445 PM_DEVICE_DT_INST_DEFINE(inst, modem_cellular_pm_action); \
446 \
447 DEVICE_DT_INST_DEFINE(inst, modem_cellular_init, PM_DEVICE_DT_INST_GET(inst), \
448 &MODEM_CELLULAR_INST_NAME(data, inst), \
449 &MODEM_CELLULAR_INST_NAME(config, inst), POST_KERNEL, \
450 CONFIG_MODEM_CELLULAR_INIT_PRIORITY, &modem_cellular_api);
451
453
455
460
478
495
497
498#ifdef __cplusplus
499}
500#endif
501
502#endif /* ZEPHYR_INCLUDE_DRIVERS_CELLULAR_INTERNAL_H_ */
Header file for the Atomic operations API.
Main header file for cellular modem driver API.
Main header file for GPIO driver API.
Main header file for UART driver API.
long atomic_t
Atomic integer variable.
Definition atomic_types.h:31
cellular_access_technology
Cellular access technologies (3GPP TS 27.007 AcT).
Definition cellular.h:33
cellular_event
Events emitted asynchronously by a cellular driver.
Definition cellular.h:137
cellular_registration_status
Cellular registration status (3GPP TS 27.007).
Definition cellular.h:109
uint32_t cellular_event_mask_t
Definition cellular.h:149
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:202
int cellular_modem_pause_periodic_script(const struct device *dev)
Pause the cellular_modem driver's periodic chat script.
int cellular_modem_resume_periodic_script(const struct device *dev)
Resume the cellular_modem driver's periodic chat script.
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:237
Runtime device structure (in ROM) per driver instance.
Definition device.h:513
Chat instance internal context.
Definition chat.h:223