drivers: modem: gsm_ppp: Enable start/stop
These changes enable applications to restart the networking stack which was previously not possible without rebooting the device. This was a major show-stopper because it made power management impossible, and furthermore made it impossible to recover from a bad modem state without rebooting. This has been verified to work on a SIMCOM7600E modem, both with and without CONFIG_GSM_MUX enabled. Signed-off-by: Benjamin Lindqvist <benjamin.lindqvist@endian.se>
This commit is contained in:
parent
966932a033
commit
54cb2bcd41
9 changed files with 205 additions and 33 deletions
|
@ -1500,6 +1500,22 @@ int gsm_mux_send(struct gsm_mux *mux, uint8_t dlci_address,
|
||||||
return gsm_mux_send_data_msg(mux, true, dlci, FT_UIH, buf, size);
|
return gsm_mux_send_data_msg(mux, true, dlci, FT_UIH, buf, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gsm_mux_detach(struct gsm_mux *mux)
|
||||||
|
{
|
||||||
|
struct gsm_dlci *dlci;
|
||||||
|
|
||||||
|
for (int i = 0; i < ARRAY_SIZE(dlcis); i++) {
|
||||||
|
dlci = &dlcis[i];
|
||||||
|
|
||||||
|
if (mux != dlci->mux || !dlci->in_use) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
dlci->in_use = false;
|
||||||
|
sys_slist_prepend(&dlci_free_entries, &dlci->node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void gsm_mux_init(void)
|
void gsm_mux_init(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
|
@ -36,3 +36,4 @@ int gsm_dlci_create(struct gsm_mux *mux,
|
||||||
struct gsm_dlci **dlci);
|
struct gsm_dlci **dlci);
|
||||||
int gsm_dlci_send(struct gsm_dlci *dlci, const uint8_t *buf, size_t size);
|
int gsm_dlci_send(struct gsm_dlci *dlci, const uint8_t *buf, size_t size);
|
||||||
int gsm_dlci_id(struct gsm_dlci *dlci);
|
int gsm_dlci_id(struct gsm_dlci *dlci);
|
||||||
|
void gsm_mux_detach(struct gsm_mux *mux);
|
||||||
|
|
|
@ -148,7 +148,7 @@ static void uart_mux_cb_work(struct k_work *work)
|
||||||
struct uart_mux_dev_data *dev_data =
|
struct uart_mux_dev_data *dev_data =
|
||||||
CONTAINER_OF(work, struct uart_mux_dev_data, cb_work);
|
CONTAINER_OF(work, struct uart_mux_dev_data, cb_work);
|
||||||
|
|
||||||
dev_data->cb(dev_data->cb_user_data);
|
dev_data->cb(dev_data->dev, dev_data->cb_user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int uart_mux_consume_ringbuf(struct uart_mux *uart_mux)
|
static int uart_mux_consume_ringbuf(struct uart_mux *uart_mux)
|
||||||
|
@ -288,6 +288,35 @@ static void uart_mux_flush_isr(const struct device *dev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void uart_mux_disable(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct uart_mux_dev_data *dev_data = DEV_DATA(dev);
|
||||||
|
const struct device *uart = dev_data->real_uart->uart;
|
||||||
|
|
||||||
|
uart_irq_rx_disable(uart);
|
||||||
|
uart_irq_tx_disable(uart);
|
||||||
|
uart_mux_flush_isr(uart);
|
||||||
|
|
||||||
|
gsm_mux_detach(dev_data->real_uart->mux);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uart_mux_enable(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct uart_mux_dev_data *dev_data = DEV_DATA(dev);
|
||||||
|
struct uart_mux *real_uart = dev_data->real_uart;
|
||||||
|
|
||||||
|
LOG_DBG("Claiming uart for uart_mux");
|
||||||
|
|
||||||
|
uart_irq_rx_disable(real_uart->uart);
|
||||||
|
uart_irq_tx_disable(real_uart->uart);
|
||||||
|
uart_mux_flush_isr(real_uart->uart);
|
||||||
|
uart_irq_callback_user_data_set(
|
||||||
|
real_uart->uart, uart_mux_isr,
|
||||||
|
real_uart);
|
||||||
|
|
||||||
|
uart_irq_rx_enable(real_uart->uart);
|
||||||
|
}
|
||||||
|
|
||||||
static void dlci_created_cb(struct gsm_dlci *dlci, bool connected,
|
static void dlci_created_cb(struct gsm_dlci *dlci, bool connected,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
|
|
|
@ -61,6 +61,8 @@ static struct gsm_modem {
|
||||||
const struct device *at_dev;
|
const struct device *at_dev;
|
||||||
const struct device *control_dev;
|
const struct device *control_dev;
|
||||||
|
|
||||||
|
struct net_if *iface;
|
||||||
|
|
||||||
bool mux_enabled : 1;
|
bool mux_enabled : 1;
|
||||||
bool mux_setup_done : 1;
|
bool mux_setup_done : 1;
|
||||||
bool setup_done : 1;
|
bool setup_done : 1;
|
||||||
|
@ -310,18 +312,39 @@ static int gsm_setup_mccmno(struct gsm_modem *gsm)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct net_if *ppp_net_if(void)
|
||||||
|
{
|
||||||
|
return net_if_get_first_by_type(&NET_L2_GET_NAME(PPP));
|
||||||
|
}
|
||||||
|
|
||||||
static void set_ppp_carrier_on(struct gsm_modem *gsm)
|
static void set_ppp_carrier_on(struct gsm_modem *gsm)
|
||||||
{
|
{
|
||||||
const struct device *ppp_dev = device_get_binding(CONFIG_NET_PPP_DRV_NAME);
|
static const struct ppp_api *api;
|
||||||
const struct ppp_api *api;
|
const struct device *ppp_dev =
|
||||||
|
device_get_binding(CONFIG_NET_PPP_DRV_NAME);
|
||||||
|
struct net_if *iface = gsm->iface;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!ppp_dev) {
|
if (!ppp_dev) {
|
||||||
LOG_ERR("Cannot find PPP %s!", "device");
|
LOG_ERR("Cannot find PPP %s!", CONFIG_NET_PPP_DRV_NAME);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
api = (const struct ppp_api *)ppp_dev->api;
|
if (!api) {
|
||||||
api->start(ppp_dev);
|
api = (const struct ppp_api *)ppp_dev->api;
|
||||||
|
|
||||||
|
/* For the first call, we want to call ppp_start()... */
|
||||||
|
ret = api->start(ppp_dev);
|
||||||
|
if (ret) {
|
||||||
|
LOG_ERR("ppp start returned %d", ret);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* ...but subsequent calls should be to ppp_enable() */
|
||||||
|
ret = net_if_l2(iface)->enable(iface, true);
|
||||||
|
if (ret) {
|
||||||
|
LOG_ERR("ppp l2 enable returned %d", ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gsm_finalize_connection(struct gsm_modem *gsm)
|
static void gsm_finalize_connection(struct gsm_modem *gsm)
|
||||||
|
@ -336,7 +359,7 @@ static void gsm_finalize_connection(struct gsm_modem *gsm)
|
||||||
"AT", &gsm->sem_response,
|
"AT", &gsm->sem_response,
|
||||||
GSM_CMD_AT_TIMEOUT);
|
GSM_CMD_AT_TIMEOUT);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
LOG_DBG("modem setup returned %d, %s",
|
LOG_ERR("modem setup returned %d, %s",
|
||||||
ret, "retrying...");
|
ret, "retrying...");
|
||||||
(void)k_delayed_work_submit(&gsm->gsm_configure_work,
|
(void)k_delayed_work_submit(&gsm->gsm_configure_work,
|
||||||
K_SECONDS(1));
|
K_SECONDS(1));
|
||||||
|
@ -393,13 +416,6 @@ static void gsm_finalize_connection(struct gsm_modem *gsm)
|
||||||
|
|
||||||
gsm->setup_done = true;
|
gsm->setup_done = true;
|
||||||
|
|
||||||
/* If we are not muxing, the modem interface and gsm_rx() thread is not
|
|
||||||
* needed as PPP will handle the incoming traffic internally.
|
|
||||||
*/
|
|
||||||
if (!IS_ENABLED(CONFIG_GSM_MUX)) {
|
|
||||||
k_thread_abort(&gsm_rx_thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
set_ppp_carrier_on(gsm);
|
set_ppp_carrier_on(gsm);
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_GSM_MUX) && gsm->mux_enabled) {
|
if (IS_ENABLED(CONFIG_GSM_MUX) && gsm->mux_enabled) {
|
||||||
|
@ -511,14 +527,24 @@ static void mux_setup(struct k_work *work)
|
||||||
const struct device *uart = device_get_binding(CONFIG_MODEM_GSM_UART_NAME);
|
const struct device *uart = device_get_binding(CONFIG_MODEM_GSM_UART_NAME);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/* We need to call this to reactivate mux ISR. Note: This is only called
|
||||||
|
* after re-initing gsm_ppp.
|
||||||
|
*/
|
||||||
|
if (IS_ENABLED(CONFIG_GSM_MUX) &&
|
||||||
|
gsm->ppp_dev && gsm->state == STATE_CONTROL_CHANNEL) {
|
||||||
|
uart_mux_enable(gsm->ppp_dev);
|
||||||
|
}
|
||||||
|
|
||||||
switch (gsm->state) {
|
switch (gsm->state) {
|
||||||
case STATE_CONTROL_CHANNEL:
|
case STATE_CONTROL_CHANNEL:
|
||||||
/* Get UART device. There is one dev / DLCI */
|
/* Get UART device. There is one dev / DLCI */
|
||||||
gsm->control_dev = uart_mux_alloc();
|
|
||||||
if (gsm->control_dev == NULL) {
|
if (gsm->control_dev == NULL) {
|
||||||
LOG_DBG("Cannot get UART mux for %s channel",
|
gsm->control_dev = uart_mux_alloc();
|
||||||
"control");
|
if (gsm->control_dev == NULL) {
|
||||||
goto fail;
|
LOG_DBG("Cannot get UART mux for %s channel",
|
||||||
|
"control");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gsm->state = STATE_PPP_CHANNEL;
|
gsm->state = STATE_PPP_CHANNEL;
|
||||||
|
@ -531,10 +557,13 @@ static void mux_setup(struct k_work *work)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_PPP_CHANNEL:
|
case STATE_PPP_CHANNEL:
|
||||||
gsm->ppp_dev = uart_mux_alloc();
|
|
||||||
if (gsm->ppp_dev == NULL) {
|
if (gsm->ppp_dev == NULL) {
|
||||||
LOG_DBG("Cannot get UART mux for %s channel", "PPP");
|
gsm->ppp_dev = uart_mux_alloc();
|
||||||
goto fail;
|
if (gsm->ppp_dev == NULL) {
|
||||||
|
LOG_DBG("Cannot get UART mux for %s channel",
|
||||||
|
"PPP");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gsm->state = STATE_AT_CHANNEL;
|
gsm->state = STATE_AT_CHANNEL;
|
||||||
|
@ -547,10 +576,13 @@ static void mux_setup(struct k_work *work)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_AT_CHANNEL:
|
case STATE_AT_CHANNEL:
|
||||||
gsm->at_dev = uart_mux_alloc();
|
|
||||||
if (gsm->at_dev == NULL) {
|
if (gsm->at_dev == NULL) {
|
||||||
LOG_DBG("Cannot get UART mux for %s channel", "AT");
|
gsm->at_dev = uart_mux_alloc();
|
||||||
goto fail;
|
if (gsm->at_dev == NULL) {
|
||||||
|
LOG_DBG("Cannot get UART mux for %s channel",
|
||||||
|
"AT");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gsm->state = STATE_DONE;
|
gsm->state = STATE_DONE;
|
||||||
|
@ -623,6 +655,9 @@ static void gsm_configure(struct k_work *work)
|
||||||
gsm->mux_enabled = true;
|
gsm->mux_enabled = true;
|
||||||
} else {
|
} else {
|
||||||
gsm->mux_enabled = false;
|
gsm->mux_enabled = false;
|
||||||
|
(void)k_delayed_work_submit(&gsm->gsm_configure_work,
|
||||||
|
K_NO_WAIT);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DBG("GSM muxing %s", gsm->mux_enabled ? "enabled" :
|
LOG_DBG("GSM muxing %s", gsm->mux_enabled ? "enabled" :
|
||||||
|
@ -643,6 +678,39 @@ static void gsm_configure(struct k_work *work)
|
||||||
gsm_finalize_connection(gsm);
|
gsm_finalize_connection(gsm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gsm_ppp_start(const struct device *device)
|
||||||
|
{
|
||||||
|
struct gsm_modem *gsm = device->data;
|
||||||
|
|
||||||
|
/* Re-init underlying UART comms */
|
||||||
|
int r = modem_iface_uart_init_dev(&gsm->context.iface,
|
||||||
|
CONFIG_MODEM_GSM_UART_NAME);
|
||||||
|
if (r) {
|
||||||
|
LOG_ERR("modem_iface_uart_init returned %d", r);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_delayed_work_init(&gsm->gsm_configure_work, gsm_configure);
|
||||||
|
(void)k_delayed_work_submit(&gsm->gsm_configure_work, K_NO_WAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gsm_ppp_stop(const struct device *device)
|
||||||
|
{
|
||||||
|
struct gsm_modem *gsm = device->data;
|
||||||
|
struct net_if *iface = gsm->iface;
|
||||||
|
|
||||||
|
net_if_l2(iface)->enable(iface, false);
|
||||||
|
|
||||||
|
if (IS_ENABLED(CONFIG_GSM_MUX)) {
|
||||||
|
/* Lower mux_enabled flag to trigger re-sending AT+CMUX etc */
|
||||||
|
gsm->mux_enabled = false;
|
||||||
|
|
||||||
|
if (gsm->ppp_dev) {
|
||||||
|
uart_mux_disable(gsm->ppp_dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int gsm_init(const struct device *device)
|
static int gsm_init(const struct device *device)
|
||||||
{
|
{
|
||||||
struct gsm_modem *gsm = device->data;
|
struct gsm_modem *gsm = device->data;
|
||||||
|
@ -706,9 +774,13 @@ static int gsm_init(const struct device *device)
|
||||||
gsm, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
|
gsm, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
|
||||||
k_thread_name_set(&gsm_rx_thread, "gsm_rx");
|
k_thread_name_set(&gsm_rx_thread, "gsm_rx");
|
||||||
|
|
||||||
k_delayed_work_init(&gsm->gsm_configure_work, gsm_configure);
|
gsm->iface = ppp_net_if();
|
||||||
|
if (!gsm->iface) {
|
||||||
|
LOG_ERR("Couldn't find ppp net_if!");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
(void)k_delayed_work_submit(&gsm->gsm_configure_work, K_NO_WAIT);
|
gsm_ppp_start(device);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -554,7 +554,8 @@ int modem_cmd_handler_setup_cmds(struct modem_iface *iface,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
LOG_ERR("command %s ret:%d", log_strdup(cmds[i].send_cmd), ret);
|
LOG_ERR("command %s ret:%d",
|
||||||
|
log_strdup(cmds[i].send_cmd), ret);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,17 +147,34 @@ int modem_iface_uart_init_dev(struct modem_iface *iface,
|
||||||
const char *dev_name)
|
const char *dev_name)
|
||||||
{
|
{
|
||||||
/* get UART device */
|
/* get UART device */
|
||||||
iface->dev = device_get_binding(dev_name);
|
const struct device *dev = device_get_binding(dev_name);
|
||||||
if (!iface->dev) {
|
const struct device *prev = iface->dev;
|
||||||
|
|
||||||
|
if (!dev) {
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
uart_irq_rx_disable(iface->dev);
|
/* Check if there's already a device inited to this iface. If so,
|
||||||
uart_irq_tx_disable(iface->dev);
|
* interrupts needs to be disabled on that too before switching to avoid
|
||||||
|
* race conditions with modem_iface_uart_isr.
|
||||||
|
*/
|
||||||
|
if (prev) {
|
||||||
|
uart_irq_tx_disable(prev);
|
||||||
|
uart_irq_rx_disable(prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
uart_irq_rx_disable(dev);
|
||||||
|
uart_irq_tx_disable(dev);
|
||||||
|
iface->dev = dev;
|
||||||
|
|
||||||
modem_iface_uart_flush(iface);
|
modem_iface_uart_flush(iface);
|
||||||
uart_irq_callback_set(iface->dev, modem_iface_uart_isr);
|
uart_irq_callback_set(iface->dev, modem_iface_uart_isr);
|
||||||
uart_irq_rx_enable(iface->dev);
|
uart_irq_rx_enable(iface->dev);
|
||||||
|
|
||||||
|
if (prev) {
|
||||||
|
uart_irq_rx_enable(prev);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -847,7 +847,7 @@ static int ppp_start(const struct device *dev)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DBG("Initializing PPP to use %s", dev_name);
|
LOG_INF("Initializing PPP to use %s", dev_name);
|
||||||
|
|
||||||
context->dev = device_get_binding(dev_name);
|
context->dev = device_get_binding(dev_name);
|
||||||
if (!context->dev) {
|
if (!context->dev) {
|
||||||
|
@ -874,7 +874,7 @@ static int ppp_stop(const struct device *dev)
|
||||||
struct ppp_driver_context *context = dev->data;
|
struct ppp_driver_context *context = dev->data;
|
||||||
|
|
||||||
net_ppp_carrier_off(context->iface);
|
net_ppp_carrier_off(context->iface);
|
||||||
|
context->modem_init_done = false;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -128,6 +128,25 @@ typedef void (*uart_mux_cb_t)(const struct device *uart,
|
||||||
*/
|
*/
|
||||||
void uart_mux_foreach(uart_mux_cb_t cb, void *user_data);
|
void uart_mux_foreach(uart_mux_cb_t cb, void *user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Disable the mux.
|
||||||
|
*
|
||||||
|
* @details Disable does not re-instate whatever ISRs and configs were present
|
||||||
|
* before the mux was enabled. This must be done by the user.
|
||||||
|
*
|
||||||
|
* @param dev UART mux device pointer
|
||||||
|
*/
|
||||||
|
void uart_mux_disable(const struct device *dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enable the mux.
|
||||||
|
*
|
||||||
|
* @details Enables the correct ISRs for the UART mux.
|
||||||
|
*
|
||||||
|
* @param dev UART mux device pointer
|
||||||
|
*/
|
||||||
|
void uart_mux_enable(const struct device *dev);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
17
include/drivers/gsm_ppp.h
Normal file
17
include/drivers/gsm_ppp.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Endian Technologies AB
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GSM_PPP_H_
|
||||||
|
#define GSM_PPP_H_
|
||||||
|
|
||||||
|
|
||||||
|
/** @cond INTERNAL_HIDDEN */
|
||||||
|
struct device;
|
||||||
|
void gsm_ppp_start(const struct device *device);
|
||||||
|
void gsm_ppp_stop(const struct device *device);
|
||||||
|
/** @endcond */
|
||||||
|
|
||||||
|
#endif /* GSM_PPP_H_ */
|
Loading…
Add table
Add a link
Reference in a new issue