modem_cellular: Add support to define a shutdown script for cellular modems

Add support to define a shutdown script for cellular modems, i.e.
AT commands to shutdown the modem. This allows to shutdown the
modem much quicker compared to using a power pulse which saves power.

Signed-off-by: Niklas Gürtler <niklas.guertler@e-obs.de>
This commit is contained in:
Niklas Gürtler 2024-09-27 17:51:49 +02:00 committed by Carles Cufí
parent 193023b075
commit 93330b07f2

View file

@ -61,6 +61,7 @@ enum modem_cellular_state {
MODEM_CELLULAR_STATE_AWAIT_REGISTERED,
MODEM_CELLULAR_STATE_CARRIER_ON,
MODEM_CELLULAR_STATE_INIT_POWER_OFF,
MODEM_CELLULAR_STATE_RUN_SHUTDOWN_SCRIPT,
MODEM_CELLULAR_STATE_POWER_OFF_PULSE,
MODEM_CELLULAR_STATE_AWAIT_POWER_OFF,
};
@ -96,6 +97,8 @@ struct modem_cellular_data {
struct modem_cmux_dlci dlci2;
struct modem_pipe *dlci1_pipe;
struct modem_pipe *dlci2_pipe;
/* Points to dlci2_pipe or NULL. Used for shutdown script if not NULL */
struct modem_pipe *cmd_pipe;
uint8_t dlci1_receive_buf[CONFIG_MODEM_CELLULAR_CMUX_MAX_FRAME_SIZE];
/* DLCI 2 is only used for chat scripts. */
uint8_t dlci2_receive_buf[CONFIG_MODEM_CELLULAR_CHAT_BUFFER_SIZES];
@ -159,6 +162,7 @@ struct modem_cellular_config {
const struct modem_chat_script *init_chat_script;
const struct modem_chat_script *dial_chat_script;
const struct modem_chat_script *periodic_chat_script;
const struct modem_chat_script *shutdown_chat_script;
const struct modem_chat_script *set_baudrate_chat_script;
struct modem_cellular_user_pipe *user_pipes;
uint8_t user_pipes_size;
@ -193,6 +197,8 @@ static const char *modem_cellular_state_str(enum modem_cellular_state state)
return "carrier on";
case MODEM_CELLULAR_STATE_INIT_POWER_OFF:
return "init power off";
case MODEM_CELLULAR_STATE_RUN_SHUTDOWN_SCRIPT:
return "run shutdown script";
case MODEM_CELLULAR_STATE_POWER_OFF_PULSE:
return "power off pulse";
case MODEM_CELLULAR_STATE_AWAIT_POWER_OFF:
@ -569,6 +575,20 @@ static void modem_cellular_delegate_event(struct modem_cellular_data *data,
k_work_submit(&data->event_dispatch_work);
}
static void modem_cellular_begin_power_off_pulse(struct modem_cellular_data *data)
{
const struct modem_cellular_config *config =
(const struct modem_cellular_config *)data->dev->config;
modem_pipe_close_async(data->uart_pipe);
if (modem_cellular_gpio_is_enabled(&config->power_gpio)) {
modem_cellular_enter_state(data, MODEM_CELLULAR_STATE_POWER_OFF_PULSE);
} else {
modem_cellular_enter_state(data, MODEM_CELLULAR_STATE_IDLE);
}
}
static int modem_cellular_on_idle_state_enter(struct modem_cellular_data *data)
{
const struct modem_cellular_config *config =
@ -941,6 +961,7 @@ static void modem_cellular_open_dlci2_event_handler(struct modem_cellular_data *
{
switch (evt) {
case MODEM_CELLULAR_EVENT_DLCI2_OPENED:
data->cmd_pipe = data->dlci2_pipe;
modem_cellular_enter_state(data, MODEM_CELLULAR_STATE_RUN_DIAL_SCRIPT);
break;
@ -1089,7 +1110,6 @@ static int modem_cellular_on_carrier_on_state_leave(struct modem_cellular_data *
static int modem_cellular_on_init_power_off_state_enter(struct modem_cellular_data *data)
{
modem_pipe_close_async(data->uart_pipe);
modem_cellular_start_timer(data, K_MSEC(2000));
return 0;
}
@ -1102,12 +1122,15 @@ static void modem_cellular_init_power_off_event_handler(struct modem_cellular_da
switch (evt) {
case MODEM_CELLULAR_EVENT_TIMEOUT:
if (modem_cellular_gpio_is_enabled(&config->power_gpio)) {
modem_cellular_enter_state(data, MODEM_CELLULAR_STATE_POWER_OFF_PULSE);
/* Shutdown script can only be used if cmd_pipe is available, i.e. we are not in
* some intermediary state without a pipe for commands available
*/
if (config->shutdown_chat_script != NULL && data->cmd_pipe != NULL) {
modem_cellular_enter_state(data, MODEM_CELLULAR_STATE_RUN_SHUTDOWN_SCRIPT);
break;
}
modem_cellular_enter_state(data, MODEM_CELLULAR_STATE_IDLE);
modem_cellular_begin_power_off_pulse(data);
break;
default:
@ -1123,11 +1146,50 @@ static int modem_cellular_on_init_power_off_state_leave(struct modem_cellular_da
return 0;
}
static int modem_cellular_on_run_shutdown_script_state_enter(struct modem_cellular_data *data)
{
const struct modem_cellular_config *config =
(const struct modem_cellular_config *)data->dev->config;
modem_chat_attach(&data->chat, data->cmd_pipe);
return modem_chat_run_script_async(&data->chat, config->shutdown_chat_script);
}
static void modem_cellular_run_shutdown_script_event_handler(struct modem_cellular_data *data,
enum modem_cellular_event evt)
{
switch (evt) {
case MODEM_CELLULAR_EVENT_SCRIPT_FAILED:
data->cmd_pipe = NULL;
/* If shutdown by software failed, try by power pulse if possible */
modem_cellular_begin_power_off_pulse(data);
break;
case MODEM_CELLULAR_EVENT_SCRIPT_SUCCESS:
modem_pipe_close_async(data->uart_pipe);
data->cmd_pipe = NULL;
modem_cellular_enter_state(data, MODEM_CELLULAR_STATE_IDLE);
break;
default:
break;
}
}
static int modem_cellular_on_run_shutdown_script_state_leave(struct modem_cellular_data *data)
{
modem_chat_release(&data->chat);
return 0;
}
static int modem_cellular_on_power_off_pulse_state_enter(struct modem_cellular_data *data)
{
const struct modem_cellular_config *config =
(const struct modem_cellular_config *)data->dev->config;
data->cmd_pipe = NULL;
gpio_pin_set_dt(&config->power_gpio, 1);
modem_cellular_start_timer(data, K_MSEC(config->power_pulse_duration_ms));
return 0;
@ -1235,6 +1297,10 @@ static int modem_cellular_on_state_enter(struct modem_cellular_data *data)
ret = modem_cellular_on_init_power_off_state_enter(data);
break;
case MODEM_CELLULAR_STATE_RUN_SHUTDOWN_SCRIPT:
ret = modem_cellular_on_run_shutdown_script_state_enter(data);
break;
case MODEM_CELLULAR_STATE_POWER_OFF_PULSE:
ret = modem_cellular_on_power_off_pulse_state_enter(data);
break;
@ -1292,6 +1358,10 @@ static int modem_cellular_on_state_leave(struct modem_cellular_data *data)
ret = modem_cellular_on_init_power_off_state_leave(data);
break;
case MODEM_CELLULAR_STATE_RUN_SHUTDOWN_SCRIPT:
ret = modem_cellular_on_run_shutdown_script_state_leave(data);
break;
case MODEM_CELLULAR_STATE_POWER_OFF_PULSE:
ret = modem_cellular_on_power_off_pulse_state_leave(data);
break;
@ -1387,6 +1457,9 @@ static void modem_cellular_event_handler(struct modem_cellular_data *data,
modem_cellular_init_power_off_event_handler(data, evt);
break;
case MODEM_CELLULAR_STATE_RUN_SHUTDOWN_SCRIPT:
modem_cellular_run_shutdown_script_event_handler(data, evt);
case MODEM_CELLULAR_STATE_POWER_OFF_PULSE:
modem_cellular_power_off_pulse_event_handler(data, evt);
break;
@ -1655,6 +1728,8 @@ static int modem_cellular_init(const struct device *dev)
data->uart_pipe = modem_backend_uart_init(&data->uart_backend,
&uart_backend_config);
data->cmd_pipe = NULL;
}
{
@ -2290,7 +2365,8 @@ MODEM_CHAT_SCRIPT_DEFINE(sqn_gm02s_periodic_chat_script,
set_baudrate_script, \
init_script, \
dial_script, \
periodic_script) \
periodic_script, \
shutdown_script) \
static const struct modem_cellular_config MODEM_CELLULAR_INST_NAME(config, inst) = { \
.uart = DEVICE_DT_GET(DT_INST_BUS(inst)), \
.power_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, mdm_power_gpios, {}), \
@ -2304,6 +2380,7 @@ MODEM_CHAT_SCRIPT_DEFINE(sqn_gm02s_periodic_chat_script,
.init_chat_script = (init_script), \
.dial_chat_script = (dial_script), \
.periodic_chat_script = (periodic_script), \
.shutdown_chat_script = (shutdown_script), \
.user_pipes = MODEM_CELLULAR_GET_USER_PIPES(inst), \
.user_pipes_size = ARRAY_SIZE(MODEM_CELLULAR_GET_USER_PIPES(inst)), \
}; \
@ -2332,7 +2409,7 @@ MODEM_CHAT_SCRIPT_DEFINE(sqn_gm02s_periodic_chat_script,
NULL, \
&quectel_bg95_init_chat_script, \
&quectel_bg95_dial_chat_script, \
&quectel_bg95_periodic_chat_script)
&quectel_bg95_periodic_chat_script, NULL)
#define MODEM_CELLULAR_DEVICE_QUECTEL_EG25_G(inst) \
MODEM_PPP_DEFINE(MODEM_CELLULAR_INST_NAME(ppp, inst), NULL, 98, 1500, 64); \
@ -2351,7 +2428,7 @@ MODEM_CHAT_SCRIPT_DEFINE(sqn_gm02s_periodic_chat_script,
NULL, \
&quectel_eg25_g_init_chat_script, \
&quectel_eg25_g_dial_chat_script, \
&quectel_eg25_g_periodic_chat_script)
&quectel_eg25_g_periodic_chat_script, NULL)
#define MODEM_CELLULAR_DEVICE_SIMCOM_SIM7080(inst) \
MODEM_PPP_DEFINE(MODEM_CELLULAR_INST_NAME(ppp, inst), NULL, 98, 1500, 64); \
@ -2370,7 +2447,7 @@ MODEM_CHAT_SCRIPT_DEFINE(sqn_gm02s_periodic_chat_script,
NULL, \
&simcom_sim7080_init_chat_script, \
&simcom_sim7080_dial_chat_script, \
&simcom_sim7080_periodic_chat_script)
&simcom_sim7080_periodic_chat_script, NULL)
#define MODEM_CELLULAR_DEVICE_U_BLOX_SARA_R4(inst) \
MODEM_PPP_DEFINE(MODEM_CELLULAR_INST_NAME(ppp, inst), NULL, 98, 1500, 64); \
@ -2389,7 +2466,7 @@ MODEM_CHAT_SCRIPT_DEFINE(sqn_gm02s_periodic_chat_script,
NULL, \
&u_blox_sara_r4_init_chat_script, \
&u_blox_sara_r4_dial_chat_script, \
&u_blox_sara_r4_periodic_chat_script)
&u_blox_sara_r4_periodic_chat_script, NULL)
#define MODEM_CELLULAR_DEVICE_U_BLOX_SARA_R5(inst) \
MODEM_PPP_DEFINE(MODEM_CELLULAR_INST_NAME(ppp, inst), NULL, 98, 1500, 64); \
@ -2408,7 +2485,7 @@ MODEM_CHAT_SCRIPT_DEFINE(sqn_gm02s_periodic_chat_script,
NULL, \
&u_blox_sara_r5_init_chat_script, \
&u_blox_sara_r5_dial_chat_script, \
&u_blox_sara_r5_periodic_chat_script)
&u_blox_sara_r5_periodic_chat_script, NULL)
#define MODEM_CELLULAR_DEVICE_U_BLOX_LARA_R6(inst) \
MODEM_PPP_DEFINE(MODEM_CELLULAR_INST_NAME(ppp, inst), NULL, 98, 1500, 64); \
@ -2427,7 +2504,8 @@ MODEM_CHAT_SCRIPT_DEFINE(sqn_gm02s_periodic_chat_script,
&u_blox_lara_r6_set_baudrate_chat_script, \
&u_blox_lara_r6_init_chat_script, \
&u_blox_lara_r6_dial_chat_script, \
&u_blox_lara_r6_periodic_chat_script)
&u_blox_lara_r6_periodic_chat_script, \
NULL )
#define MODEM_CELLULAR_DEVICE_SWIR_HL7800(inst) \
MODEM_PPP_DEFINE(MODEM_CELLULAR_INST_NAME(ppp, inst), NULL, 98, 1500, 64); \
@ -2446,7 +2524,7 @@ MODEM_CHAT_SCRIPT_DEFINE(sqn_gm02s_periodic_chat_script,
NULL, \
&swir_hl7800_init_chat_script, \
&swir_hl7800_dial_chat_script, \
&swir_hl7800_periodic_chat_script)
&swir_hl7800_periodic_chat_script, NULL)
#define MODEM_CELLULAR_DEVICE_TELIT_ME910G1(inst) \
MODEM_PPP_DEFINE(MODEM_CELLULAR_INST_NAME(ppp, inst), NULL, 98, 1500, 64); \
@ -2464,7 +2542,8 @@ MODEM_CHAT_SCRIPT_DEFINE(sqn_gm02s_periodic_chat_script,
NULL, \
&telit_me910g1_init_chat_script, \
&telit_me910g1_dial_chat_script, \
&telit_me910g1_periodic_chat_script)
&telit_me910g1_periodic_chat_script, \
NULL)
#define MODEM_CELLULAR_DEVICE_NORDIC_NRF91_SLM(inst) \
MODEM_PPP_DEFINE(MODEM_CELLULAR_INST_NAME(ppp, inst), NULL, 98, 1500, 1500); \
@ -2481,7 +2560,7 @@ MODEM_CHAT_SCRIPT_DEFINE(sqn_gm02s_periodic_chat_script,
NULL, \
&nordic_nrf91_slm_init_chat_script, \
&nordic_nrf91_slm_dial_chat_script, \
&nordic_nrf91_slm_periodic_chat_script)
&nordic_nrf91_slm_periodic_chat_script, NULL)
#define MODEM_CELLULAR_DEVICE_SQN_GM02S(inst) \
MODEM_PPP_DEFINE(MODEM_CELLULAR_INST_NAME(ppp, inst), NULL, 98, 1500, 64); \
@ -2500,7 +2579,7 @@ MODEM_CHAT_SCRIPT_DEFINE(sqn_gm02s_periodic_chat_script,
NULL, \
&sqn_gm02s_init_chat_script, \
&sqn_gm02s_dial_chat_script, \
&sqn_gm02s_periodic_chat_script)
&sqn_gm02s_periodic_chat_script, NULL)
#define DT_DRV_COMPAT quectel_bg95
DT_INST_FOREACH_STATUS_OKAY(MODEM_CELLULAR_DEVICE_QUECTEL_BG95)