Skip to content

Commit

Permalink
lib: lte_link_control: Send NEIGHOR_CELL_MEAS event in case of error
Browse files Browse the repository at this point in the history
Earlier, no LTE_LC_EVT_NEIGHBOR_CELL_MEAS event was sent in case an
error occurred during parsing of the %NCELLMEAS notification. Because
of this, the user of the library did not have any way to determine
when the operation had been completed. The implementation has been
changed so, that an LTE_LC_EVT_NEIGHBOR_CELL_MEAS event is always
sent, if lte_lc_neighbor_cell_measurement() has returned 0. In case
of an error, an empty LTE_LC_EVT_NEIGHBOR_CELL_MEAS (current_cell
set to LTE_LC_CELL_EUTRAN_ID_INVALID) is sent.

Signed-off-by: Tommi Kangas <tommi.kangas@nordicsemi.no>
  • Loading branch information
tokangas committed Jan 10, 2025
1 parent 8f2b54a commit 1a86f28
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 10 deletions.
8 changes: 6 additions & 2 deletions include/modem/lte_lc.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,10 @@ enum lte_lc_evt_type {
* Neighbor cell measurement results.
*
* The associated payload is the @c lte_lc_evt.cells_info member of type
* @ref lte_lc_cells_info in the event.
* @ref lte_lc_cells_info in the event. In case of an error or if no cells were found,
* the @c lte_lc_cells_info.current_cell member is set to
* @ref LTE_LC_CELL_EUTRAN_ID_INVALID, and @c lte_lc_cells_info.ncells_count and
* @c lte_lc_cells_info.gci_cells_count members to zero.
*/
LTE_LC_EVT_NEIGHBOR_CELL_MEAS = 7,
#endif /* CONFIG_LTE_LC_NEIGHBOR_CELL_MEAS_MODULE */
Expand Down Expand Up @@ -1682,7 +1685,8 @@ int lte_lc_lte_mode_get(enum lte_lc_lte_mode *mode);
* @ref LTE_LC_EVT_NEIGHBOR_CELL_MEAS, meaning that an event handler must be registered to receive
* the information. Depending on the network conditions, LTE connection state and requested search
* type, it may take a while before the measurement result is ready and reported back. After the
* event is received, the neighbor cell measurements are automatically stopped.
* event is received, the neighbor cell measurements are automatically stopped. If the
* function returns successfully, the @ref LTE_LC_EVT_NEIGHBOR_CELL_MEAS event is always reported.
*
* @note This feature is only supported by modem firmware versions >= 1.3.0.
*
Expand Down
24 changes: 17 additions & 7 deletions lib/lte_link_control/modules/ncellmeas.c
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,17 @@ static int parse_ncellmeas(const char *at_response, struct lte_lc_cells_info *ce
return err;
}

static void dispatch_empty_neighbor_cell_meas_event(void)
{
struct lte_lc_evt evt = {0};

LOG_DBG("Dispatching empty LTE_LC_EVT_NEIGHBOR_CELL_MEAS event");

evt.type = LTE_LC_EVT_NEIGHBOR_CELL_MEAS;
evt.cells_info.current_cell.id = LTE_LC_CELL_EUTRAN_ID_INVALID;
event_handler_list_dispatch(&evt);
}

static void at_handler_ncellmeas_gci(const char *response)
{
int err;
Expand All @@ -625,6 +636,7 @@ static void at_handler_ncellmeas_gci(const char *response)
cells = k_calloc(ncellmeas_params.gci_count, sizeof(struct lte_lc_cell));
if (cells == NULL) {
LOG_ERR("Failed to allocate memory for the GCI cells");
dispatch_empty_neighbor_cell_meas_event();
return;
}

Expand All @@ -646,6 +658,7 @@ static void at_handler_ncellmeas_gci(const char *response)
break;
default:
LOG_ERR("Parsing of neighbor cells failed, err: %d", err);
dispatch_empty_neighbor_cell_meas_event();
break;
}

Expand All @@ -655,14 +668,9 @@ static void at_handler_ncellmeas_gci(const char *response)

static void ncellmeas_cancel_timeout_work_fn(struct k_work *work)
{
struct lte_lc_evt evt = {0};

LOG_DBG("No %%NCELLMEAS notification received after AT%%NCELLMEASSTOP, "
"sending empty results");
LOG_DBG("No %%NCELLMEAS notification received after AT%%NCELLMEASSTOP");

evt.type = LTE_LC_EVT_NEIGHBOR_CELL_MEAS;
evt.cells_info.current_cell.id = LTE_LC_CELL_EUTRAN_ID_INVALID;
event_handler_list_dispatch(&evt);
dispatch_empty_neighbor_cell_meas_event();

k_sem_give(&ncellmeas_idle_sem);
}
Expand Down Expand Up @@ -709,6 +717,7 @@ static void at_handler_ncellmeas(const char *response)
neighbor_cells = k_calloc(ncell_count, sizeof(struct lte_lc_ncell));
if (neighbor_cells == NULL) {
LOG_ERR("Failed to allocate memory for neighbor cells");
dispatch_empty_neighbor_cell_meas_event();
goto exit;
}
}
Expand All @@ -730,6 +739,7 @@ static void at_handler_ncellmeas(const char *response)
break;
default:
LOG_ERR("Parsing of neighbor cells failed, err: %d", err);
dispatch_empty_neighbor_cell_meas_event();
break;
}

Expand Down
66 changes: 65 additions & 1 deletion tests/lib/lte_lc_api/src/lte_lc_api_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
#include "cmock_nrf_modem_at.h"
#include "cmock_nrf_modem.h"

#define TEST_EVENT_MAX_COUNT 10
#define TEST_EVENT_MAX_COUNT 20

static struct lte_lc_evt test_event_data[TEST_EVENT_MAX_COUNT];
static struct lte_lc_ncell test_neighbor_cells[CONFIG_LTE_NEIGHBOR_CELLS_MAX];
Expand Down Expand Up @@ -2746,6 +2746,14 @@ void test_lte_lc_neighbor_cell_measurement_cell_id_missing_fail(void)
"%NCELLMEAS:0,,\"98712\",\"0AB9\",4800,7,63,31,456,4800,"
"8,60,29,4,3500,9,99,18,5,5300,11\r\n");

lte_lc_callback_count_expected = 1;

/* In case of an error, we're expected to receive an empty event. */
test_event_data[0].type = LTE_LC_EVT_NEIGHBOR_CELL_MEAS;
test_event_data[0].cells_info.current_cell.id = LTE_LC_CELL_EUTRAN_ID_INVALID;
test_event_data[0].cells_info.ncells_count = 0;
test_event_data[0].cells_info.gci_cells_count = 0;

__mock_nrf_modem_at_printf_ExpectAndReturn("AT%NCELLMEAS", EXIT_SUCCESS);

ret = lte_lc_neighbor_cell_measurement(NULL);
Expand All @@ -2762,6 +2770,14 @@ void test_lte_lc_neighbor_cell_measurement_cell_id_too_big_fail(void)
"%NCELLMEAS:0,\"FFFFFFFF\",\"98712\",\"0AB9\",4800,7,63,31,456,4800,"
"8,60,29,4,3500,9,99,18,5,5300,11\r\n");

lte_lc_callback_count_expected = 1;

/* In case of an error, we're expected to receive an empty event. */
test_event_data[0].type = LTE_LC_EVT_NEIGHBOR_CELL_MEAS;
test_event_data[0].cells_info.current_cell.id = LTE_LC_CELL_EUTRAN_ID_INVALID;
test_event_data[0].cells_info.ncells_count = 0;
test_event_data[0].cells_info.gci_cells_count = 0;

__mock_nrf_modem_at_printf_ExpectAndReturn("AT%NCELLMEAS", EXIT_SUCCESS);

ret = lte_lc_neighbor_cell_measurement(NULL);
Expand All @@ -2778,6 +2794,14 @@ void test_lte_lc_neighbor_cell_measurement_malformed_fail(void)
"%NCELLMEAS:0,\"00112233\",\"98712\",\"0AB9\",4800,7,63,31,456,4800,"
"8,60,29,4,35 00,9,99,18,5,5300,11\r\n");

lte_lc_callback_count_expected = 1;

/* In case of an error, we're expected to receive an empty event. */
test_event_data[0].type = LTE_LC_EVT_NEIGHBOR_CELL_MEAS;
test_event_data[0].cells_info.current_cell.id = LTE_LC_CELL_EUTRAN_ID_INVALID;
test_event_data[0].cells_info.ncells_count = 0;
test_event_data[0].cells_info.gci_cells_count = 0;

__mock_nrf_modem_at_printf_ExpectAndReturn("AT%NCELLMEAS", EXIT_SUCCESS);

ret = lte_lc_neighbor_cell_measurement(NULL);
Expand Down Expand Up @@ -3506,6 +3530,16 @@ void test_lte_lc_neighbor_cell_measurement_normal_invalid_field_format_fail(void
.gci_count = 0,
};

lte_lc_callback_count_expected = 16;

/* In case of an error, we're expected to receive an empty event. */
for (int i = 0; i < lte_lc_callback_count_expected; i++) {
test_event_data[i].type = LTE_LC_EVT_NEIGHBOR_CELL_MEAS;
test_event_data[i].cells_info.current_cell.id = LTE_LC_CELL_EUTRAN_ID_INVALID;
test_event_data[i].cells_info.ncells_count = 0;
test_event_data[i].cells_info.gci_cells_count = 0;
}

/* Syntax:
* %NCELLMEAS: status
* [,<cell_id>,<plmn>,<tac>,<ta>,<earfcn>,<phys_cell_id>,<rsrp>,<rsrq>,<meas_time>
Expand Down Expand Up @@ -3671,6 +3705,16 @@ void test_lte_lc_neighbor_cell_measurement_gci_invalid_field_format_fail(void)
.gci_count = 10,
};

lte_lc_callback_count_expected = 18;

/* In case of an error, we're expected to receive an empty event. */
for (int i = 0; i < lte_lc_callback_count_expected; i++) {
test_event_data[i].type = LTE_LC_EVT_NEIGHBOR_CELL_MEAS;
test_event_data[i].cells_info.current_cell.id = LTE_LC_CELL_EUTRAN_ID_INVALID;
test_event_data[i].cells_info.ncells_count = 0;
test_event_data[i].cells_info.gci_cells_count = 0;
}

/* Syntax for GCI search types:
* High level:
* status[,
Expand Down Expand Up @@ -3881,6 +3925,16 @@ void test_lte_lc_neighbor_cell_measurement_normal_invalid_mccmnc_fail(void)
{
int ret;

lte_lc_callback_count_expected = 2;

/* In case of an error, we're expected to receive an empty event. */
for (int i = 0; i < lte_lc_callback_count_expected; i++) {
test_event_data[i].type = LTE_LC_EVT_NEIGHBOR_CELL_MEAS;
test_event_data[i].cells_info.current_cell.id = LTE_LC_CELL_EUTRAN_ID_INVALID;
test_event_data[i].cells_info.ncells_count = 0;
test_event_data[i].cells_info.gci_cells_count = 0;
}

/* Invalid MCC */
__mock_nrf_modem_at_printf_ExpectAndReturn("AT%NCELLMEAS", EXIT_SUCCESS);
ret = lte_lc_neighbor_cell_measurement(NULL);
Expand Down Expand Up @@ -3908,6 +3962,16 @@ void test_lte_lc_neighbor_cell_measurement_gci_invalid_mccmnc_fail(void)
.gci_count = 2,
};

lte_lc_callback_count_expected = 2;

/* In case of an error, we're expected to receive an empty event. */
for (int i = 0; i < lte_lc_callback_count_expected; i++) {
test_event_data[i].type = LTE_LC_EVT_NEIGHBOR_CELL_MEAS;
test_event_data[i].cells_info.current_cell.id = LTE_LC_CELL_EUTRAN_ID_INVALID;
test_event_data[i].cells_info.ncells_count = 0;
test_event_data[i].cells_info.gci_cells_count = 0;
}

/* Invalid MCC */
__mock_nrf_modem_at_printf_ExpectAndReturn("AT%NCELLMEAS=5,2", EXIT_SUCCESS);
ret = lte_lc_neighbor_cell_measurement(&params);
Expand Down

0 comments on commit 1a86f28

Please sign in to comment.