modem_cellular: Add dormant state to support network loss

Previously, it was expected that the modem would immediately
disconnect and close the PPP link, when the network was lost
(+CEREG: 4 is received). Failure to do so, would result in
modem attempting to chat with dlci2 channel, where as cellular
modem driver would drop to dlci1. This would show as dial
script timing out, which would freeze cellular modem driver.

Adding dormant state enables graceful disconnection of PPP-link
when the network is lost. This allows the modems which do
not automatically close with network link loss to close
based on PPP link closure.

Signed-off-by: Markus Lassila <markus.lassila@nordicsemi.no>
This commit is contained in:
Markus Lassila 2025-05-12 14:11:12 +03:00 committed by Benjamin Cabé
parent 93c3975fe6
commit 5b6a865464
2 changed files with 75 additions and 2 deletions

View File

@ -13,6 +13,7 @@ config MODEM_CELLULAR
select RING_BUFFER
select NET_L2_PPP_OPTION_MRU
select NET_L2_PPP_PAP
select NET_L2_PPP_MGMT
depends on (DT_HAS_QUECTEL_BG95_ENABLED || \
DT_HAS_SIMCOM_SIM7080_ENABLED || DT_HAS_U_BLOX_SARA_R4_ENABLED || \
DT_HAS_U_BLOX_SARA_R5_ENABLED || DT_HAS_SWIR_HL7800_ENABLED || \

View File

@ -60,6 +60,7 @@ enum modem_cellular_state {
MODEM_CELLULAR_STATE_RUN_DIAL_SCRIPT,
MODEM_CELLULAR_STATE_AWAIT_REGISTERED,
MODEM_CELLULAR_STATE_CARRIER_ON,
MODEM_CELLULAR_STATE_DORMANT,
MODEM_CELLULAR_STATE_INIT_POWER_OFF,
MODEM_CELLULAR_STATE_RUN_SHUTDOWN_SCRIPT,
MODEM_CELLULAR_STATE_POWER_OFF_PULSE,
@ -79,6 +80,7 @@ enum modem_cellular_event {
MODEM_CELLULAR_EVENT_DEREGISTERED,
MODEM_CELLULAR_EVENT_BUS_OPENED,
MODEM_CELLULAR_EVENT_BUS_CLOSED,
MODEM_CELLULAR_EVENT_PPP_DEAD,
};
struct modem_cellular_data {
@ -126,6 +128,7 @@ struct modem_cellular_data {
/* PPP */
struct modem_ppp *ppp;
struct net_mgmt_event_callback net_mgmt_event_callback;
enum modem_cellular_state state;
const struct device *dev;
@ -196,6 +199,8 @@ static const char *modem_cellular_state_str(enum modem_cellular_state state)
return "run dial script";
case MODEM_CELLULAR_STATE_CARRIER_ON:
return "carrier on";
case MODEM_CELLULAR_STATE_DORMANT:
return "dormant";
case MODEM_CELLULAR_STATE_INIT_POWER_OFF:
return "init power off";
case MODEM_CELLULAR_STATE_RUN_SHUTDOWN_SCRIPT:
@ -236,6 +241,8 @@ static const char *modem_cellular_event_str(enum modem_cellular_event event)
return "bus opened";
case MODEM_CELLULAR_EVENT_BUS_CLOSED:
return "bus closed";
case MODEM_CELLULAR_EVENT_PPP_DEAD:
return "ppp dead";
}
return "";
@ -1021,7 +1028,9 @@ static void modem_cellular_run_dial_script_event_handler(struct modem_cellular_d
modem_chat_attach(&data->chat, data->dlci1_pipe);
modem_chat_run_script_async(&data->chat, config->dial_chat_script);
break;
case MODEM_CELLULAR_EVENT_SCRIPT_FAILED:
modem_cellular_start_timer(data, MODEM_CELLULAR_PERIODIC_SCRIPT_TIMEOUT);
break;
case MODEM_CELLULAR_EVENT_SCRIPT_SUCCESS:
modem_cellular_enter_state(data, MODEM_CELLULAR_STATE_AWAIT_REGISTERED);
break;
@ -1110,7 +1119,7 @@ static void modem_cellular_carrier_on_event_handler(struct modem_cellular_data *
break;
case MODEM_CELLULAR_EVENT_DEREGISTERED:
modem_cellular_enter_state(data, MODEM_CELLULAR_STATE_RUN_DIAL_SCRIPT);
modem_cellular_enter_state(data, MODEM_CELLULAR_STATE_DORMANT);
break;
case MODEM_CELLULAR_EVENT_SUSPEND:
@ -1125,9 +1134,37 @@ static void modem_cellular_carrier_on_event_handler(struct modem_cellular_data *
static int modem_cellular_on_carrier_on_state_leave(struct modem_cellular_data *data)
{
modem_cellular_stop_timer(data);
return 0;
}
static int modem_cellular_on_dormant_state_enter(struct modem_cellular_data *data)
{
net_if_dormant_on(modem_ppp_get_iface(data->ppp));
return 0;
}
static void modem_cellular_dormant_event_handler(struct modem_cellular_data *data,
enum modem_cellular_event evt)
{
switch (evt) {
case MODEM_CELLULAR_EVENT_PPP_DEAD:
modem_cellular_enter_state(data, MODEM_CELLULAR_STATE_RUN_DIAL_SCRIPT);
break;
default:
break;
}
}
static int modem_cellular_on_dormant_state_leave(struct modem_cellular_data *data)
{
net_if_carrier_off(modem_ppp_get_iface(data->ppp));
modem_chat_release(&data->chat);
modem_ppp_release(data->ppp);
net_if_dormant_off(modem_ppp_get_iface(data->ppp));
return 0;
}
@ -1316,6 +1353,10 @@ static int modem_cellular_on_state_enter(struct modem_cellular_data *data)
ret = modem_cellular_on_carrier_on_state_enter(data);
break;
case MODEM_CELLULAR_STATE_DORMANT:
ret = modem_cellular_on_dormant_state_enter(data);
break;
case MODEM_CELLULAR_STATE_INIT_POWER_OFF:
ret = modem_cellular_on_init_power_off_state_enter(data);
break;
@ -1377,6 +1418,10 @@ static int modem_cellular_on_state_leave(struct modem_cellular_data *data)
ret = modem_cellular_on_carrier_on_state_leave(data);
break;
case MODEM_CELLULAR_STATE_DORMANT:
ret = modem_cellular_on_dormant_state_leave(data);
break;
case MODEM_CELLULAR_STATE_INIT_POWER_OFF:
ret = modem_cellular_on_init_power_off_state_leave(data);
break;
@ -1476,6 +1521,10 @@ static void modem_cellular_event_handler(struct modem_cellular_data *data,
modem_cellular_carrier_on_event_handler(data, evt);
break;
case MODEM_CELLULAR_STATE_DORMANT:
modem_cellular_dormant_event_handler(data, evt);
break;
case MODEM_CELLULAR_STATE_INIT_POWER_OFF:
modem_cellular_init_power_off_event_handler(data, evt);
break;
@ -1719,6 +1768,22 @@ static int modem_cellular_pm_action(const struct device *dev, enum pm_device_act
}
#endif /* CONFIG_PM_DEVICE */
static void net_mgmt_event_handler(struct net_mgmt_event_callback *cb, uint32_t mgmt_event,
struct net_if *iface)
{
struct modem_cellular_data *data =
CONTAINER_OF(cb, struct modem_cellular_data, net_mgmt_event_callback);
switch (mgmt_event) {
case NET_EVENT_PPP_PHASE_DEAD:
modem_cellular_delegate_event(data, MODEM_CELLULAR_EVENT_PPP_DEAD);
break;
default:
break;
}
}
static int modem_cellular_init(const struct device *dev)
{
struct modem_cellular_data *data = (struct modem_cellular_data *)dev->data;
@ -1827,6 +1892,13 @@ static int modem_cellular_init(const struct device *dev)
modem_chat_init(&data->chat, &chat_config);
}
{
net_mgmt_init_event_callback(&data->net_mgmt_event_callback, net_mgmt_event_handler,
NET_EVENT_PPP_PHASE_DEAD);
net_mgmt_add_event_callback(&data->net_mgmt_event_callback);
}
#ifndef CONFIG_PM_DEVICE
modem_cellular_delegate_event(data, MODEM_CELLULAR_EVENT_RESUME);
#else