diff --git a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst index 1e2be3d78430..62a8df633a84 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst @@ -495,6 +495,7 @@ Modem libraries * :ref:`lte_lc_readme` library: * Fixed handling of ``%NCELLMEAS`` notification with status 2 (measurement interrupted) and no cells. + * Added sending of ``LTE_LC_EVT_NEIGHBOR_CELL_MEAS`` event with ``current_cell`` set to ``LTE_LC_CELL_EUTRAN_ID_INVALID`` in case an error occurs while parsing the ``%NCELLMEAS`` notification. * :ref:`modem_key_mgmt` library: diff --git a/include/modem/lte_lc.h b/include/modem/lte_lc.h index b6526690cea8..a958742b0ed8 100644 --- a/include/modem/lte_lc.h +++ b/include/modem/lte_lc.h @@ -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 are set to zero. */ LTE_LC_EVT_NEIGHBOR_CELL_MEAS = 7, #endif /* CONFIG_LTE_LC_NEIGHBOR_CELL_MEAS_MODULE */ @@ -1688,7 +1691,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. * diff --git a/lib/lte_link_control/modules/ncellmeas.c b/lib/lte_link_control/modules/ncellmeas.c index c38e1c9c2653..2c54e7093199 100644 --- a/lib/lte_link_control/modules/ncellmeas.c +++ b/lib/lte_link_control/modules/ncellmeas.c @@ -610,6 +610,17 @@ static int parse_ncellmeas(const char *at_response, struct lte_lc_cells_info *ce return err; } +static void ncellmeas_empty_event_dispatch(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; @@ -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"); + ncellmeas_empty_event_dispatch(); return; } @@ -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); + ncellmeas_empty_event_dispatch(); break; } @@ -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); + ncellmeas_empty_event_dispatch(); k_sem_give(&ncellmeas_idle_sem); } @@ -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"); + ncellmeas_empty_event_dispatch(); goto exit; } } @@ -730,6 +739,7 @@ static void at_handler_ncellmeas(const char *response) break; default: LOG_ERR("Parsing of neighbor cells failed, err: %d", err); + ncellmeas_empty_event_dispatch(); break; } diff --git a/tests/lib/lte_lc_api/src/lte_lc_api_test.c b/tests/lib/lte_lc_api/src/lte_lc_api_test.c index 9cc13996fe46..6e86a7fed873 100644 --- a/tests/lib/lte_lc_api/src/lte_lc_api_test.c +++ b/tests/lib/lte_lc_api/src/lte_lc_api_test.c @@ -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]; @@ -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); @@ -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); @@ -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); @@ -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 * [,,,,,,,,, @@ -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[, @@ -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); @@ -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(¶ms);