drivers: modem: RSSI measurement for PPP link

This PR addresses radio signal stength measurement during
and before PPP session. The PR provides provides facility
of readout for both +CSQ and +CESQ versions depending
upon the modems. This PR follows the idea of rssi readout
of PR#35496. Additionally, reliable Cell info update
is also ensured.

Signed-off-by: Tahir Akram <mtahirbutt@hotmail.com>
This commit is contained in:
Tahir Akram 2021-06-17 02:57:45 -07:00 committed by Christopher Friedt
commit 608ad430f3
3 changed files with 246 additions and 33 deletions

View file

@ -78,6 +78,19 @@ config MODEM_GSM_MANUAL_MCCMNO
the network provider and may need to be changed if auto is not
selected.
config MODEM_GSM_RSSI_POLLING_PERIOD
int "Configure RSSI polling period (in seconds)"
default 30
help
This settings is used to configure the period of RSSI polling
config MODEM_GSM_ENABLE_CESQ_RSSI
bool "Enable +CESQ RSSI measurement"
help
If this is enabled, RSRP, RSCP and RXREL values are read from the
modem with +CESQ. Otherwise only RSSI value is read with +CSQ
from the modem.
config MODEM_GSM_FACTORY_RESET_AT_BOOT
bool "Factory reset modem at boot"
help

View file

@ -22,6 +22,8 @@ LOG_MODULE_REGISTER(modem_gsm, CONFIG_MODEM_LOG_LEVEL);
#include "modem_cmd_handler.h"
#include "../console/gsm_mux.h"
#include <stdio.h>
#define GSM_CMD_READ_BUF 128
#define GSM_CMD_AT_TIMEOUT K_SECONDS(2)
#define GSM_CMD_SETUP_TIMEOUT K_SECONDS(6)
@ -30,6 +32,15 @@ LOG_MODULE_REGISTER(modem_gsm, CONFIG_MODEM_LOG_LEVEL);
#define GSM_RECV_BUF_SIZE 128
#define GSM_ATTACH_RETRY_DELAY_MSEC 1000
#define GSM_RSSI_RETRY_DELAY_MSEC 2000
#define GSM_RSSI_RETRIES 10
#define GSM_RSSI_INVALID -1000
#if defined(CONFIG_MODEM_GSM_ENABLE_CESQ_RSSI)
#define GSM_RSSI_MAXVAL 0
#else
#define GSM_RSSI_MAXVAL -51
#endif
/* During the modem setup, we first create DLCI control channel and then
* PPP and AT channels. Currently the modem does not create possible GNSS
@ -64,10 +75,12 @@ static struct gsm_modem {
struct net_if *iface;
int rssi_retries;
int attach_retries;
bool mux_enabled : 1;
bool mux_setup_done : 1;
bool setup_done : 1;
bool attached : 1;
} gsm;
NET_BUF_POOL_DEFINE(gsm_recv_pool, GSM_RECV_MAX_BUF, GSM_RECV_BUF_SIZE,
@ -75,6 +88,38 @@ NET_BUF_POOL_DEFINE(gsm_recv_pool, GSM_RECV_MAX_BUF, GSM_RECV_BUF_SIZE,
K_KERNEL_STACK_DEFINE(gsm_rx_stack, GSM_RX_STACK_SIZE);
struct k_thread gsm_rx_thread;
static struct k_work_delayable rssi_work_handle;
#if defined(CONFIG_MODEM_GSM_ENABLE_CESQ_RSSI)
/* helper macro to keep readability */
#define ATOI(s_, value_, desc_) modem_atoi(s_, value_, desc_, __func__)
/**
* @brief Convert string to long integer, but handle errors
*
* @param s: string with representation of integer number
* @param err_value: on error return this value instead
* @param desc: name the string being converted
* @param func: function where this is called (typically __func__)
*
* @retval return integer conversion on success, or err_value on error
*/
static int modem_atoi(const char *s, const int err_value,
const char *desc, const char *func)
{
int ret;
char *endptr;
ret = (int)strtol(s, &endptr, 10);
if (!endptr || *endptr != '\0') {
LOG_ERR("bad %s '%s' in %s", log_strdup(s),
log_strdup(desc), log_strdup(func));
return err_value;
}
return ret;
}
#endif
static void gsm_rx(struct gsm_modem *gsm)
{
@ -111,6 +156,36 @@ static const struct modem_cmd response_cmds[] = {
MODEM_CMD("CONNECT", gsm_cmd_ok, 0U, ""),
};
static int unquoted_atoi(const char *s, int base)
{
if (*s == '"') {
s++;
}
return strtol(s, NULL, base);
}
/*
* Handler: +COPS: <mode>[0],<format>[1],<oper>[2]
*/
MODEM_CMD_DEFINE(on_cmd_atcmdinfo_cops)
{
if (argc >= 3) {
#if defined(CONFIG_MODEM_CELL_INFO)
gsm.context.data_operator = unquoted_atoi(argv[2], 10);
LOG_INF("operator: %u",
gsm.context.data_operator);
#endif
if (unquoted_atoi(argv[0], 10) == 0) {
gsm.context.is_automatic_oper = true;
} else {
gsm.context.is_automatic_oper = false;
}
}
return 0;
}
#if defined(CONFIG_MODEM_SHELL)
#define MDM_MANUFACTURER_LENGTH 10
#define MDM_MODEL_LENGTH 16
@ -233,28 +308,6 @@ MODEM_CMD_DEFINE(on_cmd_atcmdinfo_iccid)
#endif /* CONFIG_MODEM_SIM_NUMBERS */
#if defined(CONFIG_MODEM_CELL_INFO)
static int unquoted_atoi(const char *s, int base)
{
if (*s == '"') {
s++;
}
return strtol(s, NULL, base);
}
/*
* Handler: +COPS: <mode>[0],<format>[1],<oper>[2]
*/
MODEM_CMD_DEFINE(on_cmd_atcmdinfo_cops)
{
if (argc >= 3) {
gsm.context.data_operator = unquoted_atoi(argv[2], 10);
LOG_INF("operator: %u",
gsm.context.data_operator);
}
return 0;
}
/*
* Handler: +CEREG: <n>[0],<stat>[1],<tac>[2],<ci>[3],<AcT>[4]
@ -298,6 +351,66 @@ static int gsm_query_cellinfo(struct gsm_modem *gsm)
#endif /* CONFIG_MODEM_CELL_INFO */
#endif /* CONFIG_MODEM_SHELL */
#if defined(CONFIG_MODEM_GSM_ENABLE_CESQ_RSSI)
/*
* Handler: +CESQ: <rxlev>[0],<ber>[1],<rscp>[2],<ecn0>[3],<rsrq>[4],<rsrp>[5]
*/
MODEM_CMD_DEFINE(on_cmd_atcmdinfo_rssi_cesq)
{
int rsrp, rscp, rxlev;
rsrp = ATOI(argv[5], 0, "rsrp");
rscp = ATOI(argv[2], 0, "rscp");
rxlev = ATOI(argv[0], 0, "rxlev");
if (rsrp >= 0 && rsrp <= 97) {
gsm.context.data_rssi = -140 + (rsrp - 1);
LOG_INF("RSRP: %d", gsm.context.data_rssi);
} else if (rscp >= 0 && rscp <= 96) {
gsm.context.data_rssi = -120 + (rscp - 1);
LOG_INF("RSCP: %d", gsm.context.data_rssi);
} else if (rxlev >= 0 && rxlev <= 63) {
gsm.context.data_rssi = -110 + (rxlev - 1);
LOG_INF("RSSI: %d", gsm.context.data_rssi);
} else {
gsm.context.data_rssi = GSM_RSSI_INVALID;
LOG_INF("RSRP/RSCP/RSSI not known");
}
return 0;
}
#else
/* Handler: +CSQ: <signal_power>[0],<qual>[1] */
MODEM_CMD_DEFINE(on_cmd_atcmdinfo_rssi_csq)
{
/* Expected response is "+CSQ: <signal_power>,<qual>" */
if (argc) {
int rssi = atoi(argv[0]);
if (rssi >= 0 && rssi <= 31) {
rssi = -113 + (rssi * 2);
} else {
rssi = GSM_RSSI_INVALID;
}
gsm.context.data_rssi = rssi;
LOG_INF("RSSI: %d", rssi);
}
k_sem_give(&gsm.sem_response);
return 0;
}
#endif
#if defined(CONFIG_MODEM_GSM_ENABLE_CESQ_RSSI)
static const struct modem_cmd read_rssi_cmd =
MODEM_CMD("+CESQ:", on_cmd_atcmdinfo_rssi_cesq, 6U, ",");
#else
static const struct modem_cmd read_rssi_cmd =
MODEM_CMD("+CSQ:", on_cmd_atcmdinfo_rssi_csq, 2U, ",");
#endif
static const struct setup_cmd setup_cmds[] = {
/* no echo */
SETUP_CMD_NOHANDLE("ATE0"),
@ -341,6 +454,10 @@ MODEM_CMD_DEFINE(on_cmd_atcmdinfo_attached)
return 0;
}
static const struct modem_cmd read_cops_cmd =
MODEM_CMD("+COPS", on_cmd_atcmdinfo_cops, 3U, ",");
static const struct modem_cmd check_attached_cmd =
MODEM_CMD("+CGATT:", on_cmd_atcmdinfo_attached, 1U, ",");
@ -351,7 +468,7 @@ static const struct setup_cmd connect_cmds[] = {
static int gsm_setup_mccmno(struct gsm_modem *gsm)
{
int ret;
int ret = 0;
if (CONFIG_MODEM_GSM_MANUAL_MCCMNO[0]) {
/* use manual MCC/MNO entry */
@ -364,12 +481,30 @@ static int gsm_setup_mccmno(struct gsm_modem *gsm)
&gsm->sem_response,
GSM_CMD_AT_TIMEOUT);
} else {
/* register operator automatically */
/* First AT+COPS? is sent to check if automatic selection for operator
* is already enabled, if yes we do not send the command AT+COPS= 0,0.
*/
ret = modem_cmd_send_nolock(&gsm->context.iface,
&gsm->context.cmd_handler,
NULL, 0, "AT+COPS=0,0",
&read_cops_cmd,
1, "AT+COPS?",
&gsm->sem_response,
GSM_CMD_AT_TIMEOUT);
GSM_CMD_SETUP_TIMEOUT);
if (ret < 0) {
return ret;
}
if (!gsm->context.is_automatic_oper) {
/* 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) {
@ -414,9 +549,38 @@ static void set_ppp_carrier_on(struct gsm_modem *gsm)
}
}
static void gsm_finalize_connection(struct gsm_modem *gsm)
static void rssi_handler(struct k_work *work)
{
int ret;
#if defined(CONFIG_MODEM_GSM_ENABLE_CESQ_RSSI)
ret = modem_cmd_send_nolock(&gsm.context.iface, &gsm.context.cmd_handler,
&read_rssi_cmd, 1, "AT+CESQ", &gsm.sem_response, GSM_CMD_SETUP_TIMEOUT);
#else
ret = modem_cmd_send_nolock(&gsm.context.iface, &gsm.context.cmd_handler,
&read_rssi_cmd, 1, "AT+CSQ", &gsm.sem_response, GSM_CMD_SETUP_TIMEOUT);
#endif
if (ret < 0) {
LOG_DBG("No answer to RSSI readout, %s", "ignoring...");
}
#if defined(CONFIG_GSM_MUX)
#if defined(CONFIG_MODEM_CELL_INFO)
(void) gsm_query_cellinfo(&gsm);
#endif
k_work_reschedule(&rssi_work_handle, K_SECONDS(CONFIG_MODEM_GSM_RSSI_POLLING_PERIOD));
#endif
}
static void gsm_finalize_connection(struct gsm_modem *gsm)
{
int ret = 0;
/* If already attached, jump right to RSSI readout */
if (gsm->attached) {
goto attached;
}
/* If attach check failed, we should not redo every setup step */
if (gsm->attach_retries) {
@ -449,7 +613,16 @@ static void gsm_finalize_connection(struct gsm_modem *gsm)
k_sleep(K_SECONDS(1));
}
(void)gsm_setup_mccmno(gsm);
ret = gsm_setup_mccmno(gsm);
if (ret < 0) {
LOG_ERR("modem setup returned %d, %s",
ret, "retrying...");
(void)k_work_reschedule(&gsm->gsm_configure_work,
K_SECONDS(1));
return;
}
ret = modem_cmd_handler_setup_cmds_nolock(&gsm->context.iface,
&gsm->context.cmd_handler,
@ -492,13 +665,34 @@ attaching:
return;
}
#if defined(CONFIG_MODEM_CELL_INFO)
(void)gsm_query_cellinfo(gsm);
#endif
/* Attached, clear retry counter */
gsm->attached = true;
gsm->attach_retries = 0;
LOG_DBG("modem attach returned %d, %s", ret, "read RSSI");
gsm->rssi_retries = GSM_RSSI_RETRIES;
attached:
if (!IS_ENABLED(CONFIG_GSM_MUX)) {
/* Read connection quality (RSSI) before PPP carrier is ON */
rssi_handler(NULL);
if (!(gsm->context.data_rssi && gsm->context.data_rssi != GSM_RSSI_INVALID &&
gsm->context.data_rssi < GSM_RSSI_MAXVAL)) {
LOG_DBG("Not valid RSSI, %s", "retrying...");
if (gsm->rssi_retries-- > 0) {
(void)k_work_reschedule(&gsm->gsm_configure_work,
K_MSEC(GSM_RSSI_RETRY_DELAY_MSEC));
return;
}
}
#if defined(CONFIG_MODEM_CELL_INFO)
(void) gsm_query_cellinfo(gsm);
#endif
}
LOG_DBG("modem setup returned %d, %s", ret, "enable PPP");
ret = modem_cmd_handler_setup_cmds_nolock(&gsm->context.iface,
@ -542,6 +736,7 @@ attaching:
}
}
modem_cmd_handler_tx_unlock(&gsm->context.cmd_handler);
k_work_schedule(&rssi_work_handle, K_SECONDS(CONFIG_MODEM_GSM_RSSI_POLLING_PERIOD));
}
}
@ -793,6 +988,10 @@ void gsm_ppp_start(const struct device *dev)
k_work_init_delayable(&gsm->gsm_configure_work, gsm_configure);
(void)k_work_reschedule(&gsm->gsm_configure_work, K_NO_WAIT);
#if defined(CONFIG_GSM_MUX)
k_work_init_delayable(&rssi_work_handle, rssi_handler);
#endif
}
void gsm_ppp_stop(const struct device *dev)
@ -853,6 +1052,7 @@ static int gsm_init(const struct device *dev)
#endif /* CONFIG_MODEM_SIM_NUMBERS */
#endif /* CONFIG_MODEM_SHELL */
gsm->context.is_automatic_oper = false;
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);

View file

@ -72,7 +72,7 @@ struct modem_context {
int data_cellid;
#endif
int data_rssi;
bool is_automatic_oper;
/* pin config */
struct modem_pin *pins;
size_t pins_len;