28ac1f70bf
In a previous commit, a check was added to ensure modem was properly attached to packet service before initializing PPP. Failure to perform this check can lead to the dreaded 'NO CARRIER' issue. While this is a nice idea, the implementation was lacking because when the check failed, the entire modem initialization procedure was restarted from square one. For modems/bearers that were slow to attach, this is potentially disastrous. The proper solution is to loop only the 'AT+CGATT?' part until it succeeds, or fails N times. This commit implements this looping behavior (using the work queue). Signed-off-by: Benjamin Lindqvist <benjamin.lindqvist@endian.se>
829 lines
20 KiB
C
829 lines
20 KiB
C
/*
|
|
* Copyright (c) 2020 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <logging/log.h>
|
|
LOG_MODULE_REGISTER(modem_gsm, CONFIG_MODEM_LOG_LEVEL);
|
|
|
|
#include <stdlib.h>
|
|
#include <kernel.h>
|
|
#include <device.h>
|
|
#include <sys/ring_buffer.h>
|
|
#include <sys/util.h>
|
|
#include <net/ppp.h>
|
|
#include <drivers/gsm_ppp.h>
|
|
#include <drivers/uart.h>
|
|
#include <drivers/console/uart_mux.h>
|
|
|
|
#include "modem_context.h"
|
|
#include "modem_iface_uart.h"
|
|
#include "modem_cmd_handler.h"
|
|
#include "../console/gsm_mux.h"
|
|
|
|
#define GSM_CMD_READ_BUF 128
|
|
#define GSM_CMD_AT_TIMEOUT K_SECONDS(2)
|
|
#define GSM_CMD_SETUP_TIMEOUT K_SECONDS(6)
|
|
#define GSM_RX_STACK_SIZE CONFIG_MODEM_GSM_RX_STACK_SIZE
|
|
#define GSM_RECV_MAX_BUF 30
|
|
#define GSM_RECV_BUF_SIZE 128
|
|
#define GSM_ATTACH_RETRY_DELAY_MSEC 1000
|
|
|
|
|
|
/* During the modem setup, we first create DLCI control channel and then
|
|
* PPP and AT channels. Currently the modem does not create possible GNSS
|
|
* channel.
|
|
*/
|
|
enum setup_state {
|
|
STATE_INIT = 0,
|
|
STATE_CONTROL_CHANNEL = 0,
|
|
STATE_PPP_CHANNEL,
|
|
STATE_AT_CHANNEL,
|
|
STATE_DONE
|
|
};
|
|
|
|
static struct gsm_modem {
|
|
struct modem_context context;
|
|
|
|
struct modem_cmd_handler_data cmd_handler_data;
|
|
uint8_t cmd_match_buf[GSM_CMD_READ_BUF];
|
|
struct k_sem sem_response;
|
|
|
|
struct modem_iface_uart_data gsm_data;
|
|
struct k_delayed_work gsm_configure_work;
|
|
char gsm_rx_rb_buf[PPP_MRU * 3];
|
|
|
|
uint8_t *ppp_recv_buf;
|
|
size_t ppp_recv_buf_len;
|
|
|
|
enum setup_state state;
|
|
const struct device *ppp_dev;
|
|
const struct device *at_dev;
|
|
const struct device *control_dev;
|
|
|
|
struct net_if *iface;
|
|
|
|
int attach_retries;
|
|
bool mux_enabled : 1;
|
|
bool mux_setup_done : 1;
|
|
bool setup_done : 1;
|
|
} gsm;
|
|
|
|
NET_BUF_POOL_DEFINE(gsm_recv_pool, GSM_RECV_MAX_BUF, GSM_RECV_BUF_SIZE,
|
|
0, NULL);
|
|
K_KERNEL_STACK_DEFINE(gsm_rx_stack, GSM_RX_STACK_SIZE);
|
|
|
|
struct k_thread gsm_rx_thread;
|
|
|
|
static void gsm_rx(struct gsm_modem *gsm)
|
|
{
|
|
LOG_DBG("starting");
|
|
|
|
while (true) {
|
|
k_sem_take(&gsm->gsm_data.rx_sem, K_FOREVER);
|
|
|
|
/* The handler will listen AT channel */
|
|
gsm->context.cmd_handler.process(&gsm->context.cmd_handler,
|
|
&gsm->context.iface);
|
|
}
|
|
}
|
|
|
|
MODEM_CMD_DEFINE(gsm_cmd_ok)
|
|
{
|
|
modem_cmd_handler_set_error(data, 0);
|
|
LOG_DBG("ok");
|
|
k_sem_give(&gsm.sem_response);
|
|
return 0;
|
|
}
|
|
|
|
MODEM_CMD_DEFINE(gsm_cmd_error)
|
|
{
|
|
modem_cmd_handler_set_error(data, -EINVAL);
|
|
LOG_DBG("error");
|
|
k_sem_give(&gsm.sem_response);
|
|
return 0;
|
|
}
|
|
|
|
static const struct modem_cmd response_cmds[] = {
|
|
MODEM_CMD("OK", gsm_cmd_ok, 0U, ""),
|
|
MODEM_CMD("ERROR", gsm_cmd_error, 0U, ""),
|
|
MODEM_CMD("CONNECT", gsm_cmd_ok, 0U, ""),
|
|
};
|
|
|
|
#if defined(CONFIG_MODEM_SHELL)
|
|
#define MDM_MANUFACTURER_LENGTH 10
|
|
#define MDM_MODEL_LENGTH 16
|
|
#define MDM_REVISION_LENGTH 64
|
|
#define MDM_IMEI_LENGTH 16
|
|
#define MDM_IMSI_LENGTH 16
|
|
#define MDM_ICCID_LENGTH 32
|
|
|
|
struct modem_info {
|
|
char mdm_manufacturer[MDM_MANUFACTURER_LENGTH];
|
|
char mdm_model[MDM_MODEL_LENGTH];
|
|
char mdm_revision[MDM_REVISION_LENGTH];
|
|
char mdm_imei[MDM_IMEI_LENGTH];
|
|
#if defined(CONFIG_MODEM_SIM_NUMBERS)
|
|
char mdm_imsi[MDM_IMSI_LENGTH];
|
|
char mdm_iccid[MDM_ICCID_LENGTH];
|
|
#endif
|
|
};
|
|
|
|
static struct modem_info minfo;
|
|
|
|
/*
|
|
* Provide modem info if modem shell is enabled. This can be shown with
|
|
* "modem list" shell command.
|
|
*/
|
|
|
|
/* Handler: <manufacturer> */
|
|
MODEM_CMD_DEFINE(on_cmd_atcmdinfo_manufacturer)
|
|
{
|
|
size_t out_len;
|
|
|
|
out_len = net_buf_linearize(minfo.mdm_manufacturer,
|
|
sizeof(minfo.mdm_manufacturer) - 1,
|
|
data->rx_buf, 0, len);
|
|
minfo.mdm_manufacturer[out_len] = '\0';
|
|
LOG_INF("Manufacturer: %s", log_strdup(minfo.mdm_manufacturer));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Handler: <model> */
|
|
MODEM_CMD_DEFINE(on_cmd_atcmdinfo_model)
|
|
{
|
|
size_t out_len;
|
|
|
|
out_len = net_buf_linearize(minfo.mdm_model,
|
|
sizeof(minfo.mdm_model) - 1,
|
|
data->rx_buf, 0, len);
|
|
minfo.mdm_model[out_len] = '\0';
|
|
LOG_INF("Model: %s", log_strdup(minfo.mdm_model));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Handler: <rev> */
|
|
MODEM_CMD_DEFINE(on_cmd_atcmdinfo_revision)
|
|
{
|
|
size_t out_len;
|
|
|
|
out_len = net_buf_linearize(minfo.mdm_revision,
|
|
sizeof(minfo.mdm_revision) - 1,
|
|
data->rx_buf, 0, len);
|
|
minfo.mdm_revision[out_len] = '\0';
|
|
LOG_INF("Revision: %s", log_strdup(minfo.mdm_revision));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Handler: <IMEI> */
|
|
MODEM_CMD_DEFINE(on_cmd_atcmdinfo_imei)
|
|
{
|
|
size_t out_len;
|
|
|
|
out_len = net_buf_linearize(minfo.mdm_imei, sizeof(minfo.mdm_imei) - 1,
|
|
data->rx_buf, 0, len);
|
|
minfo.mdm_imei[out_len] = '\0';
|
|
LOG_INF("IMEI: %s", log_strdup(minfo.mdm_imei));
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_MODEM_SIM_NUMBERS)
|
|
/* Handler: <IMSI> */
|
|
MODEM_CMD_DEFINE(on_cmd_atcmdinfo_imsi)
|
|
{
|
|
size_t out_len;
|
|
|
|
out_len = net_buf_linearize(minfo.mdm_imsi, sizeof(minfo.mdm_imsi) - 1,
|
|
data->rx_buf, 0, len);
|
|
minfo.mdm_imsi[out_len] = '\0';
|
|
LOG_INF("IMSI: %s", log_strdup(minfo.mdm_imsi));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Handler: <ICCID> */
|
|
MODEM_CMD_DEFINE(on_cmd_atcmdinfo_iccid)
|
|
{
|
|
size_t out_len;
|
|
|
|
out_len = net_buf_linearize(minfo.mdm_iccid, sizeof(minfo.mdm_iccid) - 1,
|
|
data->rx_buf, 0, len);
|
|
minfo.mdm_iccid[out_len] = '\0';
|
|
if (minfo.mdm_iccid[0] == '+') {
|
|
/* Seen on U-blox SARA: "+CCID: nnnnnnnnnnnnnnnnnnnn".
|
|
* Skip over the +CCID bit, which other modems omit.
|
|
*/
|
|
char *p = strchr(minfo.mdm_iccid, ' ');
|
|
|
|
if (p) {
|
|
size_t len = strlen(p+1);
|
|
|
|
memmove(minfo.mdm_iccid, p+1, len+1);
|
|
}
|
|
}
|
|
LOG_INF("ICCID: %s", log_strdup(minfo.mdm_iccid));
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_MODEM_SIM_NUMBERS */
|
|
#endif /* CONFIG_MODEM_SHELL */
|
|
|
|
static const struct setup_cmd setup_cmds[] = {
|
|
/* no echo */
|
|
SETUP_CMD_NOHANDLE("ATE0"),
|
|
/* hang up */
|
|
SETUP_CMD_NOHANDLE("ATH"),
|
|
/* extender errors in numeric form */
|
|
SETUP_CMD_NOHANDLE("AT+CMEE=1"),
|
|
|
|
#if defined(CONFIG_MODEM_SHELL)
|
|
/* query modem info */
|
|
SETUP_CMD("AT+CGMI", "", on_cmd_atcmdinfo_manufacturer, 0U, ""),
|
|
SETUP_CMD("AT+CGMM", "", on_cmd_atcmdinfo_model, 0U, ""),
|
|
SETUP_CMD("AT+CGMR", "", on_cmd_atcmdinfo_revision, 0U, ""),
|
|
# if defined(CONFIG_MODEM_SIM_NUMBERS)
|
|
SETUP_CMD("AT+CIMI", "", on_cmd_atcmdinfo_imsi, 0U, ""),
|
|
SETUP_CMD("AT+CCID", "", on_cmd_atcmdinfo_iccid, 0U, ""),
|
|
# endif
|
|
SETUP_CMD("AT+CGSN", "", on_cmd_atcmdinfo_imei, 0U, ""),
|
|
#endif
|
|
|
|
/* disable unsolicited network registration codes */
|
|
SETUP_CMD_NOHANDLE("AT+CREG=0"),
|
|
|
|
/* create PDP context */
|
|
SETUP_CMD_NOHANDLE("AT+CGDCONT=1,\"IP\",\"" CONFIG_MODEM_GSM_APN "\""),
|
|
};
|
|
|
|
MODEM_CMD_DEFINE(on_cmd_atcmdinfo_attached)
|
|
{
|
|
int error = -EAGAIN;
|
|
|
|
/* Expected response is "+CGATT: 0|1" so simply look for '1' */
|
|
if (argc && atoi(argv[0]) == 1) {
|
|
error = 0;
|
|
LOG_INF("Attached to packet service!");
|
|
}
|
|
|
|
modem_cmd_handler_set_error(data, error);
|
|
k_sem_give(&gsm.sem_response);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct modem_cmd check_attached_cmd =
|
|
MODEM_CMD("+CGATT:", on_cmd_atcmdinfo_attached, 1U, ",");
|
|
|
|
static const struct setup_cmd connect_cmds[] = {
|
|
/* connect to network */
|
|
SETUP_CMD_NOHANDLE("ATD*99#"),
|
|
};
|
|
|
|
static int gsm_setup_mccmno(struct gsm_modem *gsm)
|
|
{
|
|
int ret;
|
|
|
|
if (CONFIG_MODEM_GSM_MANUAL_MCCMNO[0]) {
|
|
/* use manual MCC/MNO entry */
|
|
ret = modem_cmd_send_nolock(&gsm->context.iface,
|
|
&gsm->context.cmd_handler,
|
|
NULL, 0,
|
|
"AT+COPS=1,2,\""
|
|
CONFIG_MODEM_GSM_MANUAL_MCCMNO
|
|
"\"",
|
|
&gsm->sem_response,
|
|
GSM_CMD_AT_TIMEOUT);
|
|
} else {
|
|
/* register operator automatically */
|
|
ret = modem_cmd_send_nolock(&gsm->context.iface,
|
|
&gsm->context.cmd_handler,
|
|
NULL, 0, "AT+COPS=0,0",
|
|
&gsm->sem_response,
|
|
GSM_CMD_AT_TIMEOUT);
|
|
}
|
|
|
|
if (ret < 0) {
|
|
LOG_ERR("AT+COPS ret:%d", 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 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) {
|
|
LOG_ERR("Cannot find PPP %s!", CONFIG_NET_PPP_DRV_NAME);
|
|
return;
|
|
}
|
|
|
|
if (!api) {
|
|
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)
|
|
{
|
|
int ret;
|
|
|
|
/* If attach check failed, we should not redo every setup step */
|
|
if (gsm->attach_retries) {
|
|
goto attaching;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_GSM_MUX) && gsm->mux_enabled) {
|
|
ret = modem_cmd_send_nolock(&gsm->context.iface,
|
|
&gsm->context.cmd_handler,
|
|
&response_cmds[0],
|
|
ARRAY_SIZE(response_cmds),
|
|
"AT", &gsm->sem_response,
|
|
GSM_CMD_AT_TIMEOUT);
|
|
if (ret < 0) {
|
|
LOG_ERR("modem setup returned %d, %s",
|
|
ret, "retrying...");
|
|
(void)k_delayed_work_submit(&gsm->gsm_configure_work,
|
|
K_SECONDS(1));
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_MODEM_GSM_FACTORY_RESET_AT_BOOT)) {
|
|
(void)modem_cmd_send_nolock(&gsm->context.iface,
|
|
&gsm->context.cmd_handler,
|
|
&response_cmds[0],
|
|
ARRAY_SIZE(response_cmds),
|
|
"AT&F", &gsm->sem_response,
|
|
GSM_CMD_AT_TIMEOUT);
|
|
k_sleep(K_SECONDS(1));
|
|
}
|
|
|
|
(void)gsm_setup_mccmno(gsm);
|
|
|
|
ret = modem_cmd_handler_setup_cmds_nolock(&gsm->context.iface,
|
|
&gsm->context.cmd_handler,
|
|
setup_cmds,
|
|
ARRAY_SIZE(setup_cmds),
|
|
&gsm->sem_response,
|
|
GSM_CMD_SETUP_TIMEOUT);
|
|
if (ret < 0) {
|
|
LOG_DBG("modem setup returned %d, %s",
|
|
ret, "retrying...");
|
|
(void)k_delayed_work_submit(&gsm->gsm_configure_work,
|
|
K_SECONDS(1));
|
|
return;
|
|
}
|
|
|
|
attaching:
|
|
/* Don't initialize PPP until we're attached to packet service */
|
|
ret = modem_cmd_send_nolock(&gsm->context.iface,
|
|
&gsm->context.cmd_handler,
|
|
&check_attached_cmd, 1,
|
|
"AT+CGATT?",
|
|
&gsm->sem_response,
|
|
GSM_CMD_SETUP_TIMEOUT);
|
|
if (ret < 0) {
|
|
/*
|
|
* attach_retries not set -> trigger N attach retries
|
|
* attach_retries set -> decrement and retry
|
|
* attach_retries set, becomes 0 -> trigger full retry
|
|
*/
|
|
if (!gsm->attach_retries) {
|
|
gsm->attach_retries = CONFIG_MODEM_GSM_ATTACH_TIMEOUT *
|
|
MSEC_PER_SEC / GSM_ATTACH_RETRY_DELAY_MSEC;
|
|
} else {
|
|
gsm->attach_retries--;
|
|
}
|
|
|
|
LOG_DBG("Not attached, %s", "retrying...");
|
|
(void)k_delayed_work_submit(&gsm->gsm_configure_work,
|
|
K_MSEC(GSM_ATTACH_RETRY_DELAY_MSEC));
|
|
return;
|
|
}
|
|
|
|
/* Attached, clear retry counter */
|
|
gsm->attach_retries = 0;
|
|
|
|
LOG_DBG("modem setup returned %d, %s", ret, "enable PPP");
|
|
|
|
ret = modem_cmd_handler_setup_cmds_nolock(&gsm->context.iface,
|
|
&gsm->context.cmd_handler,
|
|
connect_cmds,
|
|
ARRAY_SIZE(connect_cmds),
|
|
&gsm->sem_response,
|
|
GSM_CMD_SETUP_TIMEOUT);
|
|
if (ret < 0) {
|
|
LOG_DBG("modem setup returned %d, %s",
|
|
ret, "retrying...");
|
|
(void)k_delayed_work_submit(&gsm->gsm_configure_work,
|
|
K_SECONDS(1));
|
|
return;
|
|
}
|
|
|
|
gsm->setup_done = true;
|
|
|
|
set_ppp_carrier_on(gsm);
|
|
|
|
if (IS_ENABLED(CONFIG_GSM_MUX) && gsm->mux_enabled) {
|
|
/* Re-use the original iface for AT channel */
|
|
ret = modem_iface_uart_init_dev(&gsm->context.iface,
|
|
gsm->at_dev->name);
|
|
if (ret < 0) {
|
|
LOG_DBG("iface %suart error %d", "AT ", ret);
|
|
} else {
|
|
/* Do a test and try to send AT command to modem */
|
|
ret = modem_cmd_send_nolock(
|
|
&gsm->context.iface,
|
|
&gsm->context.cmd_handler,
|
|
&response_cmds[0],
|
|
ARRAY_SIZE(response_cmds),
|
|
"AT", &gsm->sem_response,
|
|
GSM_CMD_AT_TIMEOUT);
|
|
if (ret < 0) {
|
|
LOG_WRN("modem setup returned %d, %s",
|
|
ret, "AT cmds failed");
|
|
} else {
|
|
LOG_INF("AT channel %d connected to %s",
|
|
DLCI_AT, gsm->at_dev->name);
|
|
}
|
|
}
|
|
modem_cmd_handler_tx_unlock(&gsm->context.cmd_handler);
|
|
}
|
|
}
|
|
|
|
static int mux_enable(struct gsm_modem *gsm)
|
|
{
|
|
int ret;
|
|
|
|
/* Turn on muxing */
|
|
if (IS_ENABLED(CONFIG_MODEM_GSM_SIMCOM)) {
|
|
ret = modem_cmd_send_nolock(
|
|
&gsm->context.iface,
|
|
&gsm->context.cmd_handler,
|
|
&response_cmds[0],
|
|
ARRAY_SIZE(response_cmds),
|
|
#if defined(SIMCOM_LTE)
|
|
/* FIXME */
|
|
/* Some SIMCOM modems can set the channels */
|
|
/* Control channel always at DLCI 0 */
|
|
"AT+CMUXSRVPORT=0,0;"
|
|
/* PPP should be at DLCI 1 */
|
|
"+CMUXSRVPORT=" STRINGIFY(DLCI_PPP) ",1;"
|
|
/* AT should be at DLCI 2 */
|
|
"+CMUXSRVPORT=" STRINGIFY(DLCI_AT) ",1;"
|
|
#else
|
|
"AT"
|
|
#endif
|
|
"+CMUX=0,0,5,"
|
|
STRINGIFY(CONFIG_GSM_MUX_MRU_DEFAULT_LEN),
|
|
&gsm->sem_response,
|
|
GSM_CMD_AT_TIMEOUT);
|
|
} else {
|
|
/* Generic GSM modem */
|
|
ret = modem_cmd_send_nolock(&gsm->context.iface,
|
|
&gsm->context.cmd_handler,
|
|
&response_cmds[0],
|
|
ARRAY_SIZE(response_cmds),
|
|
"AT+CMUX=0", &gsm->sem_response,
|
|
GSM_CMD_AT_TIMEOUT);
|
|
}
|
|
|
|
if (ret < 0) {
|
|
LOG_ERR("AT+CMUX ret:%d", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void mux_setup_next(struct gsm_modem *gsm)
|
|
{
|
|
(void)k_delayed_work_submit(&gsm->gsm_configure_work, K_MSEC(1));
|
|
}
|
|
|
|
static void mux_attach_cb(const struct device *mux, int dlci_address,
|
|
bool connected, void *user_data)
|
|
{
|
|
LOG_DBG("DLCI %d to %s %s", dlci_address, mux->name,
|
|
connected ? "connected" : "disconnected");
|
|
|
|
if (connected) {
|
|
uart_irq_rx_enable(mux);
|
|
uart_irq_tx_enable(mux);
|
|
}
|
|
|
|
mux_setup_next(user_data);
|
|
}
|
|
|
|
static int mux_attach(const struct device *mux, const struct device *uart,
|
|
int dlci_address, void *user_data)
|
|
{
|
|
int ret = uart_mux_attach(mux, uart, dlci_address, mux_attach_cb,
|
|
user_data);
|
|
if (ret < 0) {
|
|
LOG_ERR("Cannot attach DLCI %d (%s) to %s (%d)", dlci_address,
|
|
mux->name, uart->name, ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mux_setup(struct k_work *work)
|
|
{
|
|
struct gsm_modem *gsm = CONTAINER_OF(work, struct gsm_modem,
|
|
gsm_configure_work);
|
|
const struct device *uart = device_get_binding(CONFIG_MODEM_GSM_UART_NAME);
|
|
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) {
|
|
case STATE_CONTROL_CHANNEL:
|
|
/* Get UART device. There is one dev / DLCI */
|
|
if (gsm->control_dev == NULL) {
|
|
gsm->control_dev = uart_mux_alloc();
|
|
if (gsm->control_dev == NULL) {
|
|
LOG_DBG("Cannot get UART mux for %s channel",
|
|
"control");
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
gsm->state = STATE_PPP_CHANNEL;
|
|
|
|
ret = mux_attach(gsm->control_dev, uart, DLCI_CONTROL, gsm);
|
|
if (ret < 0) {
|
|
goto fail;
|
|
}
|
|
|
|
break;
|
|
|
|
case STATE_PPP_CHANNEL:
|
|
if (gsm->ppp_dev == NULL) {
|
|
gsm->ppp_dev = uart_mux_alloc();
|
|
if (gsm->ppp_dev == NULL) {
|
|
LOG_DBG("Cannot get UART mux for %s channel",
|
|
"PPP");
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
gsm->state = STATE_AT_CHANNEL;
|
|
|
|
ret = mux_attach(gsm->ppp_dev, uart, DLCI_PPP, gsm);
|
|
if (ret < 0) {
|
|
goto fail;
|
|
}
|
|
|
|
break;
|
|
|
|
case STATE_AT_CHANNEL:
|
|
if (gsm->at_dev == NULL) {
|
|
gsm->at_dev = uart_mux_alloc();
|
|
if (gsm->at_dev == NULL) {
|
|
LOG_DBG("Cannot get UART mux for %s channel",
|
|
"AT");
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
gsm->state = STATE_DONE;
|
|
|
|
ret = mux_attach(gsm->at_dev, uart, DLCI_AT, gsm);
|
|
if (ret < 0) {
|
|
goto fail;
|
|
}
|
|
|
|
break;
|
|
|
|
case STATE_DONE:
|
|
/* At least the SIMCOM modem expects that the Internet
|
|
* connection is created in PPP channel. We will need
|
|
* to attach the AT channel to context iface after the
|
|
* PPP connection is established in order to give AT commands
|
|
* to the modem.
|
|
*/
|
|
ret = modem_iface_uart_init_dev(&gsm->context.iface,
|
|
gsm->ppp_dev->name);
|
|
if (ret < 0) {
|
|
LOG_DBG("iface %suart error %d", "PPP ", ret);
|
|
gsm->mux_enabled = false;
|
|
goto fail;
|
|
}
|
|
|
|
LOG_INF("PPP channel %d connected to %s",
|
|
DLCI_PPP, gsm->ppp_dev->name);
|
|
|
|
gsm_finalize_connection(gsm);
|
|
break;
|
|
}
|
|
|
|
return;
|
|
|
|
fail:
|
|
gsm->state = STATE_INIT;
|
|
gsm->mux_enabled = false;
|
|
}
|
|
|
|
static void gsm_configure(struct k_work *work)
|
|
{
|
|
struct gsm_modem *gsm = CONTAINER_OF(work, struct gsm_modem,
|
|
gsm_configure_work);
|
|
int ret = -1;
|
|
|
|
LOG_DBG("Starting modem %p configuration", gsm);
|
|
|
|
ret = modem_cmd_send_nolock(&gsm->context.iface,
|
|
&gsm->context.cmd_handler,
|
|
&response_cmds[0],
|
|
ARRAY_SIZE(response_cmds),
|
|
"AT", &gsm->sem_response,
|
|
GSM_CMD_AT_TIMEOUT);
|
|
if (ret < 0) {
|
|
LOG_DBG("modem not ready %d", ret);
|
|
|
|
(void)k_delayed_work_submit(&gsm->gsm_configure_work,
|
|
K_NO_WAIT);
|
|
|
|
return;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_GSM_MUX) && ret == 0 &&
|
|
gsm->mux_enabled == false) {
|
|
gsm->mux_setup_done = false;
|
|
|
|
ret = mux_enable(gsm);
|
|
if (ret == 0) {
|
|
gsm->mux_enabled = true;
|
|
} else {
|
|
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" :
|
|
"disabled");
|
|
|
|
if (gsm->mux_enabled) {
|
|
gsm->state = STATE_INIT;
|
|
|
|
k_delayed_work_init(&gsm->gsm_configure_work,
|
|
mux_setup);
|
|
|
|
(void)k_delayed_work_submit(&gsm->gsm_configure_work,
|
|
K_NO_WAIT);
|
|
return;
|
|
}
|
|
}
|
|
|
|
gsm_finalize_connection(gsm);
|
|
}
|
|
|
|
void gsm_ppp_start(const struct device *dev)
|
|
{
|
|
struct gsm_modem *gsm = dev->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 *dev)
|
|
{
|
|
struct gsm_modem *gsm = dev->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);
|
|
}
|
|
}
|
|
|
|
if (modem_cmd_handler_tx_lock(&gsm->context.cmd_handler,
|
|
K_SECONDS(10))) {
|
|
LOG_WRN("Failed locking modem cmds!");
|
|
}
|
|
}
|
|
|
|
static int gsm_init(const struct device *dev)
|
|
{
|
|
struct gsm_modem *gsm = dev->data;
|
|
int r;
|
|
|
|
LOG_DBG("Generic GSM modem (%p)", gsm);
|
|
|
|
gsm->cmd_handler_data.cmds[CMD_RESP] = response_cmds;
|
|
gsm->cmd_handler_data.cmds_len[CMD_RESP] = ARRAY_SIZE(response_cmds);
|
|
gsm->cmd_handler_data.match_buf = &gsm->cmd_match_buf[0];
|
|
gsm->cmd_handler_data.match_buf_len = sizeof(gsm->cmd_match_buf);
|
|
gsm->cmd_handler_data.buf_pool = &gsm_recv_pool;
|
|
gsm->cmd_handler_data.alloc_timeout = K_NO_WAIT;
|
|
gsm->cmd_handler_data.eol = "\r";
|
|
|
|
k_sem_init(&gsm->sem_response, 0, 1);
|
|
|
|
r = modem_cmd_handler_init(&gsm->context.cmd_handler,
|
|
&gsm->cmd_handler_data);
|
|
if (r < 0) {
|
|
LOG_DBG("cmd handler error %d", r);
|
|
return r;
|
|
}
|
|
|
|
#if defined(CONFIG_MODEM_SHELL)
|
|
/* modem information storage */
|
|
gsm->context.data_manufacturer = minfo.mdm_manufacturer;
|
|
gsm->context.data_model = minfo.mdm_model;
|
|
gsm->context.data_revision = minfo.mdm_revision;
|
|
gsm->context.data_imei = minfo.mdm_imei;
|
|
#if defined(CONFIG_MODEM_SIM_NUMBERS)
|
|
gsm->context.data_imsi = minfo.mdm_imsi;
|
|
gsm->context.data_iccid = minfo.mdm_iccid;
|
|
#endif /* CONFIG_MODEM_SIM_NUMBERS */
|
|
#endif /* CONFIG_MODEM_SHELL */
|
|
|
|
gsm->gsm_data.rx_rb_buf = &gsm->gsm_rx_rb_buf[0];
|
|
gsm->gsm_data.rx_rb_buf_len = sizeof(gsm->gsm_rx_rb_buf);
|
|
|
|
r = modem_iface_uart_init(&gsm->context.iface, &gsm->gsm_data,
|
|
CONFIG_MODEM_GSM_UART_NAME);
|
|
if (r < 0) {
|
|
LOG_DBG("iface uart error %d", r);
|
|
return r;
|
|
}
|
|
|
|
r = modem_context_register(&gsm->context);
|
|
if (r < 0) {
|
|
LOG_DBG("context error %d", r);
|
|
return r;
|
|
}
|
|
|
|
LOG_DBG("iface->read %p iface->write %p",
|
|
gsm->context.iface.read, gsm->context.iface.write);
|
|
|
|
k_thread_create(&gsm_rx_thread, gsm_rx_stack,
|
|
K_KERNEL_STACK_SIZEOF(gsm_rx_stack),
|
|
(k_thread_entry_t) gsm_rx,
|
|
gsm, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
|
|
k_thread_name_set(&gsm_rx_thread, "gsm_rx");
|
|
|
|
gsm->iface = ppp_net_if();
|
|
if (!gsm->iface) {
|
|
LOG_ERR("Couldn't find ppp net_if!");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_GSM_PPP_AUTOSTART)) {
|
|
gsm_ppp_start(dev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
DEVICE_DEFINE(gsm_ppp, GSM_MODEM_DEVICE_NAME, gsm_init, NULL, &gsm, NULL,
|
|
POST_KERNEL, CONFIG_MODEM_GSM_INIT_PRIORITY, NULL);
|