diff --git a/CHANGELOG.md b/CHANGELOG.md index 66530dc..c492656 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## 3.4.1 (June 23rd, 2023) + +### Features +- (commercial feature only) Added support for bootstrapping from SIM card on + nRF9160-based devices +- Added support for nRF700x Wi-FI IC +- Added Light Control object for LED handling +- Added persistence of attribute storage +- Added support for FOTA of application and modem firmware for nRF9160 using + experimental Advanced Firmware Update object (/33629) +- (commercial feature only) Added support for Core Peristence + +### Improvements +- Updated Anjay to version 3.4.1 +- Fixed avs_commons and anjay_zephyr configurations dependencies +- Updated Nordic Location Services-related APIs and object implementations to match new object definitions and server-side behavior +- Kconfig options which associated values can be changed during runtime have been marked as defaults +- Fixed compatibility with NCS v2.3.0 + ## 3.3.0 (Feb 21st, 2023) ### Improvements diff --git a/CMakeLists.txt b/CMakeLists.txt index 54f77f5..84238f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ if(CONFIG_ANJAY) src/objects/buzzer.c src/objects/device.c src/objects/led_color_light.c + src/objects/light_control.c src/objects/location.c src/objects/objects.h src/objects/push_button.c @@ -71,15 +72,24 @@ if(CONFIG_ANJAY) list(APPEND CLIENT_SOURCES src/nrf_lc_info.c src/nrf_lc_info.h - src/nrf_lc_jobs.c - src/nrf_lc_jobs.h src/objects/conn_mon.c src/objects/ecid.c) endif() + if(CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION) + list(APPEND CLIENT_SOURCES + src/objects/ground_fix_location.c) + endif() + + if(CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_ASSISTANCE) + list(APPEND CLIENT_SOURCES + src/objects/gnss_assistance.c) + endif() + if(CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES) list(APPEND CLIENT_SOURCES - src/objects/loc_assist.c) + src/location_services.c + src/location_services.h) endif() if(CONFIG_ANJAY_ZEPHYR_FOTA) @@ -87,6 +97,14 @@ if(CONFIG_ANJAY) src/firmware_update.c) endif() + if(CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160) + list(APPEND CLIENT_SOURCES + src/afu/nrf9160/afu_nrf9160_application.c + src/afu/nrf9160/afu_nrf9160_modem.c + src/afu/nrf9160/afu_nrf9160.c + src/afu/nrf9160/afu_nrf9160.h) + endif() + if(CONFIG_LTE_LINK_CONTROL) list(APPEND CLIENT_SOURCES src/network/network_nrf91.c) @@ -99,6 +117,9 @@ if(CONFIG_ANJAY) elseif(CONFIG_WIFI_ESWIFI) list(APPEND CLIENT_SOURCES src/network/network_eswifi.c) + elseif(CONFIG_WIFI_NRF700X) + list(APPEND CLIENT_SOURCES + src/network/network_nrf700x.c) elseif(CONFIG_WIFI) list(APPEND CLIENT_SOURCES src/network/network_wifi.c) @@ -145,4 +166,4 @@ if(CONFIG_ANJAY) target_compile_definitions(${ZEPHYR_CURRENT_LIBRARY} PRIVATE NDEBUG) endif() - endif() +endif() diff --git a/Kconfig.anjay b/Kconfig.anjay index c8d662a..8bccb53 100644 --- a/Kconfig.anjay +++ b/Kconfig.anjay @@ -89,7 +89,7 @@ menuconfig ANJAY_WITH_LOGS config ANJAY_WITH_TRACE_LOGS bool "Enable TRACE-level logs in Anjay, avs_commons and avs_coap." - default y + default n depends on ANJAY_WITH_LOGS config ANJAY_WITH_MICRO_LOGS @@ -147,6 +147,11 @@ config ANJAY_WITH_OBSERVE help Information Reporting interface includes Observe and Notify operations +config ANJAY_WITH_OBSERVE_PERSISTENCE + bool "Enable support for observation persistence" + default n + depends on ANJAY_WITH_OBSERVE + config ANJAY_WITH_NET_STATS bool "Enable support for measuring amount of LwM2M traffic." default y @@ -303,6 +308,11 @@ config ANJAY_WITH_MODULE_FW_UPDATE default y depends on ANJAY_WITH_DOWNLOADER +config ANJAY_WITH_MODULE_ADVANCED_FW_UPDATE + bool "Enable advanced_fw_update module" + default n + depends on ANJAY_WITH_DOWNLOADER + config ANJAY_WITH_MODULE_IPSO_OBJECTS bool "Enable IPSO objects implementation" @@ -313,11 +323,6 @@ config ANJAY_WITH_MODULE_FACTORY_PROVISIONING default n depends on ANJAY_WITH_CBOR -config ANJAY_WITH_NORDIC_LOCATION_SERVICES - bool "Enable Nordic location services" - depends on BOARD_THINGY91_NRF9160_NS || BOARD_NRF9160DK_NRF9160_NS - default n - endif endmenu diff --git a/Kconfig.anjay_zephyr b/Kconfig.anjay_zephyr index 305a4c9..a502f0c 100644 --- a/Kconfig.anjay_zephyr +++ b/Kconfig.anjay_zephyr @@ -24,54 +24,58 @@ config ANJAY_ZEPHYR_MODEL_NUMBER config ANJAY_ZEPHYR_VERSION string "Client Version" - default "3.3.0" + default "3.4.1" + +config ANJAY_ZEPHYR_AUTOGENERATE_ENDPOINT_NAME + bool "Autogenerate endpoint name" + default y + menu "Client default options" visible if !ANJAY_ZEPHYR_FACTORY_PROVISIONING - config ANJAY_ZEPHYR_AUTOGENERATE_ENDPOINT_NAME - bool "Autogenerate endpoint name" - default y - config ANJAY_ZEPHYR_BOOTSTRAP_SERVER - bool "Bootstrap server" + bool "Default bootstrap server" default n + help + This is a default value, it will be used if there are no saved settings from a previously + flashed Anjay-zephyr, or by executing the "anjay config default" command in the shell. config ANJAY_ZEPHYR_USE_PERSISTENCE - bool "Use persistence" + bool "Default use persistence" default n depends on ANJAY_ZEPHYR_PERSISTENCE + help + This is a default value, it will be used if there are no saved settings from a previously + flashed Anjay-zephyr, or by executing the "anjay config default" command in the shell. config ANJAY_ZEPHYR_ENDPOINT_NAME - string "Endpoint Name" + string "Default endpoint name" default "Anjay-zephyr-client" depends on !ANJAY_ZEPHYR_AUTOGENERATE_ENDPOINT_NAME + help + This is a default value, it will be used if there are no saved settings from a previously + flashed Anjay-zephyr, or by executing the "anjay config default" command in the shell. config ANJAY_ZEPHYR_LIFETIME - int "Client lifetime" + int "Default client lifetime" default 50 + help + This is a default value, it will be used if there are no saved settings from a previously + flashed Anjay-zephyr, or by executing the "anjay config default" command in the shell. config ANJAY_ZEPHYR_SERVER_URI - string "Server URI" + string "Default server URI" default "coaps://eu.iot.avsystem.cloud:5684" - - menuconfig ANJAY_ZEPHYR_RUNTIME_CERT_CONFIG - bool "Enable runtime certificate configuration through shell" - depends on MBEDTLS_TLS_LIBRARY || MBEDTLS - default n - - config ANJAY_ZEPHYR_MAX_PUBLIC_CERT_LEN - int "Max public certificate length" - default 2048 - depends on ANJAY_ZEPHYR_RUNTIME_CERT_CONFIG - - config ANJAY_ZEPHYR_MAX_PRIVATE_KEY_LEN - int "Max private key length" - default 1024 - depends on ANJAY_ZEPHYR_RUNTIME_CERT_CONFIG + help + This is a default value, it will be used if there are no saved settings from a previously + flashed Anjay-zephyr, or by executing the "anjay config default" command in the shell. choice ANJAY_ZEPHYR_SECURITY_MODE - prompt "Choose security mode" + prompt "Choose default security mode" default ANJAY_ZEPHYR_SECURITY_MODE_PSK + help + This is a default value, it will be used if there are no saved settings from a previously + flashed Anjay-zephyr, or by executing the "anjay config default" command in the shell. config ANJAY_ZEPHYR_SECURITY_MODE_NOSEC bool "Non-secure connection" @@ -85,26 +89,75 @@ menu "Client default options" endchoice config ANJAY_ZEPHYR_PSK_IDENTITY - string "PSK Identity" + string "Default PSK identity" default "Anjay-zephyr-client" + help + This is a default value, it will be used if there are no saved settings from a previously + flashed Anjay-zephyr, or by executing the "anjay config default" command in the shell. config ANJAY_ZEPHYR_PSK_KEY - string "PSK Key" + string "Default PSK key" default "psk" + help + This is a default value, it will be used if there are no saved settings from a previously + flashed Anjay-zephyr, or by executing the "anjay config default" command in the shell. + + config ANJAY_ZEPHYR_GPS_NRF_PRIO_MODE_PERMITTED + bool "Default allow temporary activation of the GPS priority mode" + default y + depends on ANJAY_ZEPHYR_GPS_NRF + help + This is a default value, it will be used if there are no saved settings from a previously + flashed Anjay-zephyr, or by executing the "anjay config default" command in the shell. + + If set, Anjay Zephyr will temporarily activate the GPS priority over LTE + idle mode in case GPS fix cannot be produced. The mode will be + deactivated automatically after getting a GPS fix or after 40 seconds. + + config ANJAY_ZEPHYR_GPS_NRF_PRIO_MODE_COOLDOWN + int "Default GPS priority mode cooldown time" + default 900 + depends on ANJAY_ZEPHYR_GPS_NRF + help + This is a default value, it will be used if there are no saved settings from a previously + flashed Anjay-zephyr, or by executing the "anjay config default" command in the shell. + + Determines (in seconds) how much time must pass after a failed try to produce a GPS fix to + enable GPS priority mode again. endmenu menu "WiFi default configuration" visible if WIFI config ANJAY_ZEPHYR_WIFI_SSID - string "WiFi SSID" + string "Default WiFi SSID" default "ssid" + help + This is a default value, it will be used if there are no saved settings from a previously + flashed Anjay-zephyr, or by executing the "anjay config default" command in the shell. config ANJAY_ZEPHYR_WIFI_PASSWORD - string "WiFi Password" + string "Default WiFi Password" default "mypassword" + help + This is a default value, it will be used if there are no saved settings from a previously + flashed Anjay-zephyr, or by executing the "anjay config default" command in the shell. endmenu +menuconfig ANJAY_ZEPHYR_RUNTIME_CERT_CONFIG + bool "Enable runtime certificate configuration through shell" + depends on MBEDTLS_TLS_LIBRARY || MBEDTLS + default n + + config ANJAY_ZEPHYR_MAX_PUBLIC_CERT_LEN + int "Max public certificate length" + default 2048 + depends on ANJAY_ZEPHYR_RUNTIME_CERT_CONFIG + + config ANJAY_ZEPHYR_MAX_PRIVATE_KEY_LEN + int "Max private key length" + default 1024 + depends on ANJAY_ZEPHYR_RUNTIME_CERT_CONFIG config ANJAY_ZEPHYR_THREAD_PRIORITY int "Priority of the Anjay thread" @@ -142,7 +195,7 @@ config ANJAY_ZEPHYR_GPS_SPEED def_bool ANJAY_ZEPHYR_GPS_NRF config ANJAY_ZEPHYR_LOCATION_SERVICES - def_bool ANJAY_ZEPHYR_LOCATION_SERVICES_ASSISTANCE || ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED + def_bool ANJAY_ZEPHYR_LOCATION_SERVICES_ASSISTANCE || ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION select ANJAY_WITH_LWM2M11 select ANJAY_WITH_SEND select ANJAY_WITH_SENML_JSON @@ -150,6 +203,13 @@ config ANJAY_ZEPHYR_LOCATION_SERVICES config ANJAY_ZEPHYR_LOCATION_SERVICES_ASSISTANCE def_bool ANJAY_ZEPHYR_GPS_NRF_A_GPS || ANJAY_ZEPHYR_GPS_NRF_P_GPS +config ANJAY_ZEPHYR_LOCATION_SERVICES_SERVER_SSID + int "Location services server SSID" + default 1 + depends on ANJAY_ZEPHYR_LOCATION_SERVICES + help + SSID of the server to which requests related to location services will be sent. + menuconfig ANJAY_ZEPHYR_GPS_NRF bool "Enable GPS on nRF9160-based devices" default n @@ -162,9 +222,8 @@ menuconfig ANJAY_ZEPHYR_GPS_NRF the chip is interrupted by any LTE activity. The application might temporarily enable GPS priority mode, which shuts down LTE completely and attempts to acquire the fix. This behavior is controlled by - gps_prio_mode_timeout and gps_prio_mode_cooldown variables which are - configurable in the runtime. Setting gps_prio_mode_timeout to 0 will - disable the GPS priority mode completely. + ANJAY_ZEPHYR_GPS_NRF_PRIO_MODE_PERMITTED and + ANJAY_ZEPHYR_GPS_NRF_PRIO_MODE_COOLDOWN config options. config ANJAY_ZEPHYR_GPS_NRF_EXTERNAL_ANTENNA bool "Use external GPS antenna" @@ -180,18 +239,29 @@ menuconfig ANJAY_ZEPHYR_GPS_NRF This is an experimental feature under active development, which is currently available only on selected instances of AVSystem's Coiote IoT DM servers. -config ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED - bool "Enable manual requests for cell-based location" + config ANJAY_ZEPHYR_GPS_NRF_A_GPS_SATELLITE_ELEVATION_MASK + int "A-GPS minimum elevation angle for visible satellites" + depends on ANJAY_ZEPHYR_GPS_NRF_A_GPS + range -1 90 + default -1 + help + Minimum elevation angle for visible satellites. Only used for A-GPS. + -1 indicates that angle is disabled and filtering shouldn't be used. + +config ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION + bool "Enable ground fix location requests" depends on ANJAY_ZEPHYR_NRF_LC_INFO help - Enable "anjay nls_cell_request" command for retrieving cell-based location using + Enable "anjay nls_gf_location_request" command for retrieving cell-based location using Nordic Location Services over LwM2M. This is an experimental feature under active development, which is currently available only on selected instances of AVSystem's Coiote IoT DM servers. config ANJAY_ZEPHYR_NRF_LC_INFO - def_bool LTE_LINK_CONTROL && (BOARD_THINGY91_NRF9160_NS || BOARD_NRF9160DK_NRF9160_NS) + bool "Enable nRF9160 Link Control library state and Connectivity Monitor" + depends on LTE_LINK_CONTROL && (BOARD_THINGY91_NRF9160_NS || BOARD_NRF9160DK_NRF9160_NS) + default y help Enables handlers tracking nRF9160 Link Control library state and Connectivity Monitor, ECID objects utilizing data from LC library. @@ -208,10 +278,55 @@ config ANJAY_ZEPHYR_NETWORK_KEEPALIVE_RATE range 1 2147483647 depends on WIFI_ESWIFI -config ANJAY_ZEPHYR_FOTA - bool "Enable the Firmware Update object" - depends on BOOTLOADER_MCUBOOT - select IMG_MANAGER +choice ANJAY_ZEPHYR_OTA + prompt "Enable Firmware Update Over-the-Air" + default ANJAY_ZEPHYR_FOTA + optional + + config ANJAY_ZEPHYR_FOTA + bool "MCUboot application FOTA" + depends on BOOTLOADER_MCUBOOT + select IMG_MANAGER + select ANJAY_WITH_MODULE_FW_UPDATE + help + Enables MCUboot application FOTA using Firmware Update object (/5) + + config ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_MODEM + bool "Multi-target (MCUboot app, delta modem) FOTA for nRF9160" + depends on BOARD_THINGY91_NRF9160_NS || BOARD_NRF9160DK_NRF9160_NS + depends on !NRF_MODEM_LIB_SYS_INIT + select DFU_TARGET + select STREAM_FLASH_ERASE + select ANJAY_WITH_MODULE_ADVANCED_FW_UPDATE + + help + Enables multi-target (MCUboot application, delta nRF9160 modem) FOTA + using experimental Advanced Firmware Update object (/33629) + + config ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_FULL_MODEM + bool "Multi-target (MCUboot app, delta/full modem) FOTA for nRF9160" + depends on BOARD_THINGY91_NRF9160_NS || BOARD_NRF9160DK_NRF9160_NS + depends on !NRF_MODEM_LIB_SYS_INIT + select STREAM_FLASH_ERASE + select ZCBOR + select DFU_TARGET + select DFU_TARGET_STREAM + select FMFU_FDEV + select DFU_TARGET_FULL_MODEM + select IMG_MANAGER + select ANJAY_WITH_MODULE_ADVANCED_FW_UPDATE + + help + Enables multi-target (MCUboot application, delta/full nRF9160 modem) + FOTA using experimental Advanced Firmware Update object (/33629) + +endchoice + +config ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160 + def_bool ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_MODEM || ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_FULL_MODEM + +config ANJAY_ZEPHYR_OTA_MCUBOOT + def_bool ANJAY_ZEPHYR_FOTA || ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160 config ANJAY_ZEPHYR_PERSISTENCE bool "Enable persistence" @@ -221,6 +336,12 @@ config ANJAY_ZEPHYR_PERSISTENCE Enables persistence of Access Control Object, Security Object and Server Object. +config ANJAY_ZEPHYR_PERSISTENCE_ATTR_STORAGE + bool "Enable persistence of Attribute Storage" + depends on ANJAY_ZEPHYR_PERSISTENCE + select ANJAY_WITH_ATTR_STORAGE + default n + config ANJAY_ZEPHYR_FACTORY_PROVISIONING bool "Use factory provisioning" depends on ANJAY_ZEPHYR_PERSISTENCE diff --git a/compat/zephyr_tls_compat.c b/compat/zephyr_tls_compat.c index e61f0c6..932f0f2 100644 --- a/compat/zephyr_tls_compat.c +++ b/compat/zephyr_tls_compat.c @@ -35,6 +35,10 @@ #include "net_impl.h" #include "zephyr_tls_compat.h" +#if __has_include("ncs_version.h") +# include "ncs_version.h" +#endif // __has_include("ncs_version.h") + avs_error_t _avs_crypto_initialize_global_state(void) { return AVS_OK; } @@ -112,7 +116,14 @@ static int ensure_modem_deactivated( if (!result) { state->needs_gnss_restart = true; } - if (!result || result == -NRF_EPERM) { + if (!result + || result == +# if NCS_VERSION_NUMBER >= 0x20300 + -NRF_EACCES +# else // NCS_VERSION_NUMBER >= 0x20300 + -NRF_EPERM +# endif // NCS_VERSION_NUMBER >= 0x20300 + ) { result = lte_lc_offline(); } } diff --git a/config/anjay/anjay_config.h b/config/anjay/anjay_config.h index 678581f..8a5abfc 100644 --- a/config/anjay/anjay_config.h +++ b/config/anjay/anjay_config.h @@ -308,6 +308,14 @@ # define ANJAY_WITH_CORE_PERSISTENCE #endif // CONFIG_ANJAY_WITH_CORE_PERSISTENCE +/** + * Disable automatic closing of server connection sockets after + * MAX_TRANSMIT_WAIT of inactivity. + */ +#ifdef CONFIG_ANJAY_WITHOUT_QUEUE_MODE_AUTOCLOSE +# define ANJAY_WITHOUT_QUEUE_MODE_AUTOCLOSE +#endif // CONFIG_ANJAY_WITHOUT_QUEUE_MODE_AUTOCLOSE + /** * Enable support for CoAP Content-Format numerical values 1541-1544 that have * been used before final LwM2M TS 1.0. @@ -581,6 +589,14 @@ # define ANJAY_WITH_MODULE_FW_UPDATE #endif // CONFIG_ANJAY_WITH_MODULE_FW_UPDATE +/** + * Enable advanced_fw_update module (implementation of the 33629 custom + * Advanced Firmware Update object). + */ +#ifdef CONFIG_ANJAY_WITH_MODULE_ADVANCED_FW_UPDATE +# define ANJAY_WITH_MODULE_ADVANCED_FW_UPDATE +#endif // CONFIG_ANJAY_WITH_MODULE_ADVANCED_FW_UPDATE + /** * Disable support for PUSH mode Firmware Update. * @@ -632,13 +648,79 @@ * AVS_COMMONS_WITH_AVS_PERSISTENCE to be enabled in avs_commons * configuration. * - * IMPORTANT: Only available with the boostrapper feature. Ignored in the open + * IMPORTANT: Only available with the bootstrapper feature. Ignored in the open * source version. */ #ifdef CONFIG_ANJAY_WITH_MODULE_BOOTSTRAPPER # define ANJAY_WITH_MODULE_BOOTSTRAPPER #endif // CONFIG_ANJAY_WITH_MODULE_BOOTSTRAPPER +/** + * Enable the SIM bootstrap module, which enables reading the SIM bootstrap + * information from a smartcard, which can then be passed through to the + * bootstrapper module. + * + * Requires ANJAY_WITH_MODULE_BOOTSTRAPPER to be enabled. + * + * IMPORTANT: Only available with the bootstrapper feature. Ignored in the open + * source version. + */ +#ifdef CONFIG_ANJAY_WITH_MODULE_SIM_BOOTSTRAP +# define ANJAY_WITH_MODULE_SIM_BOOTSTRAP +#endif // CONFIG_ANJAY_WITH_MODULE_SIM_BOOTSTRAP + +/** + * Forced ID of the file to read the SIM bootstrap information from. + * + * If not defined (default), the bootstrap information file will be discovered + * through the ODF file, as mandated by the specification. + * + * Requires ANJAY_WITH_MODULE_BOOTSTRAPPER to be enabled. At most one of + * ANJAY_MODULE_SIM_BOOTSTRAP_HARDCODED_FILE_ID and + * ANJAY_MODULE_SIM_BOOTSTRAP_DATA_OID_OVERRIDE_HEX may be defined at the + * same time. + * + * IMPORTANT: Only available with the bootstrapper feature. Ignored in the open + * source version. + */ +#ifdef CONFIG_ANJAY_MODULE_SIM_BOOTSTRAP_DISCOVERY_MODE_HARDCODED +# define ANJAY_MODULE_SIM_BOOTSTRAP_HARDCODED_FILE_ID \ + CONFIG_ANJAY_MODULE_SIM_BOOTSTRAP_FILE_ID +#endif // CONFIG_ANJAY_MODULE_SIM_BOOTSTRAP_DISCOVERY_MODE_HARDCODED + +/** + * Overridden OID of the SIM bootstrap information to look for in the DODF file, + * expressed as a hexlified DER representation (without the header). + * + * This is the hexlified expected value of the 'id' field within the 'OidDO' + * sequence in the DODF file (please refer to the PKCS #15 document for more + * information). + * + * If not defined, the default value of "672b0901", which corresponds to + * OID 2.23.43.9.1 {joint-iso-itu-t(2) international-organizations(23) wap(43) + * oma-lwm2m(9) lwm2m-bootstrap(1)}, will be used. + * + * No other values than the default are valid according to the specification, + * but some SIM cards are known to use other non-standard values, e.g. + * "0604672b0901" - including a superfluous nested BER-TLV header, as + * erroneously illustrated in the EF(DODF-bootstrap) file coding example in + * LwM2M TS 1.2 and earlier (fixed in LwM2M TS 1.2.1) - which is interpreted as + * OID 0.6.4.103.43.9.1 (note that it is invalid as the 0.6 tree does not exist + * in the repository as of writing this note). + * + * Requires ANJAY_WITH_MODULE_BOOTSTRAPPER to be enabled. At most one of + * ANJAY_MODULE_SIM_BOOTSTRAP_HARDCODED_FILE_ID and + * ANJAY_MODULE_SIM_BOOTSTRAP_DATA_OID_OVERRIDE_HEX may be defined at the + * same time. + * + * IMPORTANT: Only available with the bootstrapper feature. Ignored in the open + * source version. + */ +#ifdef CONFIG_ANJAY_MODULE_SIM_BOOTSTRAP_DISCOVERY_MODE_ODF +# define ANJAY_MODULE_SIM_BOOTSTRAP_DATA_OID_OVERRIDE_HEX \ + CONFIG_ANJAY_MODULE_SIM_BOOTSTRAP_OID +#endif // CONFIG_ANJAY_MODULE_SIM_BOOTSTRAP_DISCOVERY_MODE_ODF + /** * Enable factory provisioning module. Data provided during provisioning uses * SenML CBOR format. diff --git a/config/avsystem/coap/avs_coap_config.h b/config/avsystem/coap/avs_coap_config.h index a3f0dec..0310989 100644 --- a/config/avsystem/coap/avs_coap_config.h +++ b/config/avsystem/coap/avs_coap_config.h @@ -83,7 +83,9 @@ * * Only meaningful WITH_AVS_COAP_OBSERVE is enabled. */ -/* #undef WITH_AVS_COAP_OBSERVE_PERSISTENCE */ +#ifdef CONFIG_ANJAY_WITH_OBSERVE_PERSISTENCE +# define WITH_AVS_COAP_OBSERVE_PERSISTENCE +#endif // CONFIG_ANJAY_WITH_OBSERVE_PERSISTENCE /** * Enable support for the streaming API diff --git a/config/avsystem/commons/avs_commons_config.h b/config/avsystem/commons/avs_commons_config.h index 19ec3b2..6bcf32c 100644 --- a/config/avsystem/commons/avs_commons_config.h +++ b/config/avsystem/commons/avs_commons_config.h @@ -203,14 +203,6 @@ #define AVS_COMMONS_WITH_AVS_LOG #define AVS_COMMONS_WITH_AVS_NET -#if defined(CONFIG_ANJAY_WITH_CORE_PERSISTENCE) \ - || defined(CONFIG_ANJAY_WITH_ATTR_STORAGE) \ - || defined(CONFIG_ANJAY_WITH_MODULE_BOOTSTRAPPER) -# define AVS_COMMONS_WITH_AVS_PERSISTENCE -#endif // defined(CONFIG_ANJAY_WITH_CORE_PERSISTENCE) || - // defined(CONFIG_ANJAY_WITH_ATTR_STORAGE) || - // defined(CONFIG_ANJAY_WITH_MODULE_BOOTSTRAPPER) - /* #undef AVS_COMMONS_WITH_AVS_RBTREE */ #if defined(CONFIG_ANJAY_WITH_OBSERVE) \ @@ -742,6 +734,16 @@ void anjay_zephyr_mbedtls_entropy_init__(struct mbedtls_entropy_context *ctx); #define AVS_COMMONS_NET_WITH_TLS_SESSION_PERSISTENCE /**@}*/ +#if defined(CONFIG_ANJAY_WITH_CORE_PERSISTENCE) \ + || defined(CONFIG_ANJAY_WITH_ATTR_STORAGE) \ + || defined(CONFIG_ANJAY_WITH_MODULE_BOOTSTRAPPER) \ + || defined(AVS_COMMONS_NET_WITH_TLS_SESSION_PERSISTENCE) +# define AVS_COMMONS_WITH_AVS_PERSISTENCE +#endif // defined(CONFIG_ANJAY_WITH_CORE_PERSISTENCE) || + // defined(CONFIG_ANJAY_WITH_ATTR_STORAGE) || + // defined(CONFIG_ANJAY_WITH_MODULE_BOOTSTRAPPER) || + // defined(AVS_COMMONS_NET_WITH_TLS_SESSION_PERSISTENCE) + /** * Options related to avs_net's default implementation of TCP and UDP sockets. * @@ -793,6 +795,20 @@ void anjay_zephyr_mbedtls_entropy_init__(struct mbedtls_entropy_context *ctx); */ /* #undef AVS_COMMONS_NET_POSIX_AVS_SOCKET_HAVE_IN6_IS_ADDR_V4MAPPED */ +/** + * Should be defined if IPv4-mapped IPv6 addresses (::ffff:0.0.0.0/32) + * are NOT supported by the underlying platform. + * + * Enabling this flag will prevent avs_net from using IPv4-mapped IPv6 addresses + * and instead re-open and re-bind the socket if a connection to an IPv4 address + * is requested on a previously created IPv6 socket. + * + * This may result in otherwise redundant socket(), bind() and + * close() system calls to be performed, but may be necessary for + * interoperability with some platforms. + */ +/* #undef AVS_COMMONS_NET_POSIX_AVS_SOCKET_WITHOUT_IN6_V4MAPPED_SUPPORT */ + /** * Is the inet_ntop() function available? * diff --git a/deps/anjay b/deps/anjay index 2c88f02..a92b146 160000 --- a/deps/anjay +++ b/deps/anjay @@ -1 +1 @@ -Subproject commit 2c88f02c7b039790c5743c403fa0b52b12b3a354 +Subproject commit a92b146e2ef731f226a094a7727980e5c8d184f9 diff --git a/include_public/anjay_zephyr/config.h b/include_public/anjay_zephyr/config.h index 3b8a452..6db786b 100644 --- a/include_public/anjay_zephyr/config.h +++ b/include_public/anjay_zephyr/config.h @@ -159,11 +159,11 @@ int anjay_zephyr_config_get_private_key(char *buf, size_t buf_capacity); #ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF /** - * Get the GPS priority mode timeout stored in the settings. + * Check if the GPS priority mode is permitted. * - * @return GPS priority mode timeout. + * @return True if GPS priority mode is permitted. */ -uint32_t anjay_zephyr_config_get_gps_nrf_prio_mode_timeout(void); +bool anjay_zephyr_config_is_gps_nrf_prio_mode_permitted(void); /** * Get the GPS priority mode cooldown stored in the settings. diff --git a/include_public/anjay_zephyr/location_services.h b/include_public/anjay_zephyr/location_services.h new file mode 100644 index 0000000..bcb6857 --- /dev/null +++ b/include_public/anjay_zephyr/location_services.h @@ -0,0 +1,145 @@ +/* + * Copyright 2020-2023 AVSystem + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES +# include + +/** + * Enumeration that specifies the result of the location services request. + */ +typedef enum { + /** + * Request completed successfully. + */ + ANJAY_ZEPHYR_LOCATION_SERVICES_SUCCESSFUL, + /** + * Anjay was unable to send a request to the server. + */ + ANJAY_ZEPHYR_LOCATION_SERVICES_UNABLE_TO_SEND, + /** + * No response obtained from the server within 90 seconds after sending + * request. + */ + ANJAY_ZEPHYR_LOCATION_SERVICES_NO_RESPONSE, + /** + * Response from the server had invalid data. + */ + ANJAY_ZEPHYR_LOCATION_SERVICES_IMPROPER_RESPONSE, + /** + * Response from the server contained a result code that indicates a + * temporary failure. It means that user should utilize exponential backoff + * while retrying the request. This value will only be passed to user + * callback when user disables the exponential backoff mechanism implemented + * in Anjay-zephyr when calling anjay_zephyr_location_services_..._request + * with exponential_backoff set to false. + */ + ANJAY_ZEPHYR_LOCATION_SERVICES_TEMPORARY_FAILURE, + /** + * Response from the server contained a result code that indicates a + * permanent failure. It means that further location services requests will + * not be processed before device reboot. + */ + ANJAY_ZEPHYR_LOCATION_SERVICES_PERMANENT_FAILURE +} anjay_zephyr_location_services_request_result_t; + +/** + * Enumeration that specifies the available ground fix location request types. + */ +typedef enum { + /** + * Request update of location of the device only on the server side (new + * location will not be sent back to the device) based on the nearest + * cellular tower. + */ + ANJAY_ZEPHYR_LOC_SERVICES_GF_LOCATION_REQUEST_INFORM_SINGLE, + /** + * Request update of location of the device only on the server side (new + * location will not be sent to the device) based on the nearest cellular + * tower and its neighbor cellular towers. + */ + ANJAY_ZEPHYR_LOC_SERVICES_GF_LOCATION_REQUEST_INFORM_MULTI, + /** + * Request location of the device based on the nearest cellular tower. + */ + ANJAY_ZEPHYR_LOC_SERVICES_GF_LOCATION_REQUEST_REQUEST_SINGLE, + /** + * Request location of the device based on the nearest cellular tower and + * its neighbor cellular towers. + */ + ANJAY_ZEPHYR_LOC_SERVICES_GF_LOCATION_REQUEST_REQUEST_MULTI +} anjay_zephyr_location_services_gf_location_request_type_t; + +/** + * Structure with location based on cellular towers. + */ +typedef struct { + /** + * Location latitude angle. + */ + double latitude; + /** + * Location longitude angle. + */ + double longitude; + /** + * The radius of the uncertainty circle around the location in meters. + */ + double accuracy; +} anjay_zephyr_location_services_ground_fix_location_t; + +/** + * Callback function type used by @ref + * anjay_zephyr_location_services_gf_location_request. It is called when the + * ground fix location request is completed. + * + * @param result Specifies the result with which the request processing was + * completed. + * + * @param location Received location. This structure contains a valid value + * only if @p result is equal to ANJAY_ZEPHYR_LOCATION_SERVICES_SUCCESSFUL and + * user has selected a type of request that sends the location to the device + * (ANJAY_ZEPHYR_LOC_SERVICES_GF_LOCATION_REQUEST_REQUEST_SINGLE or + * ANJAY_ZEPHYR_LOC_SERVICES_GF_LOCATION_REQUEST_REQUEST_MULTI). + */ +typedef void anjay_zephyr_location_services_gf_location_request_cb_t( + anjay_zephyr_location_services_request_result_t result, + anjay_zephyr_location_services_ground_fix_location_t location); + +/** + * Request ground fix location. + * + * @param anjay Anjay Object to operate on. + * + * @param cb User callback called when the request is + * completed. + * + * @param request_type Request type. + * + * @param exponential_backoff Specifies whether the exponential backoff + * mechanism is enabled. If so, and if the server response contains a result + * code indicating a temporary failure, the request will be rescheduled with an + * appropriate delay without calling user callback. + * + * @return 0 for success, or -1 in case of error. + */ +int anjay_zephyr_location_services_gf_location_request( + anjay_t *anjay, + anjay_zephyr_location_services_gf_location_request_cb_t *cb, + anjay_zephyr_location_services_gf_location_request_type_t request_type, + bool exponential_backoff); +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES diff --git a/include_public/anjay_zephyr/objects.h b/include_public/anjay_zephyr/objects.h index 715db71..0b0c3e7 100644 --- a/include_public/anjay_zephyr/objects.h +++ b/include_public/anjay_zephyr/objects.h @@ -162,3 +162,25 @@ void anjay_zephyr_location_object_release( */ void anjay_zephyr_location_object_update( anjay_t *anjay, const anjay_dm_object_def_t *const *def); + +/** + * Create LwM2M Light Control object. + * + * @param user_leds Pointer to an array with LED + * configurations. + * @param user_leds_len Length of @p user_leds array. + * @return Pointer to pointer to a structure + * defining a LwM2M Light Control object. + */ +const anjay_dm_object_def_t ** +anjay_zephyr_light_control_object_create(const struct gpio_dt_spec *user_leds, + uint16_t user_leds_len); +/** + * Release memory related to LwM2M light control object. + * + * @param out_def Pointer to definition of LwM2M light + * Control object previously obtained by @ref + * anjay_zephyr_light_control_object_create. + */ +void anjay_zephyr_light_control_object_release( + const anjay_dm_object_def_t ***out_def); diff --git a/src/afu/nrf9160/afu_nrf9160.c b/src/afu/nrf9160/afu_nrf9160.c new file mode 100644 index 0000000..e3ef406 --- /dev/null +++ b/src/afu/nrf9160/afu_nrf9160.c @@ -0,0 +1,305 @@ +/* + * Copyright 2020-2023 AVSystem + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_FULL_MODEM +# include +#endif // CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_FULL_MODEM + +#include "afu_nrf9160.h" + +LOG_MODULE_REGISTER(afu_nrf9160); + +static int fw_update_expected_target_mask = -1; +static int fw_update_current_target = -1; + +#define MINUSONE(...) -1 +static int fw_update_finished_targets[AFU_NRF9160_INSTANCE_COUNT] = { + LISTIFY(AFU_NRF9160_INSTANCE_COUNT, MINUSONE, (, )) +}; + +static uint8_t identify_buf[32] __aligned(__alignof(avs_max_align_t)); +static size_t identify_buf_count; + +static bool update_requested; + +int _anjay_zephyr_afu_nrf9160_install(anjay_t *anjay) { + int result = anjay_advanced_fw_update_install(anjay, NULL); + + if (!result) { + result = _anjay_zephyr_afu_nrf9160_application_install(anjay); + } + if (!result) { + result = _anjay_zephyr_afu_nrf9160_modem_install(anjay); + } + + return result; +} + +int _anjay_zephyr_afu_nrf9160_common_open(int expected_image_type_mask) { + if (fw_update_expected_target_mask >= 0) { + LOG_ERR("Concurrent firmware updates are not supported"); + return ANJAY_ADVANCED_FW_UPDATE_ERR_OUT_OF_MEMORY; + } + + fw_update_expected_target_mask = expected_image_type_mask; + fw_update_current_target = -1; + identify_buf_count = 0; + return 0; +} + +static void dfu_target_cb(enum dfu_target_evt_id evt) { + (void) evt; +} + +int _anjay_zephyr_afu_nrf9160_common_write(anjay_iid_t iid, + void *user_ptr, + const void *data, + size_t length) { + (void) iid; + (void) user_ptr; + + assert(fw_update_expected_target_mask >= 0); + + if (identify_buf_count < sizeof(identify_buf)) { + assert(fw_update_current_target < 0); + + size_t identify_data_length = + AVS_MIN(length, sizeof(identify_buf) - identify_buf_count); + + memcpy(identify_buf + identify_buf_count, data, identify_data_length); + identify_buf_count += identify_data_length; + data = (const char *) data + identify_data_length; + length -= identify_data_length; + + if (identify_buf_count >= sizeof(identify_buf)) { + enum dfu_target_image_type target = + dfu_target_img_type(identify_buf, sizeof(identify_buf)); + + if (!(target & fw_update_expected_target_mask)) { + LOG_ERR("Unsupported or unexpected image format"); + identify_buf_count = 0; + return ANJAY_ADVANCED_FW_UPDATE_ERR_UNSUPPORTED_PACKAGE_TYPE; + } + + int result = dfu_target_init(target, 0, 0, dfu_target_cb); + + if (result) { + identify_buf_count = 0; + return result; + } + + fw_update_current_target = target; + + if (dfu_target_write(identify_buf, sizeof(identify_buf))) { + return -1; + } + } + } + + if (identify_buf_count >= sizeof(identify_buf)) { + assert(fw_update_current_target >= 0 + && (fw_update_current_target & fw_update_expected_target_mask)); + if (length && dfu_target_write(data, length)) { + return -1; + } + } + return 0; +} + +int _anjay_zephyr_afu_nrf9160_common_finish(anjay_iid_t iid, void *anjay_) { + (void) iid; + + anjay_t *anjay = (anjay_t *) anjay_; + + assert(fw_update_expected_target_mask >= 0); + assert(identify_buf_count < sizeof(identify_buf) + || (fw_update_current_target >= 0 + && (fw_update_current_target & fw_update_expected_target_mask))); + assert(iid < AVS_ARRAY_SIZE(fw_update_finished_targets)); + + int result = 0; + + if (identify_buf_count < sizeof(identify_buf)) { + // Format not yet determined, fail + return ANJAY_ADVANCED_FW_UPDATE_ERR_UNSUPPORTED_PACKAGE_TYPE; + } else if (dfu_target_done(true)) { + result = -1; + } + + if (!result) { + fw_update_finished_targets[iid] = fw_update_current_target; + } else { + fw_update_finished_targets[iid] = -1; + } + + int result2 = _anjay_zephyr_afu_nrf9160_update_linked_instances( + anjay, iid, + result ? ANJAY_ADVANCED_FW_UPDATE_STATE_IDLE + : ANJAY_ADVANCED_FW_UPDATE_STATE_DOWNLOADED); + + fw_update_expected_target_mask = -1; + fw_update_current_target = -1; + return result ? result : result2; +} + +void _anjay_zephyr_afu_nrf9160_common_reset(anjay_iid_t iid, void *anjay_) { + (void) iid; + assert(iid < AVS_ARRAY_SIZE(fw_update_finished_targets)); + + anjay_t *anjay = (anjay_t *) anjay_; + + if (fw_update_expected_target_mask >= 0) { + if (identify_buf_count >= sizeof(identify_buf)) { + assert(fw_update_current_target >= 0 + && (fw_update_current_target + & fw_update_expected_target_mask)); + dfu_target_done(false); + dfu_target_reset(); + } + fw_update_current_target = -1; + fw_update_expected_target_mask = -1; + } + + fw_update_finished_targets[iid] = -1; + + _anjay_zephyr_afu_nrf9160_update_linked_instances( + anjay, iid, ANJAY_ADVANCED_FW_UPDATE_STATE_IDLE); +} + +int _anjay_zephyr_afu_nrf9160_common_perform( + anjay_iid_t iid, + void *anjay_, + const anjay_iid_t *requested_supplemental_iids, + size_t requested_supplemental_iids_count) { + anjay_t *anjay = (anjay_t *) anjay_; + bool do_update[AFU_NRF9160_INSTANCE_COUNT] = { 0 }; + int result = 0; + + assert(iid < AVS_ARRAY_SIZE(do_update)); + + do_update[iid] = true; + + if (!requested_supplemental_iids) { + result = anjay_advanced_fw_update_get_linked_instances( + anjay, iid, &requested_supplemental_iids, + &requested_supplemental_iids_count); + if (result) { + return result; + } + } + + assert(!requested_supplemental_iids_count || requested_supplemental_iids); + + for (size_t i = 0; i < requested_supplemental_iids_count; ++i) { + assert(requested_supplemental_iids[i] < AVS_ARRAY_SIZE(do_update)); + do_update[requested_supplemental_iids[i]] = true; + } + + if (!result && do_update[AFU_NRF9160_IID_APPLICATION]) { + result = dfu_target_mcuboot_schedule_update(0); + } + if (!result && do_update[AFU_NRF9160_IID_MODEM]) { +#ifdef CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_FULL_MODEM + if (fw_update_finished_targets[AFU_NRF9160_IID_MODEM] + == DFU_TARGET_IMAGE_TYPE_FULL_MODEM) { + result = dfu_target_full_modem_schedule_update(0); + } else +#endif // CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_FULL_MODEM + result = dfu_target_modem_delta_schedule_update(0); + + if (!result) { + _anjay_zephyr_afu_nrf9160_modem_save_result( + ANJAY_ADVANCED_FW_UPDATE_RESULT_FAILED); + } + } + if (!result) { + update_requested = true; + anjay_event_loop_interrupt(anjay); + } + + return result; +} + +bool _anjay_zephyr_afu_nrf9160_requested(void) { + return update_requested; +} + +void _anjay_zephyr_afu_nrf9160_reboot(void) { +#ifdef CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_FULL_MODEM + if (fw_update_finished_targets[AFU_NRF9160_IID_MODEM] + == DFU_TARGET_IMAGE_TYPE_FULL_MODEM) { + _anjay_zephyr_afu_nrf9160_full_modem_apply(); + } +#endif // CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_FULL_MODEM + + LOG_INF("Rebooting to perform a firmware upgrade..."); + LOG_PANIC(); + sys_reboot(SYS_REBOOT_WARM); +} + +int _anjay_zephyr_afu_nrf9160_update_linked_instances( + anjay_t *anjay, + anjay_iid_t source_iid, + anjay_advanced_fw_update_state_t source_state) { + anjay_advanced_fw_update_state_t states[AFU_NRF9160_INSTANCE_COUNT]; + + assert(source_iid < AVS_ARRAY_SIZE(states)); + + states[source_iid] = source_state; + + for (anjay_iid_t i = 0; i < AVS_ARRAY_SIZE(states); ++i) { + if (i != source_iid) { + if (anjay_advanced_fw_update_get_state(anjay, i, &states[i])) { + states[i] = ANJAY_ADVANCED_FW_UPDATE_STATE_IDLE; + } + } + } + + int result = 0; + + for (anjay_iid_t i = 0; i < AVS_ARRAY_SIZE(states); ++i) { + anjay_iid_t linked_instances[AFU_NRF9160_INSTANCE_COUNT - 1]; + size_t linked_instances_count = 0; + + // Make all downloaded instances linked + if (states[i] == ANJAY_ADVANCED_FW_UPDATE_STATE_DOWNLOADED) { + for (anjay_iid_t j = 0; j < AVS_ARRAY_SIZE(states); ++j) { + if (i != j + && states[j] == ANJAY_ADVANCED_FW_UPDATE_STATE_DOWNLOADED) { + linked_instances[linked_instances_count++] = j; + } + } + } + + int partial_result = anjay_advanced_fw_update_set_linked_instances( + anjay, i, linked_instances, linked_instances_count); + + if (!result) { + result = partial_result; + } + } + + return result; +} diff --git a/src/afu/nrf9160/afu_nrf9160.h b/src/afu/nrf9160/afu_nrf9160.h new file mode 100644 index 0000000..8e968f1 --- /dev/null +++ b/src/afu/nrf9160/afu_nrf9160.h @@ -0,0 +1,62 @@ +/* + * Copyright 2020-2023 AVSystem + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include + +#ifdef CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160 + +# define AFU_NRF9160_IID_APPLICATION 0 +# define AFU_NRF9160_IID_MODEM 1 +# define AFU_NRF9160_INSTANCE_COUNT 2 + +int _anjay_zephyr_afu_nrf9160_application_install(anjay_t *anjay); +void _anjay_zephyr_afu_nrf9160_application_apply(void); + +int _anjay_zephyr_afu_nrf9160_modem_install(anjay_t *anjay); +void _anjay_zephyr_afu_nrf9160_modem_save_result( + anjay_advanced_fw_update_result_t result); +void _anjay_zephyr_afu_nrf9160_modem_apply(void); + +# ifdef CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_FULL_MODEM +void _anjay_zephyr_afu_nrf9160_full_modem_apply(void); +# endif // CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_FULL_MODEM + +int _anjay_zephyr_afu_nrf9160_install(anjay_t *anjay); +int _anjay_zephyr_afu_nrf9160_common_open(int expected_image_type_mask); +int _anjay_zephyr_afu_nrf9160_common_write(anjay_iid_t iid, + void *user_ptr, + const void *data, + size_t length); +int _anjay_zephyr_afu_nrf9160_common_finish(anjay_iid_t iid, void *anjay_); +void _anjay_zephyr_afu_nrf9160_common_reset(anjay_iid_t iid, void *anjay_); +int _anjay_zephyr_afu_nrf9160_common_perform( + anjay_iid_t iid, + void *anjay_, + const anjay_iid_t *requested_supplemental_iids, + size_t requested_supplemental_iids_count); +bool _anjay_zephyr_afu_nrf9160_requested(void); +void _anjay_zephyr_afu_nrf9160_reboot(void); + +int _anjay_zephyr_afu_nrf9160_update_linked_instances( + anjay_t *anjay, + anjay_iid_t source_iid, + anjay_advanced_fw_update_state_t source_state); + +#endif // CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160 diff --git a/src/afu/nrf9160/afu_nrf9160_application.c b/src/afu/nrf9160/afu_nrf9160_application.c new file mode 100644 index 0000000..9850f96 --- /dev/null +++ b/src/afu/nrf9160/afu_nrf9160_application.c @@ -0,0 +1,182 @@ +/* + * Copyright 2020-2023 AVSystem + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include +#include + +#include "../../utils.h" +#include "afu_nrf9160.h" + +LOG_MODULE_REGISTER(afu_nrf9160_app); + +#define SETTINGS_ROOT_NAME "anjay_afu_nrf9160_app" +#define SETTINGS_APP_JUST_UPDATED_KEY "app_just_updated" + +static bool just_updated; + +static uint32_t dfu_buf[CONFIG_IMG_BLOCK_BUF_SIZE / sizeof(uint32_t)]; + +#define EXPECTED_IMAGE_TYPE DFU_TARGET_IMAGE_TYPE_MCUBOOT + +static int fw_stream_open(anjay_iid_t iid, void *user_ptr) { + (void) iid; + (void) user_ptr; + + int result = _anjay_zephyr_afu_nrf9160_common_open(EXPECTED_IMAGE_TYPE); + + if (!result) { + memset(dfu_buf, 0, sizeof(dfu_buf)); + } + + return result; +} + +static const char *fw_get_current_version(anjay_iid_t iid, void *user_ptr) { + (void) iid; + (void) user_ptr; + + static char fw_version[BOOT_IMG_VER_STRLEN_MAX]; + + if (_anjay_zephyr_get_fw_version_image_0(fw_version, sizeof(fw_version))) { + return NULL; + } + + return fw_version; +} + +static const char *fw_get_pkg_version(anjay_iid_t iid, void *user_ptr) { + (void) iid; + (void) user_ptr; + + static char fw_version[BOOT_IMG_VER_STRLEN_MAX]; + + if (_anjay_zephyr_get_fw_version_image_1(fw_version, sizeof(fw_version))) { + return NULL; + } + + return fw_version; +} + +static const anjay_advanced_fw_update_handlers_t handlers = { + .stream_open = fw_stream_open, + .stream_write = _anjay_zephyr_afu_nrf9160_common_write, + .stream_finish = _anjay_zephyr_afu_nrf9160_common_finish, + .reset = _anjay_zephyr_afu_nrf9160_common_reset, + .get_pkg_version = fw_get_pkg_version, + .get_current_version = fw_get_current_version, + .perform_upgrade = _anjay_zephyr_afu_nrf9160_common_perform +}; + +int _anjay_zephyr_afu_nrf9160_application_install(anjay_t *anjay) { + anjay_advanced_fw_update_initial_state_t state = { 0 }; + + if (just_updated) { + state.result = ANJAY_ADVANCED_FW_UPDATE_RESULT_SUCCESS; + } + + int result = + dfu_target_mcuboot_set_buf((uint8_t *) dfu_buf, sizeof(dfu_buf)); + + if (!result) { + result = anjay_advanced_fw_update_instance_add( + anjay, AFU_NRF9160_IID_APPLICATION, "application", &handlers, + anjay, &state); + } + + if (!result && just_updated) { + if (settings_delete(SETTINGS_ROOT_NAME + "/" SETTINGS_APP_JUST_UPDATED_KEY)) { + LOG_ERR("Couldn't delete the just_updated flag"); + } + + just_updated = false; + } + + return result; +} + +static int fw_settings_set(const char *key, + size_t len, + settings_read_cb read_cb, + void *cb_arg) { + if (strcmp(key, SETTINGS_APP_JUST_UPDATED_KEY) != 0) { + return -ENOENT; + } + + if (len > 1) { + return -EINVAL; + } + + char value = 0; + + int result = read_cb(cb_arg, &value, len); + + if (result < 0) { + return result; + } + + if (value != 0) { + just_updated = true; + } + + return 0; +} + +SETTINGS_STATIC_HANDLER_DEFINE( + anjay_fw_update, SETTINGS_ROOT_NAME, NULL, fw_settings_set, NULL, NULL); + +void _anjay_zephyr_afu_nrf9160_application_apply(void) { + int settings_state = settings_subsys_init(); + + if (settings_state) { + LOG_ERR("Couldn't init settings subsystem"); + } else { + settings_load_subtree(SETTINGS_ROOT_NAME); + } + + if (just_updated) { + LOG_INF("Undelivered previous firmware update success"); + } + + // Image may be unconfirmed, because: + // - we've just did a FOTA of the device and new + // firmware is being run + // - the firmware was flashed using external programmer + // + // In both cases we want to mark the image as + // confirmed (to either accept the new firmware, + // or put MCUBoot in consistent state after flashing), + // but only in the former case we should notify the + // server that we've successfully updated the firmware. + // + // We can differentiate these two situations by taking + // the retval of boot_write_img_confirmed(). + if (!boot_is_img_confirmed() && !boot_write_img_confirmed()) { + LOG_INF("Successfully updated firmware"); + + if (!just_updated) { + just_updated = true; + + if (settings_save_one(SETTINGS_ROOT_NAME + "/" SETTINGS_APP_JUST_UPDATED_KEY, + "1", 1)) { + LOG_ERR("Couldn't save the just_updated flag"); + } + } + } +} diff --git a/src/afu/nrf9160/afu_nrf9160_modem.c b/src/afu/nrf9160/afu_nrf9160_modem.c new file mode 100644 index 0000000..483e5d1 --- /dev/null +++ b/src/afu/nrf9160/afu_nrf9160_modem.c @@ -0,0 +1,264 @@ +/* + * Copyright 2020-2023 AVSystem + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include +#include +#ifdef CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_FULL_MODEM +# include +# include +#endif // CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_FULL_MODEM + +#include +#include + +#include "afu_nrf9160.h" + +#include + +#ifdef CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_FULL_MODEM +# if !defined(PM_DFU_TARGET_FMFU_DEV) \ + || !defined(PM_DFU_TARGET_FMFU_OFFSET) \ + || !defined(PM_DFU_TARGET_FMFU_SIZE) +# error "dfu_target_fmfu partition must be defined in Partition Manager config" +# endif // !defined(PM_DFU_TARGET_FMFU_DEV) || + // !defined(PM_DFU_TARGET_FMFU_OFFSET) || + // !defined(PM_DFU_TARGET_FMFU_SIZE) +#endif // CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_FULL_MODEM + +LOG_MODULE_REGISTER(afu_nrf9160_modem); + +#define SETTINGS_ROOT_NAME "anjay_afu_nrf9160_modem" +#define SETTINGS_RESULT_KEY "result" + +#define EXPECTED_IMAGE_TYPE_MASK DFU_TARGET_IMAGE_TYPE_ANY_MODEM + +#ifdef CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_FULL_MODEM +static uint8_t temp_full_modem_buf[4096]; +#endif // CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_FULL_MODEM + +NRF_MODEM_LIB_ON_INIT(serial_lte_modem_init_hook, on_modem_lib_init, NULL); + +/* Initialized to value different than success (0) */ +static int modem_lib_init_result = -1; +static anjay_advanced_fw_update_result_t restored_update_result; + +static void on_modem_lib_init(int ret, void *ctx) { + modem_lib_init_result = ret; +} + +static int fw_stream_open(anjay_iid_t iid, void *user_ptr) { + (void) iid; + (void) user_ptr; + + int result = + _anjay_zephyr_afu_nrf9160_common_open(EXPECTED_IMAGE_TYPE_MASK); + + if (!result) { + // A file from previous delta upgrade attempt may be left dormant, + // try to remove it just in case + dfu_target_modem_delta_reset(); + } + + return result; +} + +static const char *fw_get_current_version(anjay_iid_t iid, void *user_ptr) { + (void) iid; + (void) user_ptr; + + static char buf[MODEM_INFO_MAX_RESPONSE_SIZE]; + + if (modem_info_init() + || modem_info_string_get(MODEM_INFO_FW_VERSION, buf, sizeof(buf)) + < 0) { + return NULL; + } + + return buf; +} + +void _anjay_zephyr_afu_nrf9160_modem_save_result( + anjay_advanced_fw_update_result_t result) { + uint8_t result8 = (uint8_t) result; + + if ((anjay_advanced_fw_update_result_t) result8 != result) { + LOG_ERR("Invalid result"); + } else if (settings_subsys_init()) { + LOG_ERR("Couldn't init settings subsystem"); + } else if (settings_save_one(SETTINGS_ROOT_NAME "/" SETTINGS_RESULT_KEY, + &result8, 1)) { + LOG_ERR("Couldn't save update result"); + } +} + +static const anjay_advanced_fw_update_handlers_t handlers = { + .stream_open = fw_stream_open, + .stream_write = _anjay_zephyr_afu_nrf9160_common_write, + .stream_finish = _anjay_zephyr_afu_nrf9160_common_finish, + .reset = _anjay_zephyr_afu_nrf9160_common_reset, + .get_current_version = fw_get_current_version, + .perform_upgrade = _anjay_zephyr_afu_nrf9160_common_perform +}; + +static int fw_settings_set(const char *key, + size_t len, + settings_read_cb read_cb, + void *cb_arg) { + if (strcmp(key, SETTINGS_RESULT_KEY) != 0) { + return -ENOENT; + } + + if (len > 1) { + return -EINVAL; + } + + uint8_t value = 0; + + int result = read_cb(cb_arg, &value, len); + + if (result < 0) { + return result; + } + + restored_update_result = (anjay_advanced_fw_update_result_t) value; + return 0; +} + +SETTINGS_STATIC_HANDLER_DEFINE(anjay_fw_update_modem, + SETTINGS_ROOT_NAME, + NULL, + fw_settings_set, + NULL, + NULL); + +int _anjay_zephyr_afu_nrf9160_modem_install(anjay_t *anjay) { + int result = 0; + +#ifdef CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_FULL_MODEM + result = dfu_target_full_modem_cfg( + &(const struct dfu_target_full_modem_params) { + .buf = temp_full_modem_buf, + .len = sizeof(temp_full_modem_buf), + .dev = &(struct dfu_target_fmfu_fdev) { + .dev = DEVICE_DT_GET(PM_DFU_TARGET_FMFU_DEV), + .offset = PM_DFU_TARGET_FMFU_OFFSET, + .size = PM_DFU_TARGET_FMFU_SIZE + } + }); + if (result) { + return result; + } +#endif // CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_FULL_MODEM + + int settings_state = settings_subsys_init(); + + if (settings_state) { + LOG_ERR("Couldn't init settings subsystem"); + } else { + settings_load_subtree(SETTINGS_ROOT_NAME); + } + + anjay_advanced_fw_update_initial_state_t state = { + .result = restored_update_result + }; + + result = anjay_advanced_fw_update_instance_add( + anjay, AFU_NRF9160_IID_MODEM, "modem", &handlers, anjay, &state); + if (!result && !settings_state + && settings_delete(SETTINGS_ROOT_NAME "/" SETTINGS_RESULT_KEY)) { + LOG_ERR("Couldn't delete the update result"); + } + + return result; +} + +static int fw_update_process_init_result(int err) { + switch (err) { + case 0: + case NRF_MODEM_DFU_RESULT_OK: + printk("Modem update suceeded, reboot\r\n"); + _anjay_zephyr_afu_nrf9160_modem_save_result( + ANJAY_ADVANCED_FW_UPDATE_RESULT_SUCCESS); + return 0; + case NRF_MODEM_DFU_RESULT_UUID_ERROR: + case NRF_MODEM_DFU_RESULT_AUTH_ERROR: + printk("Modem update failed, error: %d\r\n", err); + printk("Modem will use old firmware\r\n"); + _anjay_zephyr_afu_nrf9160_modem_save_result( + ANJAY_ADVANCED_FW_UPDATE_RESULT_INTEGRITY_FAILURE); + return 0; + case NRF_MODEM_DFU_RESULT_HARDWARE_ERROR: + case NRF_MODEM_DFU_RESULT_INTERNAL_ERROR: + printk("Modem update malfunction, error: %d, reboot\r\n", err); + _anjay_zephyr_afu_nrf9160_modem_save_result( + ANJAY_ADVANCED_FW_UPDATE_RESULT_FAILED); + return 0; + default: + return -1; + } +} + +void _anjay_zephyr_afu_nrf9160_modem_apply(void) { + int err = modem_lib_init_result; + + if (err && !fw_update_process_init_result(err)) { + _anjay_zephyr_afu_nrf9160_reboot(); + } +} + +#ifdef CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_FULL_MODEM +void _anjay_zephyr_afu_nrf9160_full_modem_apply(void) { + int result = nrf_modem_lib_shutdown(); + + if (result) { + LOG_ERR("Could not shut down nrf_modem_lib: %d", result); + goto finish; + } + + result = nrf_modem_lib_init(BOOTLOADER_MODE); + if (result) { + LOG_ERR("Could not initialize nrf_modem_lib in Bootloader (full DFU) " + "mode: %d", + result); + goto finish; + } + + result = fmfu_fdev_load(temp_full_modem_buf, sizeof(temp_full_modem_buf), + DEVICE_DT_GET(PM_DFU_TARGET_FMFU_DEV), + PM_DFU_TARGET_FMFU_OFFSET); + if (result) { + LOG_ERR("Could not perform Full Modem DFU: %d", result); + goto finish; + } + + result = nrf_modem_lib_shutdown(); + if (result) { + LOG_ERR("Could not shut down nrf_modem_lib for the second time: %d", + result); + goto finish; + } + + result = nrf_modem_lib_init(NORMAL_MODE); + if (result) { + LOG_ERR("Could not reinitialize nrf_modem_lib in normal mode: %d", + result); + } +finish: + fw_update_process_init_result(result); +} +#endif // CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160_APP_DELTA_FULL_MODEM diff --git a/src/anjay_shell.c b/src/anjay_shell.c index bb08548..59a65b2 100644 --- a/src/anjay_shell.c +++ b/src/anjay_shell.c @@ -16,6 +16,9 @@ #include #include +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION +# include +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION #include "version.h" #if __has_include("ncs_version.h") @@ -27,8 +30,12 @@ #include "persistence.h" #include "utils.h" +#include "location_services.h" #include "network/network.h" -#include "nrf_lc_jobs.h" + +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION +LOG_MODULE_REGISTER(anjay_zephyr_shell); +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION static int cmd_anjay_start(const struct shell *shell, size_t argc, char **argv) { @@ -42,7 +49,7 @@ cmd_anjay_start(const struct shell *shell, size_t argc, char **argv) { return 0; } - return ENOEXEC; + return -ENOEXEC; } static int cmd_anjay_stop(const struct shell *shell, size_t argc, char **argv) { @@ -58,7 +65,7 @@ static int cmd_anjay_stop(const struct shell *shell, size_t argc, char **argv) { return 0; } - return ENOEXEC; + return -ENOEXEC; } #ifdef WITH_ANJAY_ZEPHYR_CONFIG @@ -133,49 +140,82 @@ cmd_anjay_config_save(const struct shell *shell, size_t argc, char **argv) { } #endif // WITH_ANJAY_ZEPHYR_CONFIG -#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED -static int cmd_anjay_nls_cell_request(const struct shell *shell, - size_t argc, - char **argv, - void *data) { +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION +static void gf_location_request_cb( + anjay_zephyr_location_services_request_result_t result, + anjay_zephyr_location_services_ground_fix_location_t location) { + if (result == ANJAY_ZEPHYR_LOCATION_SERVICES_SUCCESSFUL) { + LOG_INF("Received ground fix location" + ", lat: %.3f deg, lon: %.3f deg, acc: %.3f m", + location.latitude, location.longitude, location.accuracy); + } else { + LOG_WRN("Ground fix location request failed, err: %d", (int) result); + } +} + +static int cmd_anjay_nls_gf_location_request(const struct shell *shell, + size_t argc, + char **argv, + void *data) { + int result = 0; SYNCHRONIZED(anjay_zephyr_global_anjay_mutex) { if (anjay_zephyr_global_anjay) { - struct anjay_zephyr_cell_request_job_args args = { + anjay_zephyr_location_services_gf_location_request_type_t req_type = + (anjay_zephyr_location_services_gf_location_request_type_t) (uintptr_t) + data; + struct anjay_zephyr_gf_location_request_job_args args = { .anjay = anjay_zephyr_global_anjay, - .request_type = - (enum anjay_zephyr_loc_assist_cell_request_type) (uintptr_t) - data + .cb = (req_type + == ANJAY_ZEPHYR_LOC_SERVICES_GF_LOCATION_REQUEST_REQUEST_SINGLE + || req_type + == ANJAY_ZEPHYR_LOC_SERVICES_GF_LOCATION_REQUEST_REQUEST_MULTI) + ? gf_location_request_cb + : NULL, + .request_type = req_type }; AVS_SCHED_NOW(anjay_get_scheduler(anjay_zephyr_global_anjay), NULL, - _anjay_zephyr_cell_request_job, &args, sizeof(args)); + _anjay_zephyr_gf_location_request_job, &args, + sizeof(args)); + } else { shell_warn(shell, "Anjay is not running"); + result = -ENOEXEC; } } - return 0; + return result; } -#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION #ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS static int cmd_anjay_nls_agps_request(const struct shell *shell, size_t argc, char **argv, void *data) { - ARG_UNUSED(argc); - ARG_UNUSED(argv); - + int result = 0; SYNCHRONIZED(anjay_zephyr_global_anjay_mutex) { if (anjay_zephyr_global_anjay) { anjay_t *anjay = anjay_zephyr_global_anjay; - AVS_SCHED_NOW(anjay_get_scheduler(anjay), NULL, - _anjay_zephyr_agps_request_job, &anjay, - sizeof(anjay)); + struct anjay_zephyr_agps_request_job_args args = { + .anjay = anjay_zephyr_global_anjay, + .cb = NULL, + .request_mask = LOC_SERVICES_A_GPS_FULL_MASK, + .exponential_backoff = false + }; + + if (result == 0) { + AVS_SCHED_NOW(anjay_get_scheduler(anjay), NULL, + _anjay_zephyr_agps_request_job, &args, + sizeof(args)); + } else { + shell_error(shell, "Wrong argument, request aborted"); + } } else { shell_warn(shell, "Anjay is not running"); + result = -ENOEXEC; } } - return 0; + return result; } #endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS @@ -271,12 +311,13 @@ SHELL_STATIC_SUBCMD_SET_CREATE( # endif // CONFIG_ANJAY_ZEPHYR_RUNTIME_CERT_CONFIG # endif // CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING # ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF - SHELL_CMD(OPTION_KEY_GPS_NRF_PRIO_MODE_TIMEOUT, + SHELL_CMD(OPTION_KEY_GPS_NRF_PRIO_MODE_PERMITTED, NULL, - "GPS priority mode timeout - determines (in seconds) for how " - "long the modem can run with LTE disabled, in case of " - "trouble with producing a GPS fix. Set to 0 to disable GPS " - "priority mode at all.", + "GPS priority mode permitted - if set, Anjay Zephyr will " + "temporarily activate the GPS priority over LTE idle mode in " + "case GPS fix cannot be produced. The mode will be " + "deactivated automatically after getting a GPS fix or after " + "40 seconds.", cmd_anjay_config_set), SHELL_CMD(OPTION_KEY_GPS_NRF_PRIO_MODE_COOLDOWN, NULL, @@ -289,8 +330,11 @@ SHELL_STATIC_SUBCMD_SET_CREATE( && !defined(CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING) SHELL_CMD(OPTION_KEY_USE_PERSISTENCE, NULL, - "Enables persistence of Access Control Object, Attribute " - "Storage, Security Object and Server Object.", + "Enables persistence of Access Control Object, " +# ifdef CONFIG_ANJAY_ZEPHYR_PERSISTENCE_ATTR_STORAGE + "Attribute Storage, " +# endif // CONFIG_ANJAY_ZEPHYR_PERSISTENCE_ATTR_STORAGE + "Security Object and Server Object.", cmd_anjay_config_set), # endif /* defined(CONFIG_ANJAY_ZEPHYR_PERSISTENCE) && \ * !defined(CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING) \ @@ -309,7 +353,7 @@ SHELL_STATIC_SUBCMD_SET_CREATE( SHELL_SUBCMD_SET_END); #endif // WITH_ANJAY_ZEPHYR_CONFIG -#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION # if KERNEL_VERSION_NUMBER >= 0x30300 || NCS_VERSION_NUMBER >= 0x20263 # define SUBCMD_DEF(Handler, Arg, Help) (Handler, Arg, Help) # else // KERNEL_VERSION_NUMBER >= 0x30300 || NCS_VERSION_NUMBER >= 0x20263 @@ -317,21 +361,29 @@ SHELL_STATIC_SUBCMD_SET_CREATE( # endif // KERNEL_VERSION_NUMBER >= 0x30300 || NCS_VERSION_NUMBER >= 0x20263 SHELL_SUBCMD_DICT_SET_CREATE( - sub_anjay_nls_cell_request, - cmd_anjay_nls_cell_request, - SUBCMD_DEF(inform_single, - (void *) (uintptr_t) LOC_ASSIST_CELL_REQUEST_INFORM_SINGLE, - "Inform single"), - SUBCMD_DEF(inform_multi, - (void *) (uintptr_t) LOC_ASSIST_CELL_REQUEST_INFORM_MULTI, - "Inform multiple"), - SUBCMD_DEF(request_single, - (void *) (uintptr_t) LOC_ASSIST_CELL_REQUEST_REQUEST_SINGLE, - "Request single"), - SUBCMD_DEF(request_multi, - (void *) (uintptr_t) LOC_ASSIST_CELL_REQUEST_REQUEST_MULTI, - "Request multiple")); -#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED + sub_anjay_nls_gf_location_request, + cmd_anjay_nls_gf_location_request, + SUBCMD_DEF( + inform_single, + (void *) (uintptr_t) + ANJAY_ZEPHYR_LOC_SERVICES_GF_LOCATION_REQUEST_INFORM_SINGLE, + "Inform single"), + SUBCMD_DEF( + inform_multi, + (void *) (uintptr_t) + ANJAY_ZEPHYR_LOC_SERVICES_GF_LOCATION_REQUEST_INFORM_MULTI, + "Inform multiple"), + SUBCMD_DEF( + request_single, + (void *) (uintptr_t) + ANJAY_ZEPHYR_LOC_SERVICES_GF_LOCATION_REQUEST_REQUEST_SINGLE, + "Request single"), + SUBCMD_DEF( + request_multi, + (void *) (uintptr_t) + ANJAY_ZEPHYR_LOC_SERVICES_GF_LOCATION_REQUEST_REQUEST_MULTI, + "Request multiple")); +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION SHELL_STATIC_SUBCMD_SET_CREATE( sub_anjay, @@ -340,21 +392,23 @@ SHELL_STATIC_SUBCMD_SET_CREATE( #ifdef WITH_ANJAY_ZEPHYR_CONFIG SHELL_CMD(config, &sub_anjay_config, "Configure Anjay params", NULL), #endif // WITH_ANJAY_ZEPHYR_CONFIG -#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED - SHELL_CMD(nls_cell_request, - &sub_anjay_nls_cell_request, - "Make a cell-based location request to Nordic Location " +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION + SHELL_CMD(nls_gf_location_request, + &sub_anjay_nls_gf_location_request, + "Make a ground fix location request to Nordic Location " "Services", NULL), -#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION #ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS SHELL_CMD(nls_agps_request, NULL, - "Make a manual A-GPS request to Nordic Location Services", + "Make a manual A-GPS request to Nordic Location Services, " + "user can provide minimum elevation angle for visible " + "satellites as an additional argument", cmd_anjay_nls_agps_request), #endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS #ifdef CONFIG_ANJAY_ZEPHYR_PERSISTENCE - SHELL_CMD(_anjay_zephyr_persistence_purge, + SHELL_CMD(persistence_purge, NULL, "Purges persisted Anjay state", cmd_anjay_persistence_purge), diff --git a/src/config.c b/src/config.c index 0032068..eff2440 100644 --- a/src/config.c +++ b/src/config.c @@ -111,8 +111,9 @@ struct anjay_zephyr_app_config { # endif // CONFIG_ANJAY_ZEPHYR_RUNTIME_CERT_CONFIG # endif // CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING # ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF - struct string_option gps_nrf_prio_mode_timeout; - char gps_nrf_prio_mode_timeout_storage[AVS_UINT_STR_BUF_SIZE(uint32_t)]; + struct string_option gps_nrf_prio_mode_permitted; + char gps_nrf_prio_mode_permitted_storage + [GPS_NRF_PRIO_MODE_PERMITTED_STORAGE_SIZE]; struct string_option gps_nrf_prio_mode_cooldown; char gps_nrf_prio_mode_cooldown_storage[AVS_UINT_STR_BUF_SIZE(uint32_t)]; # endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF @@ -140,8 +141,12 @@ struct anjay_zephyr_option { static config_option_validate_t string_validate; # endif // defined(CONFIG_WIFI) || // !defined(CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING) -# ifndef CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING +# if defined(CONFIG_ANJAY_ZEPHYR_GPS_NRF) \ + || !defined(CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING) static config_option_validate_t flag_validate; +# endif // defined(CONFIG_ANJAY_ZEPHYR_GPS_NRF) || + // !defined(CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING) +# ifndef CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING static config_option_validate_t psk_hex_validate; static config_option_validate_t security_mode_validate; # endif // CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING @@ -187,9 +192,9 @@ static struct anjay_zephyr_option string_options[] = { # endif // CONFIG_ANJAY_ZEPHYR_RUNTIME_CERT_CONFIG # endif // CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING # ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF - { AVS_QUOTE_MACRO(OPTION_KEY_GPS_NRF_PRIO_MODE_TIMEOUT), - "GPS priority mode timeout", &app_config.gps_nrf_prio_mode_timeout, - sizeof(app_config.gps_nrf_prio_mode_timeout_storage), uint32_validate }, + { AVS_QUOTE_MACRO(OPTION_KEY_GPS_NRF_PRIO_MODE_PERMITTED), + "GPS priority mode permitted", &app_config.gps_nrf_prio_mode_permitted, + sizeof(app_config.gps_nrf_prio_mode_permitted_storage), flag_validate }, { AVS_QUOTE_MACRO(OPTION_KEY_GPS_NRF_PRIO_MODE_COOLDOWN), "GPS priority mode cooldown", &app_config.gps_nrf_prio_mode_cooldown, sizeof(app_config.gps_nrf_prio_mode_cooldown_storage), uint32_validate }, @@ -327,10 +332,8 @@ void _anjay_zephyr_config_default_init(void) { psk_key_length_check); # endif // CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING # ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF - AVS_STATIC_ASSERT(sizeof(GPS_NRF_PRIO_MODE_TIMEOUT) - <= AVS_UINT_STR_BUF_SIZE(uint32_t), - gps_timeout_length_check); - AVS_STATIC_ASSERT(sizeof(GPS_NRF_PRIO_MODE_COOLDOWN) + AVS_STATIC_ASSERT(sizeof(AVS_QUOTE_MACRO( + CONFIG_ANJAY_ZEPHYR_GPS_NRF_PRIO_MODE_COOLDOWN)) <= AVS_UINT_STR_BUF_SIZE(uint32_t), gps_cooldown_length_check); # endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF @@ -368,13 +371,17 @@ void _anjay_zephyr_config_default_init(void) { .bootstrap.null_terminated = false, # endif // CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING # ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF - .gps_nrf_prio_mode_timeout_storage = GPS_NRF_PRIO_MODE_TIMEOUT, - .gps_nrf_prio_mode_timeout.length = - sizeof(GPS_NRF_PRIO_MODE_TIMEOUT), - .gps_nrf_prio_mode_timeout.null_terminated = true, - .gps_nrf_prio_mode_cooldown_storage = GPS_NRF_PRIO_MODE_COOLDOWN, - .gps_nrf_prio_mode_cooldown.length = - sizeof(GPS_NRF_PRIO_MODE_COOLDOWN), +# ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_PRIO_MODE_PERMITTED + .gps_nrf_prio_mode_permitted_storage = { 'y' }, +# else // CONFIG_ANJAY_ZEPHYR_GPS_NRF_PRIO_MODE_PERMITTED + .gps_nrf_prio_mode_permitted_storage = { 'n' }, +# endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_PRIO_MODE_PERMITTED + .gps_nrf_prio_mode_permitted.length = 1, + .gps_nrf_prio_mode_permitted.null_terminated = false, + .gps_nrf_prio_mode_cooldown_storage = AVS_QUOTE_MACRO( + CONFIG_ANJAY_ZEPHYR_GPS_NRF_PRIO_MODE_COOLDOWN), + .gps_nrf_prio_mode_cooldown.length = sizeof(AVS_QUOTE_MACRO( + CONFIG_ANJAY_ZEPHYR_GPS_NRF_PRIO_MODE_COOLDOWN)), .gps_nrf_prio_mode_cooldown.null_terminated = true, # endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF # if defined(CONFIG_ANJAY_ZEPHYR_PERSISTENCE) \ @@ -429,8 +436,8 @@ void _anjay_zephyr_config_default_init(void) { # endif // CONFIG_ANJAY_ZEPHYR_RUNTIME_CERT_CONFIG # endif // CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING # ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF - app_config.gps_nrf_prio_mode_timeout.value = - app_config.gps_nrf_prio_mode_timeout_storage; + app_config.gps_nrf_prio_mode_permitted.value = + app_config.gps_nrf_prio_mode_permitted_storage; app_config.gps_nrf_prio_mode_cooldown.value = app_config.gps_nrf_prio_mode_cooldown_storage; # endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF @@ -730,9 +737,11 @@ int anjay_zephyr_config_get_private_key(char *buf, size_t buf_capacity) { #endif // CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING #ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF -uint32_t anjay_zephyr_config_get_gps_nrf_prio_mode_timeout(void) { - uint32_t ret = 0; - parse_uint32(app_config.gps_nrf_prio_mode_timeout.value, &ret); +bool anjay_zephyr_config_is_gps_nrf_prio_mode_permitted(void) { + bool ret = false; + SYNCHRONIZED(config_mutex) { + ret = app_config.gps_nrf_prio_mode_permitted.value[0] == 'y'; + } return ret; } @@ -776,7 +785,8 @@ static int string_validate(const struct shell *shell, * !defined(CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING) \ */ -#ifndef CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING +#if defined(CONFIG_ANJAY_ZEPHYR_GPS_NRF) \ + || !defined(CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING) static int flag_validate(const struct shell *shell, const char *value, size_t value_len, @@ -788,7 +798,10 @@ static int flag_validate(const struct shell *shell, return 0; } +#endif // defined(CONFIG_ANJAY_ZEPHYR_GPS_NRF) || + // !defined(CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING) +#ifndef CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING static int psk_hex_validate(const struct shell *shell, const char *value, size_t value_len, diff --git a/src/config.h b/src/config.h index b953c89..40a84f9 100644 --- a/src/config.h +++ b/src/config.h @@ -52,7 +52,8 @@ # endif // CONFIG_ANJAY_ZEPHYR_RUNTIME_CERT_CONFIG #endif // CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING #ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF -# define OPTION_KEY_GPS_NRF_PRIO_MODE_TIMEOUT gps_prio_mode_timeout +# define GPS_NRF_PRIO_MODE_PERMITTED_STORAGE_SIZE 1 +# define OPTION_KEY_GPS_NRF_PRIO_MODE_PERMITTED gps_prio_mode_permitted # define OPTION_KEY_GPS_NRF_PRIO_MODE_COOLDOWN gps_prio_mode_cooldown #endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF #if defined(CONFIG_ANJAY_ZEPHYR_PERSISTENCE) \ @@ -70,12 +71,6 @@ * !defined(CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING) \ */ -#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF -# define GPS_NRF_PRIO_MODE_TIMEOUT "300" - -# define GPS_NRF_PRIO_MODE_COOLDOWN "1800" -#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF - #ifdef WITH_ANJAY_ZEPHYR_CONFIG void _anjay_zephyr_config_init(void); void _anjay_zephyr_config_save(void); diff --git a/src/gps.h b/src/gps.h index 458f213..d7f3667 100644 --- a/src/gps.h +++ b/src/gps.h @@ -53,6 +53,7 @@ int _anjay_zephyr_initialize_gps(void); # ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS uint32_t _anjay_zephyr_gps_fetch_modem_agps_request_mask(void); +void _anjay_zephyr_gps_clear_modem_agps_request_mask(void); # endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS #endif // CONFIG_ANJAY_ZEPHYR_GPS diff --git a/src/gps_impl/gps_nrf.c b/src/gps_impl/gps_nrf.c index 884dcb7..8ae0cfa 100644 --- a/src/gps_impl/gps_nrf.c +++ b/src/gps_impl/gps_nrf.c @@ -39,7 +39,6 @@ LOG_MODULE_REGISTER(anjay_zephyr_gps_nrf); #define INTERRUPTED_FIXES_WARN_THRESHOLD 10 -#define PRIO_MODE_TIMEOUT K_MINUTES(5) // Suggested GPS tuning parameters come from Nordic's SDK example // https://github.com/nrfconnect/sdk-nrf/blob/master/samples/nrf9160/gps/src/main.c @@ -160,10 +159,7 @@ static void incoming_pvt_work_handler(struct k_work *work) { == INTERRUPTED_FIXES_WARN_THRESHOLD) { interrupted_fixes_in_row = 0; - uint32_t gps_prio_mode_timeout = - anjay_zephyr_config_get_gps_nrf_prio_mode_timeout(); - - if (gps_prio_mode_timeout == 0) { + if (!anjay_zephyr_config_is_gps_nrf_prio_mode_permitted()) { return; } @@ -178,8 +174,11 @@ static void incoming_pvt_work_handler(struct k_work *work) { atomic_store(&anjay_zephyr_gps_prio_mode, true); _anjay_zephyr_network_internal_connection_state_changed(); - k_work_schedule(&prio_mode_disable_dwork, - K_SECONDS(gps_prio_mode_timeout)); + // NOTE: As written here + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/2.2.0/nrfxlib/nrf_modem/doc/gnss_interface.html#enabling-gnss-priority-mode + // priority mode is disabled automatically after the first fix or + // after 40 seconds + k_work_schedule(&prio_mode_disable_dwork, K_SECONDS(41)); } } } @@ -190,28 +189,29 @@ handle_modem_agps_request_evt(struct nrf_modem_gnss_agps_data_frame req) { uint32_t request_mask = 0; if (req.data_flags & NRF_MODEM_GNSS_AGPS_GPS_UTC_REQUEST) { - request_mask |= LOC_ASSIST_A_GPS_MASK_UTC; + request_mask |= LOC_SERVICES_A_GPS_MASK_UTC; } if (req.data_flags & NRF_MODEM_GNSS_AGPS_KLOBUCHAR_REQUEST) { - request_mask |= LOC_ASSIST_A_GPS_MASK_KLOBUCHAR; + request_mask |= LOC_SERVICES_A_GPS_MASK_KLOBUCHAR; } if (req.data_flags & NRF_MODEM_GNSS_AGPS_NEQUICK_REQUEST) { - request_mask |= LOC_ASSIST_A_GPS_MASK_NEQUICK; + request_mask |= LOC_SERVICES_A_GPS_MASK_NEQUICK; } if (req.data_flags & NRF_MODEM_GNSS_AGPS_SYS_TIME_AND_SV_TOW_REQUEST) { - request_mask |= LOC_ASSIST_A_GPS_MASK_TOW | LOC_ASSIST_A_GPS_MASK_CLOCK; + request_mask |= + LOC_SERVICES_A_GPS_MASK_TOW | LOC_SERVICES_A_GPS_MASK_CLOCK; } if (req.data_flags & NRF_MODEM_GNSS_AGPS_POSITION_REQUEST) { - request_mask |= LOC_ASSIST_A_GPS_MASK_LOCATION; + request_mask |= LOC_SERVICES_A_GPS_MASK_LOCATION; } if (req.data_flags & NRF_MODEM_GNSS_AGPS_INTEGRITY_REQUEST) { - request_mask |= LOC_ASSIST_A_GPS_MASK_INTEGRITY; + request_mask |= LOC_SERVICES_A_GPS_MASK_INTEGRITY; } if (req.sv_mask_ephe) { - request_mask |= LOC_ASSIST_A_GPS_MASK_EPHEMERIS; + request_mask |= LOC_SERVICES_A_GPS_MASK_EPHEMERIS; } if (req.sv_mask_alm) { - request_mask |= LOC_ASSIST_A_GPS_MASK_ALMANAC; + request_mask |= LOC_SERVICES_A_GPS_MASK_ALMANAC; } SYNCHRONIZED(anjay_zephyr_gps_read_last_mtx) { @@ -268,30 +268,13 @@ static int config_at(void) { } int _anjay_zephyr_initialize_gps(void) { - if (config_at()) { - goto error; - } - - int stop_result = nrf_modem_gnss_stop(); - - if (stop_result) { - // stop failed, which means that GNSS wasn't started already - if (nrf_modem_gnss_event_handler_set(gnss_event_handler) - || nrf_modem_gnss_fix_retry_set(0) - || nrf_modem_gnss_fix_interval_set(1)) { - goto error; - } - } - - if (nrf_modem_gnss_start()) { - goto error; + if (config_at() || nrf_modem_gnss_event_handler_set(gnss_event_handler) + || nrf_modem_gnss_fix_retry_set(0) + || nrf_modem_gnss_fix_interval_set(1) || nrf_modem_gnss_start()) { + LOG_ERR("Failed to initialize GPS interface"); + return -1; } - return 0; - -error: - LOG_ERR("Failed to initialize GPS interface"); - return -1; } #ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS @@ -301,9 +284,14 @@ uint32_t _anjay_zephyr_gps_fetch_modem_agps_request_mask(void) { SYNCHRONIZED(anjay_zephyr_gps_read_last_mtx) { if (modem_agps_request_mask) { result = modem_agps_request_mask; - modem_agps_request_mask = 0; } } return result; } + +void _anjay_zephyr_gps_clear_modem_agps_request_mask(void) { + SYNCHRONIZED(anjay_zephyr_gps_read_last_mtx) { + modem_agps_request_mask = 0; + } +} #endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS diff --git a/src/location_services.c b/src/location_services.c new file mode 100644 index 0000000..5d8e9e5 --- /dev/null +++ b/src/location_services.c @@ -0,0 +1,538 @@ +/* + * Copyright 2020-2023 AVSystem + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include "location_services.h" +#include "lwm2m_internal.h" +#include "objects/objects.h" +#include "utils.h" + +#define SERVER_RESPONSE_TIMEOUT 90 + +LOG_MODULE_REGISTER(anjay_zephyr_location_services); + +static struct k_mutex gf_location_request_mutex; + +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION +static anjay_zephyr_location_services_gf_location_request_cb_t + *g_gf_location_request_cb; +static avs_sched_clb_t g_gf_location_request_exponential_backoff_job; +static anjay_zephyr_location_services_gf_location_request_type_t + g_last_gf_location_request_type; +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION +#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS +static _anjay_zephyr_location_services_agps_request_cb_t *g_agps_request_cb; +static avs_sched_clb_t g_agps_request_exponential_backoff_job; +static uint32_t g_last_request_mask; +#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS + +static struct { + const char *const name; + bool in_progress; + avs_sched_handle_t failed_due_to_no_response_from_server_handle; + bool exponential_backoff; + avs_sched_clb_t *const exponential_backoff_job; +} g_requests[] = { +#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS + [LOCATION_SERVICES_REQUESTS_AGPS_REQUEST] = { + .name = "A-GPS", + .exponential_backoff_job = g_agps_request_exponential_backoff_job, + }, +#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS +#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_P_GPS + [LOCATION_SERVICES_REQUESTS_PGPS_REQUEST] = { + .name = "P-GPS", + .exponential_backoff_job = NULL + }, +#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_P_GPS +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION + [LOCATION_SERVICES_REQUESTS_CELL_REQUEST] = { + .name = "Ground fix location", + .exponential_backoff_job = g_gf_location_request_exponential_backoff_job + } +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION +}; + +static void +process_callback(anjay_zephyr_location_services_request_result_t result, + anjay_zephyr_location_services_ground_fix_location_t *location, + enum anjay_zephyr_location_services_requests req_kind) { + SYNCHRONIZED(gf_location_request_mutex) { + switch (req_kind) { +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION + case LOCATION_SERVICES_REQUESTS_CELL_REQUEST: + if (g_gf_location_request_cb) { + g_gf_location_request_cb( + result, + location + ? *location + : (anjay_zephyr_location_services_ground_fix_location_t) { + .latitude = NAN, + .longitude = NAN, + .accuracy = NAN + }); + } + break; +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION +#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS + case LOCATION_SERVICES_REQUESTS_AGPS_REQUEST: + if (g_agps_request_cb) { + g_agps_request_cb(result); + } + break; +#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS + } + } +} + +static void request_failed_due_to_no_response_from_server(avs_sched_t *sched, + const void *data) { + SYNCHRONIZED(gf_location_request_mutex) { + enum anjay_zephyr_location_services_requests req_kind = + (enum anjay_zephyr_location_services_requests) (uintptr_t) data; + LOG_WRN("No response to %s request received from the server.", + g_requests[req_kind].name); + g_requests[req_kind].in_progress = false; + process_callback(ANJAY_ZEPHYR_LOCATION_SERVICES_NO_RESPONSE, NULL, + req_kind); + } +} + +static void send_finished_handler(anjay_t *anjay, + anjay_ssid_t ssid, + const anjay_send_batch_t *batch, + int result, + void *data) { + SYNCHRONIZED(gf_location_request_mutex) { + enum anjay_zephyr_location_services_requests req_kind = + (enum anjay_zephyr_location_services_requests) (uintptr_t) data; + + if (result != ANJAY_SEND_SUCCESS) { + LOG_WRN("Failed to send %s request to SSID=%" PRIu16, + g_requests[req_kind].name, ssid); + g_requests[req_kind].in_progress = false; + process_callback(ANJAY_ZEPHYR_LOCATION_SERVICES_UNABLE_TO_SEND, + NULL, req_kind); + } else { + LOG_INF("Sent the %s request to SSID=%" PRIu16, + g_requests[req_kind].name, ssid); +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION + if (req_kind == LOCATION_SERVICES_REQUESTS_CELL_REQUEST + && (g_last_gf_location_request_type + == ANJAY_ZEPHYR_LOC_SERVICES_GF_LOCATION_REQUEST_INFORM_SINGLE + || g_last_gf_location_request_type + == ANJAY_ZEPHYR_LOC_SERVICES_GF_LOCATION_REQUEST_INFORM_MULTI)) { + g_requests[req_kind].in_progress = false; + process_callback(ANJAY_ZEPHYR_LOCATION_SERVICES_SUCCESSFUL, + NULL, req_kind); + } else +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION + { + AVS_SCHED_DELAYED( + anjay_get_scheduler(anjay), + &g_requests[req_kind] + .failed_due_to_no_response_from_server_handle, + avs_time_duration_from_scalar(SERVER_RESPONSE_TIMEOUT, + AVS_TIME_S), + request_failed_due_to_no_response_from_server, + &req_kind, sizeof(req_kind)); + } + } + } +} + +static int +batch_compile_and_send(anjay_t *anjay, + anjay_send_batch_builder_t **builder_ptr, + enum anjay_zephyr_location_services_requests req_kind) { + int result = -1; + anjay_send_batch_t *batch = anjay_send_batch_builder_compile(builder_ptr); + + if (!batch) { + LOG_ERR("Batch compilation failed"); + return result; + } + + anjay_send_result_t send_result = anjay_send_deferrable( + anjay, CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_SERVER_SSID, batch, + send_finished_handler, (void *) (uintptr_t) req_kind); + + if (send_result) { + LOG_ERR("Couldn't send the %s request to SSID=%" PRIu16 ", err: %d", + g_requests[req_kind].name, + CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_SERVER_SSID, + (int) send_result); + } else { + result = 0; + } + + anjay_send_batch_release(&batch); + return result; +} + +static void received_req_response_from_server( + anjay_t *anjay, + bool successful, + enum anjay_zephyr_location_services_requests req_kind, + anjay_zephyr_location_services_ground_fix_location_t *location, + int32_t result_code, + uint32_t backoff_value) { + SYNCHRONIZED(gf_location_request_mutex) { + avs_sched_del(&g_requests[req_kind] + .failed_due_to_no_response_from_server_handle); + g_requests[req_kind].in_progress = false; + + if (!successful) { + process_callback(ANJAY_ZEPHYR_LOCATION_SERVICES_IMPROPER_RESPONSE, + NULL, req_kind); + } else if (result_code == 0) { + process_callback(ANJAY_ZEPHYR_LOCATION_SERVICES_SUCCESSFUL, + location, req_kind); + } else if (result_code > 0) { + if (g_requests[req_kind].exponential_backoff) { + LOG_WRN("Due to temporary failure request %s data again with " + "exponential backoff %" PRIu32 "s", + g_requests[req_kind].name, backoff_value); + g_requests[req_kind].in_progress = true; + + AVS_SCHED_DELAYED(anjay_get_scheduler(anjay), NULL, + avs_time_duration_from_scalar(backoff_value, + AVS_TIME_S), + g_requests[req_kind].exponential_backoff_job, + &anjay, sizeof(anjay)); + } else { + process_callback( + ANJAY_ZEPHYR_LOCATION_SERVICES_TEMPORARY_FAILURE, NULL, + req_kind); + } + } else { + process_callback(ANJAY_ZEPHYR_LOCATION_SERVICES_PERMANENT_FAILURE, + NULL, req_kind); + } + } +} + +uint32_t +_anjay_zephyr_location_services_calculate_backoff(uint8_t backoff_number) { + uint32_t exponential_backoff = + LOCATION_SERVICES_MAXIMUM_EXPONENTIAL_BACKOFF; + if (backoff_number < sizeof(long unsigned) * 8 + && UINT32_MAX / LOCATION_SERVICES_EXPONENTIAL_BACKOFF_INTERVAL + >= (1LU << backoff_number)) { + exponential_backoff = LOCATION_SERVICES_EXPONENTIAL_BACKOFF_INTERVAL + * (1LU << backoff_number); + exponential_backoff = + exponential_backoff + > LOCATION_SERVICES_MAXIMUM_EXPONENTIAL_BACKOFF + ? LOCATION_SERVICES_MAXIMUM_EXPONENTIAL_BACKOFF + : exponential_backoff; + } + return exponential_backoff; +} + +void _anjay_zephyr_location_services_init(void) { + k_mutex_init(&gf_location_request_mutex); +} + +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION +static int send_gf_location_request( + anjay_t *anjay, + anjay_zephyr_location_services_gf_location_request_type_t + request_type) { + if (!anjay) { + return -1; + } + + if (_anjay_zephyr_ground_fix_location_get_result_code( + anjay_zephyr_ground_fix_location_obj) + < 0) { + LOG_WRN("Permanent failure result code received, device will not retry " + "ground fix location request until reboot"); + return -1; + } + + anjay_send_batch_builder_t *builder = anjay_send_batch_builder_new(); + + if (!builder) { + LOG_ERR("Failed to allocate batch builder"); + return -1; + } + + int result = _anjay_zephyr_conn_mon_object_add_to_batch( + anjay, builder, anjay_zephyr_conn_mon_obj); + if (result) { + goto finalize_batch; + } + bool request = + request_type + == ANJAY_ZEPHYR_LOC_SERVICES_GF_LOCATION_REQUEST_REQUEST_SINGLE + || request_type + == ANJAY_ZEPHYR_LOC_SERVICES_GF_LOCATION_REQUEST_REQUEST_MULTI; + if ((result = anjay_send_batch_add_bool( + builder, OID_GROUND_FIX_LOC, 0, + RID_GROUND_FIX_LOC_SEND_LOCATION_BACK, UINT16_MAX, + avs_time_real_now(), request))) { + LOG_ERR("Failed to add location back value to bach, err: %d", result); + goto finalize_batch; + } + + if ((request_type + == ANJAY_ZEPHYR_LOC_SERVICES_GF_LOCATION_REQUEST_INFORM_MULTI + || request_type + == ANJAY_ZEPHYR_LOC_SERVICES_GF_LOCATION_REQUEST_REQUEST_MULTI) + && (result = _anjay_zephyr_ecid_object_add_to_batch( + anjay, builder, anjay_zephyr_ecid_obj))) { + goto finalize_batch; + } + + result = batch_compile_and_send(anjay, &builder, + LOCATION_SERVICES_REQUESTS_CELL_REQUEST); + +finalize_batch: + anjay_send_batch_builder_cleanup(&builder); + return result; +} + +int anjay_zephyr_location_services_gf_location_request( + anjay_t *anjay, + anjay_zephyr_location_services_gf_location_request_cb_t *cb, + anjay_zephyr_location_services_gf_location_request_type_t request_type, + bool exponential_backoff) { + int result = -1; + + SYNCHRONIZED(gf_location_request_mutex) { + if (g_requests[LOCATION_SERVICES_REQUESTS_CELL_REQUEST].in_progress) { + LOG_WRN("Ground fix location request already in progress"); + } else if (!send_gf_location_request(anjay, request_type)) { + g_requests[LOCATION_SERVICES_REQUESTS_CELL_REQUEST].in_progress = + true; + g_requests[LOCATION_SERVICES_REQUESTS_CELL_REQUEST] + .exponential_backoff = exponential_backoff; + g_last_gf_location_request_type = request_type; + g_gf_location_request_cb = cb; + result = 0; + } + } + return result; +} + +static void +g_gf_location_request_exponential_backoff_job(avs_sched_t *sched, + const void *anjay_ptr) { + SYNCHRONIZED(gf_location_request_mutex) { + if (send_gf_location_request(*(anjay_t *const *) anjay_ptr, + g_last_gf_location_request_type)) { + g_requests[LOCATION_SERVICES_REQUESTS_CELL_REQUEST].in_progress = + false; + process_callback(ANJAY_ZEPHYR_LOCATION_SERVICES_UNABLE_TO_SEND, + NULL, LOCATION_SERVICES_REQUESTS_CELL_REQUEST); + } + } +} + +void _anjay_zephyr_gf_location_request_job( + avs_sched_t *sched, const void *gf_location_request_job_args_ptr) { + LOG_INF("Manual request Ground fix location"); + struct anjay_zephyr_gf_location_request_job_args args = + *(const struct anjay_zephyr_gf_location_request_job_args *) + gf_location_request_job_args_ptr; + + anjay_zephyr_location_services_gf_location_request( + args.anjay, args.cb, args.request_type, false); +} + +void _anjay_zephyr_location_services_received_gf_location_req_response_from_server( + anjay_t *anjay, + bool successful, + anjay_zephyr_location_services_ground_fix_location_t *location) { + received_req_response_from_server( + anjay, successful, LOCATION_SERVICES_REQUESTS_CELL_REQUEST, + location, + _anjay_zephyr_ground_fix_location_get_result_code( + anjay_zephyr_ground_fix_location_obj), + _anjay_zephyr_ground_fix_location_get_exponential_backoff_value( + anjay_zephyr_ground_fix_location_obj)); +} + +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION + +#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS +static int send_agps_request(anjay_t *anjay, uint32_t request_mask) { + if (!anjay) { + return -1; + } + + if (_anjay_zephyr_gnss_assistance_get_result_code( + anjay_zephyr_gnss_assistance_obj) + < 0) { + LOG_WRN("Permanent failure result code received, device will not retry " + "A-GPS request until reboot"); + return -1; + } + + static const struct { + uint32_t req_flag; + const char *name; + } agps_flag_names[] = { + { + .req_flag = LOC_SERVICES_A_GPS_MASK_UTC, + .name = "UTC parameters" + }, + { + .req_flag = LOC_SERVICES_A_GPS_MASK_KLOBUCHAR, + .name = "Klobuchar ionospheric correction parameters" + }, + { + .req_flag = LOC_SERVICES_A_GPS_MASK_NEQUICK, + .name = "NeQuick ionospheric correction parameters" + }, + { + .req_flag = LOC_SERVICES_A_GPS_MASK_TOW, + .name = "SV time of week" + }, + { + .req_flag = LOC_SERVICES_A_GPS_MASK_CLOCK, + .name = "GPS system time" + }, + { + .req_flag = LOC_SERVICES_A_GPS_MASK_LOCATION, + .name = "Position assistance parameters" + }, + { + .req_flag = LOC_SERVICES_A_GPS_MASK_INTEGRITY, + .name = "Integrity assistance parameters" + }, + { + .req_flag = LOC_SERVICES_A_GPS_MASK_EPHEMERIS, + .name = "GPS ephemeris" + }, + { + .req_flag = LOC_SERVICES_A_GPS_MASK_ALMANAC, + .name = "GPS almanac" + } + }; + + LOG_INF("Requesting following types of A-GPS data:"); + for (size_t i = 0; i < AVS_ARRAY_SIZE(agps_flag_names); i++) { + if (agps_flag_names[i].req_flag & request_mask) { + LOG_INF("%s", agps_flag_names[i].name); + } + } + + anjay_send_batch_builder_t *builder = anjay_send_batch_builder_new(); + + if (!builder) { + LOG_ERR("Failed to allocate batch builder"); + return -1; + } + + int result = _anjay_zephyr_conn_mon_object_add_to_batch( + anjay, builder, anjay_zephyr_conn_mon_obj); + if (result) { + goto finalize_batch; + } + + avs_time_real_t current_timestamp = avs_time_real_now(); + // FIXME: current spec uses int for this resource, but uint is more + // appropriate + if ((result = anjay_send_batch_add_int( + builder, OID_GNSS_ASSISTANCE, 0, + RID_GNSS_ASSISTANCE_A_GPS_ASSISTANCE_MASK, UINT16_MAX, + current_timestamp, request_mask))) { + LOG_ERR("Failed to add assistance mask to batch, err: %d", result); + goto finalize_batch; + } + + if ((result = anjay_send_batch_add_int( + builder, OID_GNSS_ASSISTANCE, 0, + RID_GNSS_ASSISTANCE_SATELLITE_ELEVATION_MASK, UINT16_MAX, + current_timestamp, + CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS_SATELLITE_ELEVATION_MASK))) { // todo + LOG_ERR("Failed to add satellite elevation mask to batch, err: %d", + result); + goto finalize_batch; + } + + if ((result = anjay_send_batch_add_int( + builder, OID_GNSS_ASSISTANCE, 0, + RID_GNSS_ASSISTANCE_ASSISTANCE_TYPE, UINT16_MAX, + current_timestamp, LOCATION_SERVICES_REQUESTS_AGPS_REQUEST))) { + LOG_ERR("Failed to add assistance type to batch, err: %d", result); + goto finalize_batch; + } + + result = batch_compile_and_send(anjay, &builder, + LOCATION_SERVICES_REQUESTS_AGPS_REQUEST); + +finalize_batch: + anjay_send_batch_builder_cleanup(&builder); + return result; +} + +int _anjay_zephyr_send_agps_request( + anjay_t *anjay, + _anjay_zephyr_location_services_agps_request_cb_t *cb, + uint32_t request_mask, + bool exponential_backoff) { + int result = -1; + + if (g_requests[LOCATION_SERVICES_REQUESTS_AGPS_REQUEST].in_progress) { + LOG_WRN("A-GPS request already in progress"); + } else if (!send_agps_request(anjay, request_mask)) { + g_requests[LOCATION_SERVICES_REQUESTS_AGPS_REQUEST].in_progress = true; + g_requests[LOCATION_SERVICES_REQUESTS_AGPS_REQUEST] + .exponential_backoff = exponential_backoff; + g_last_request_mask = request_mask; + g_agps_request_cb = cb; + result = 0; + } + return result; +} + +static void g_agps_request_exponential_backoff_job(avs_sched_t *sched, + const void *anjay_ptr) { + if (send_agps_request(*(anjay_t *const *) anjay_ptr, g_last_request_mask)) { + g_requests[LOCATION_SERVICES_REQUESTS_AGPS_REQUEST].in_progress = false; + process_callback(ANJAY_ZEPHYR_LOCATION_SERVICES_UNABLE_TO_SEND, NULL, + LOCATION_SERVICES_REQUESTS_AGPS_REQUEST); + } +} + +void _anjay_zephyr_agps_request_job(avs_sched_t *sched, + const void *agps_job_args_ptr) { + LOG_INF("Manual request of A-GPS data"); + struct anjay_zephyr_agps_request_job_args args = + *(const struct anjay_zephyr_agps_request_job_args *) + agps_job_args_ptr; + + _anjay_zephyr_send_agps_request(args.anjay, args.cb, args.request_mask, + args.exponential_backoff); +} + +void _anjay_zephyr_location_services_received_agps_req_response_from_server( + anjay_t *anjay, bool successful) { + received_req_response_from_server( + anjay, successful, LOCATION_SERVICES_REQUESTS_AGPS_REQUEST, NULL, + _anjay_zephyr_gnss_assistance_get_result_code( + anjay_zephyr_gnss_assistance_obj), + _anjay_zephyr_gnss_assistance_get_exponential_backoff_value( + anjay_zephyr_gnss_assistance_obj)); +} +#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS diff --git a/src/location_services.h b/src/location_services.h new file mode 100644 index 0000000..4d1f290 --- /dev/null +++ b/src/location_services.h @@ -0,0 +1,78 @@ +/* + * Copyright 2020-2023 AVSystem + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES + +# include "anjay_zephyr/location_services.h" +# include "objects/objects.h" +# include + +# define LOCATION_SERVICES_MAXIMUM_EXPONENTIAL_BACKOFF 86400 +# define LOCATION_SERVICES_EXPONENTIAL_BACKOFF_INTERVAL 20 + +enum anjay_zephyr_location_services_requests { +# ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS + LOCATION_SERVICES_REQUESTS_AGPS_REQUEST, +# endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS +# ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_P_GPS + LOCATION_SERVICES_REQUESTS_PGPS_REQUEST, +# endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_P_GPS +# ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION + LOCATION_SERVICES_REQUESTS_CELL_REQUEST +# endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION +}; + +void _anjay_zephyr_location_services_init(void); + +uint32_t +_anjay_zephyr_location_services_calculate_backoff(uint8_t backoff_number); + +# ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION +struct anjay_zephyr_gf_location_request_job_args { + anjay_t *anjay; + anjay_zephyr_location_services_gf_location_request_cb_t *cb; + anjay_zephyr_location_services_gf_location_request_type_t request_type; +}; +avs_sched_clb_t _anjay_zephyr_gf_location_request_job; + +void _anjay_zephyr_location_services_received_gf_location_req_response_from_server( + anjay_t *anjay, + bool successful, + anjay_zephyr_location_services_ground_fix_location_t *location); +# endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION + +# ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS +typedef void _anjay_zephyr_location_services_agps_request_cb_t( + anjay_zephyr_location_services_request_result_t result); +struct anjay_zephyr_agps_request_job_args { + anjay_t *anjay; + _anjay_zephyr_location_services_agps_request_cb_t *cb; + uint32_t request_mask; + bool exponential_backoff; +}; +avs_sched_clb_t _anjay_zephyr_agps_request_job; + +int _anjay_zephyr_send_agps_request( + anjay_t *anjay, + _anjay_zephyr_location_services_agps_request_cb_t *cb, + uint32_t request_mask, + bool exponential_backoff); +void _anjay_zephyr_location_services_received_agps_req_response_from_server( + anjay_t *anjay, bool successful); +# endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES diff --git a/src/lwm2m.c b/src/lwm2m.c index 4c1dc59..88dfe4d 100644 --- a/src/lwm2m.c +++ b/src/lwm2m.c @@ -37,12 +37,12 @@ #include "config.h" #include "firmware_update.h" #include "gps.h" +#include "location_services.h" #include "lwm2m_internal.h" -#include "persistence.h" -#include "utils.h" - #include "network/network.h" #include "objects/objects.h" +#include "persistence.h" +#include "utils.h" #ifdef CONFIG_DATE_TIME # include @@ -52,16 +52,24 @@ # include "nrf_lc_info.h" #endif // CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO +#ifdef CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160 +# include "afu/nrf9160/afu_nrf9160.h" +#endif // CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160 + static const anjay_dm_object_def_t **device_obj; #ifdef CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO const anjay_dm_object_def_t **anjay_zephyr_ecid_obj; -static const anjay_dm_object_def_t **conn_mon_obj; +const anjay_dm_object_def_t **anjay_zephyr_conn_mon_obj; #endif // CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO -#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES -const anjay_dm_object_def_t **anjay_zephyr_loc_assist_obj; -#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION +const anjay_dm_object_def_t **anjay_zephyr_ground_fix_location_obj; +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION + +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_ASSISTANCE +const anjay_dm_object_def_t **anjay_zephyr_gnss_assistance_obj; +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_ASSISTANCE #define RETRY_SYNC_CLOCK_DELAY_TIME_S 1 @@ -180,13 +188,18 @@ static void deinitialize_anjay(anjay_t *anjay) { _anjay_zephyr_three_axis_sensors_remove(); #ifdef CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO - _anjay_zephyr_conn_mon_object_release(&conn_mon_obj); + _anjay_zephyr_conn_mon_object_release(&anjay_zephyr_conn_mon_obj); _anjay_zephyr_ecid_object_release(&anjay_zephyr_ecid_obj); #endif // CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO -#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES - _anjay_zephyr_loc_assist_object_release(&anjay_zephyr_loc_assist_obj); -#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION + _anjay_zephyr_ground_fix_location_object_release( + &anjay_zephyr_ground_fix_location_obj); +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_ASSISTANCE + _anjay_zephyr_gnss_assistance_object_release( + &anjay_zephyr_gnss_assistance_obj); +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_ASSISTANCE _anjay_zephyr_device_object_release(&device_obj); @@ -458,6 +471,13 @@ static anjay_t *initialize_anjay(void) { } #endif // CONFIG_ANJAY_ZEPHYR_FOTA +#ifdef CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160 + if (_anjay_zephyr_afu_nrf9160_install(anjay)) { + LOG_ERR("Failed to initialize advanced fw update module"); + goto error; + } +#endif // CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160 + device_obj = _anjay_zephyr_device_object_create(); if (!device_obj || anjay_register_object(anjay, device_obj)) { LOG_ERR("Failed to register Device object"); @@ -469,9 +489,10 @@ static anjay_t *initialize_anjay(void) { _anjay_zephyr_nrf_lc_info_get(&nrf_lc_info); - conn_mon_obj = _anjay_zephyr_conn_mon_object_create(&nrf_lc_info); - if (conn_mon_obj) { - anjay_register_object(anjay, conn_mon_obj); + anjay_zephyr_conn_mon_obj = + _anjay_zephyr_conn_mon_object_create(&nrf_lc_info); + if (anjay_zephyr_conn_mon_obj) { + anjay_register_object(anjay, anjay_zephyr_conn_mon_obj); } anjay_zephyr_ecid_obj = _anjay_zephyr_ecid_object_create(&nrf_lc_info); @@ -482,11 +503,24 @@ static anjay_t *initialize_anjay(void) { #endif // CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO #ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES - anjay_zephyr_loc_assist_obj = _anjay_zephyr_loc_assist_object_create(); - if (anjay_zephyr_loc_assist_obj) { - anjay_register_object(anjay, anjay_zephyr_loc_assist_obj); - } + _anjay_zephyr_location_services_init(); #endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES + +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION + anjay_zephyr_ground_fix_location_obj = + _anjay_zephyr_ground_fix_location_object_create(); + if (anjay_zephyr_ground_fix_location_obj) { + anjay_register_object(anjay, anjay_zephyr_ground_fix_location_obj); + } +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_ASSISTANCE + anjay_zephyr_gnss_assistance_obj = + _anjay_zephyr_gnss_assistance_object_create(); + if (anjay_zephyr_gnss_assistance_obj) { + anjay_register_object(anjay, anjay_zephyr_gnss_assistance_obj); + } +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_ASSISTANCE + if (execute_user_callback(anjay, ANJAY_ZEPHYR_LWM2M_CALLBACK_REASON_INIT)) { goto error; } @@ -496,7 +530,6 @@ static anjay_t *initialize_anjay(void) { return anjay; } #endif // CONFIG_ANJAY_ZEPHYR_PERSISTENCE - #ifdef CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING if (!_anjay_zephyr_restore_anjay_from_factory_provisioning(anjay)) { return anjay; @@ -569,12 +602,53 @@ void _anjay_zephyr_sched_update_anjay_network_bearer(void) { static void update_objects_nrf_lc_info(anjay_t *anjay) { struct anjay_zephyr_nrf_lc_info nrf_lc_info; if (_anjay_zephyr_nrf_lc_info_get_if_changed(&nrf_lc_info)) { - _anjay_zephyr_conn_mon_object_update(anjay, conn_mon_obj, &nrf_lc_info); + _anjay_zephyr_conn_mon_object_update(anjay, anjay_zephyr_conn_mon_obj, + &nrf_lc_info); _anjay_zephyr_ecid_object_update(anjay, anjay_zephyr_ecid_obj, &nrf_lc_info); } } #endif // CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO +#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS +static bool agps_requested; + +static void +agps_request_cb(anjay_zephyr_location_services_request_result_t result) { + static uint32_t exponential_backoff; + static uint32_t request_result_failed_in_row; + if (result == ANJAY_ZEPHYR_LOCATION_SERVICES_SUCCESSFUL) { + _anjay_zephyr_gps_clear_modem_agps_request_mask(); + exponential_backoff = 0; + request_result_failed_in_row = 0; + agps_requested = false; + } else if (result != ANJAY_ZEPHYR_LOCATION_SERVICES_PERMANENT_FAILURE + && _anjay_zephyr_gps_fetch_modem_agps_request_mask()) { + exponential_backoff = _anjay_zephyr_location_services_calculate_backoff( + request_result_failed_in_row++); + + LOG_WRN("A-GPS request failed, trying again with exponential backoff " + "%" PRIu32 "s", + exponential_backoff); + + SYNCHRONIZED(anjay_zephyr_global_anjay_mutex) { + struct anjay_zephyr_agps_request_job_args args = { + .anjay = anjay_zephyr_global_anjay, + .cb = agps_request_cb, + .request_mask = + _anjay_zephyr_gps_fetch_modem_agps_request_mask(), + .exponential_backoff = true + }; + + AVS_SCHED_DELAYED(anjay_get_scheduler(args.anjay), NULL, + avs_time_duration_from_scalar(exponential_backoff, + AVS_TIME_S), + _anjay_zephyr_agps_request_job, &args, + sizeof(args)); + } + } +} + +#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS static void update_internal_objects_and_persistence(avs_sched_t *sched, const void *anjay_ptr) { @@ -589,10 +663,11 @@ static void update_internal_objects_and_persistence(avs_sched_t *sched, #ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS uint32_t request_mask = _anjay_zephyr_gps_fetch_modem_agps_request_mask(); - if (request_mask) { + if (request_mask && !agps_requested + && !_anjay_zephyr_send_agps_request(anjay, agps_request_cb, + request_mask, true)) { LOG_INF("Modem requests A-GPS data"); - _anjay_zephyr_loc_assist_object_send_agps_request( - anjay, anjay_zephyr_loc_assist_obj, request_mask); + agps_requested = true; } #endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS @@ -686,6 +761,12 @@ static void run_anjay(void *arg1, void *arg2, void *arg3) { } #endif // CONFIG_ANJAY_ZEPHYR_FOTA +#ifdef CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160 + if (_anjay_zephyr_afu_nrf9160_requested()) { + _anjay_zephyr_afu_nrf9160_reboot(); + } +#endif // CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160 + disconnect: _anjay_zephyr_network_disconnect(); } @@ -723,6 +804,11 @@ static int anjay_zephyr_lwm2m_init(void) { _anjay_zephyr_fw_update_apply(); #endif // CONFIG_ANJAY_ZEPHYR_FOTA +#ifdef CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160 + _anjay_zephyr_afu_nrf9160_application_apply(); + _anjay_zephyr_afu_nrf9160_modem_apply(); +#endif // CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160 + #ifdef CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO if (_anjay_zephyr_initialize_nrf_lc_info_listener()) { LOG_ERR("Can't initialize Link Control info listener"); @@ -771,17 +857,18 @@ int anjay_zephyr_lwm2m_start(void) { LOG_INF("Starting Anjay"); atomic_store(&anjay_zephyr_anjay_running, true); - SYNCHRONIZED(anjay_thread_running_mutex) { - if (!k_thread_create(&anjay_thread, anjay_stack, - CONFIG_ANJAY_ZEPHYR_THREAD_STACK_SIZE, - run_anjay, NULL, NULL, NULL, - CONFIG_ANJAY_ZEPHYR_THREAD_PRIORITY, 0, - K_NO_WAIT)) { + k_tid_t tid; + if (!(tid = k_thread_create(&anjay_thread, anjay_stack, + CONFIG_ANJAY_ZEPHYR_THREAD_STACK_SIZE, + run_anjay, NULL, NULL, NULL, + CONFIG_ANJAY_ZEPHYR_THREAD_PRIORITY, 0, + K_NO_WAIT))) { LOG_ERR("Failed to create Anjay thread"); atomic_store(&anjay_zephyr_anjay_running, false); return -1; } + k_thread_name_set(tid, "Anjay Zephyr"); atomic_store(&anjay_thread_running, true); } } else { diff --git a/src/lwm2m_internal.h b/src/lwm2m_internal.h index e6276ca..638f939 100644 --- a/src/lwm2m_internal.h +++ b/src/lwm2m_internal.h @@ -26,12 +26,17 @@ extern anjay_t *volatile anjay_zephyr_global_anjay; extern struct k_mutex anjay_zephyr_global_anjay_mutex; extern volatile atomic_bool anjay_zephyr_anjay_running; -#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES -extern const anjay_dm_object_def_t **anjay_zephyr_loc_assist_obj; -#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES - #ifdef CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO extern const anjay_dm_object_def_t **anjay_zephyr_ecid_obj; +extern const anjay_dm_object_def_t **anjay_zephyr_conn_mon_obj; #endif // CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION +extern const anjay_dm_object_def_t **anjay_zephyr_ground_fix_location_obj; +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION + +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_ASSISTANCE +extern const anjay_dm_object_def_t **anjay_zephyr_gnss_assistance_obj; +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_ASSISTANCE + void _anjay_zephyr_sched_update_anjay_network_bearer(void); diff --git a/src/network/network_nrf700x.c b/src/network/network_nrf700x.c new file mode 100644 index 0000000..69e5588 --- /dev/null +++ b/src/network/network_nrf700x.c @@ -0,0 +1,213 @@ +/* + * Copyright 2020-2023 AVSystem + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include +#include +#include +#include + +#include "../config.h" +#include "../utils.h" +#include "network.h" +#include "network_internal.h" + +#define WIFI_MGMT_EVENTS \ + (NET_EVENT_WIFI_CONNECT_RESULT | NET_EVENT_WIFI_DISCONNECT_RESULT) + +LOG_MODULE_REGISTER(anjay_zephyr_network_nrf700x); + +static struct net_mgmt_event_callback wifi_sta_mgmt_cb; +static struct net_mgmt_event_callback net_addr_mgmt_cb; + +static volatile atomic_bool connected; +static volatile atomic_bool disconnect_requested; + +static void print_dhcp_ip(const struct net_if_dhcpv4 *info) { + /* Get DHCP info from struct net_if_dhcpv4 and print */ + const struct in_addr *addr = &info->requested_ip; + char dhcp_info[128] = { 0 }; + + if (net_addr_ntop(AF_INET, addr, dhcp_info, sizeof(dhcp_info))) { + LOG_DBG("IP address: %s", dhcp_info); + } else { + LOG_ERR("net_addr_ntop failed"); + } +} + +static void net_mgmt_event_handler(struct net_mgmt_event_callback *cb, + uint32_t mgmt_event, + struct net_if *iface) { + switch (mgmt_event) { + case NET_EVENT_IPV4_DHCP_BOUND: + print_dhcp_ip((const struct net_if_dhcpv4 *) cb->info); + atomic_store(&connected, true); + break; + default: + break; + } +} + +static void cmd_wifi_status(void) { + struct net_if *iface = net_if_get_default(); + struct wifi_iface_status status = { 0 }; + + if (net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, iface, &status, + sizeof(struct wifi_iface_status))) { + LOG_ERR("Wi-Fi status request failed"); + } else { + LOG_DBG("State: %s", wifi_state_txt(status.state)); + if (status.state >= WIFI_STATE_ASSOCIATED) { + LOG_DBG("Interface Mode: %s", wifi_mode_txt(status.iface_mode)); + LOG_DBG("Link Mode: %s", wifi_link_mode_txt(status.link_mode)); + LOG_DBG("SSID: %-32s", status.ssid); + LOG_DBG("Band: %s", wifi_band_txt(status.band)); + LOG_DBG("Channel: %d", status.channel); + LOG_DBG("Security: %s", wifi_security_txt(status.security)); + LOG_DBG("MFP: %s", wifi_mfp_txt(status.mfp)); + LOG_DBG("RSSI: %d", status.rssi); + } + } +} + +static void handle_wifi_connect_result(struct net_mgmt_event_callback *cb) { + const struct wifi_status *status = (const struct wifi_status *) cb->info; + + if (status->status) { + LOG_ERR("Connection request failed (%d)", status->status); + } else { + LOG_DBG("Connected"); + } + + cmd_wifi_status(); +} + +static void handle_wifi_disconnect_result(struct net_mgmt_event_callback *cb) { + const struct wifi_status *status = (const struct wifi_status *) cb->info; + + if (atomic_load(&disconnect_requested)) { + LOG_DBG("Disconnection request %s (%d)", + status->status ? "failed" : "done", + status->status); + atomic_store(&disconnect_requested, false); + } else { + LOG_DBG("Disconnected"); + atomic_store(&connected, false); + } + + cmd_wifi_status(); +} + +static void wifi_mgmt_event_handler(struct net_mgmt_event_callback *cb, + uint32_t mgmt_event, + struct net_if *iface) { + switch (mgmt_event) { + case NET_EVENT_WIFI_CONNECT_RESULT: + handle_wifi_connect_result(cb); + break; + case NET_EVENT_WIFI_DISCONNECT_RESULT: + handle_wifi_disconnect_result(cb); + break; + default: + break; + } + _anjay_zephyr_network_internal_connection_state_changed(); +} + +int _anjay_zephyr_network_internal_platform_initialize(void) { + atomic_store(&connected, false); + atomic_store(&disconnect_requested, false); + + net_mgmt_init_event_callback(&wifi_sta_mgmt_cb, wifi_mgmt_event_handler, + WIFI_MGMT_EVENTS); + + net_mgmt_add_event_callback(&wifi_sta_mgmt_cb); + + net_mgmt_init_event_callback(&net_addr_mgmt_cb, + net_mgmt_event_handler, + NET_EVENT_IPV4_DHCP_BOUND); + + net_mgmt_add_event_callback(&net_addr_mgmt_cb); + + /* HACK: Add temporary fix to prevent using Wi-Fi before WPA supplicant is + * ready. */ + k_sleep(K_SECONDS(1)); + + return 0; +} + +int _anjay_zephyr_network_connect_async(void) { + char ssid_storage[SSID_STORAGE_SIZE]; + char password_storage[PASSWORD_STORAGE_SIZE]; + struct wifi_connect_req_params wifi_params = { 0 }; + + if (anjay_zephyr_config_get_wifi_ssid(ssid_storage, sizeof(ssid_storage)) + || anjay_zephyr_config_get_wifi_password( + password_storage, sizeof(password_storage))) { + LOG_ERR("Failed to get Wi-Fi configuration from settings"); + return -1; + } + + wifi_params.ssid = ssid_storage; + wifi_params.ssid_length = strlen(ssid_storage); + wifi_params.psk = password_storage; + wifi_params.psk_length = strlen(password_storage); + if (wifi_params.psk_length) { + wifi_params.security = WIFI_SECURITY_TYPE_PSK; + } else { + wifi_params.security = WIFI_SECURITY_TYPE_NONE; + } + wifi_params.channel = WIFI_CHANNEL_ANY; + wifi_params.timeout = SYS_FOREVER_MS; + wifi_params.mfp = WIFI_MFP_OPTIONAL; + + int ret = net_mgmt(NET_REQUEST_WIFI_CONNECT, net_if_get_default(), + &wifi_params, sizeof(struct wifi_connect_req_params)); + + if (ret > 0 || ret == -EALREADY || ret == -EINPROGRESS) { + ret = 0; + } + if (ret) { + LOG_ERR("Failed to configure Wi-Fi"); + } + + return ret; +} + +enum anjay_zephyr_network_bearer_t _anjay_zephyr_network_current_bearer(void) { + return atomic_load(&connected) ? ANJAY_ZEPHYR_NETWORK_BEARER_WIFI + : ANJAY_ZEPHYR_NETWORK_BEARER_LIMIT; +} + +void _anjay_zephyr_network_disconnect(void) { + int status; + + atomic_store(&disconnect_requested, true); + status = net_mgmt(NET_REQUEST_WIFI_DISCONNECT, net_if_get_default(), NULL, + 0); + + if (status) { + atomic_store(&disconnect_requested, false); + + if (status == -EALREADY) { + LOG_DBG("Already disconnected"); + } else { + LOG_ERR("Disconnect request failed"); + } + } else { + LOG_DBG("Disconnect requested"); + } +} diff --git a/src/network/network_nrf91.c b/src/network/network_nrf91.c index 4f58b82..72c67b4 100644 --- a/src/network/network_nrf91.c +++ b/src/network/network_nrf91.c @@ -18,6 +18,11 @@ #include +#if defined(CONFIG_LTE_LINK_CONTROL) && !defined(CONFIG_NRF_MODEM_LIB_SYS_INIT) +# include +#endif // defined(CONFIG_LTE_LINK_CONTROL) && + // !defined(CONFIG_NRF_MODEM_LIB_SYS_INIT) + #include "network.h" #include "network_internal.h" @@ -41,13 +46,34 @@ static void lte_evt_handler(const struct lte_lc_evt *const evt) { } int _anjay_zephyr_network_internal_platform_initialize(void) { - int ret = lte_lc_init(); + int ret; - if (!ret) { - lte_lc_register_handler(lte_evt_handler); +#if defined(CONFIG_LTE_LINK_CONTROL) && !defined(CONFIG_NRF_MODEM_LIB_SYS_INIT) + ret = nrf_modem_lib_init(NORMAL_MODE); + if (ret) { +# ifdef CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160 + // nrf_modem_init (called indirectly) returns a positive code in case + // there was a modem DFU attempt (see + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/2.3.0/nrfxlib/nrf_modem/doc/delta_dfu.html#checking-the-result-of-the-update), + // those codes will be handled appropriately in + // _anjay_zephyr_afu_nrf9160_modem_apply(), so immediately return a 0 + // instead + return ret < 0 ? ret : 0; +# else // CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160 + return ret; +# endif // CONFIG_ANJAY_ZEPHYR_ADVANCED_FOTA_NRF9160 } +#endif // defined(CONFIG_LTE_LINK_CONTROL) && + // !defined(CONFIG_NRF_MODEM_LIB_SYS_INIT) - return ret; + ret = lte_lc_init(); + if (ret) { + return ret; + } + + lte_lc_register_handler(lte_evt_handler); + + return 0; } int _anjay_zephyr_network_connect_async(void) { diff --git a/src/nrf_lc_jobs.c b/src/nrf_lc_jobs.c deleted file mode 100644 index 14599d5..0000000 --- a/src/nrf_lc_jobs.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2020-2023 AVSystem - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "lwm2m_internal.h" -#include "nrf_lc_jobs.h" -#include "objects/objects.h" - -LOG_MODULE_REGISTER(anjay_zephyr_nrf_lc_jobs); - -#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED -void _anjay_zephyr_cell_request_job(avs_sched_t *sched, - const void *cell_request_job_args_ptr) { - struct anjay_zephyr_cell_request_job_args args = - *(const struct anjay_zephyr_cell_request_job_args *) - cell_request_job_args_ptr; - _anjay_zephyr_loc_assist_object_send_cell_request( - args.anjay, anjay_zephyr_loc_assist_obj, anjay_zephyr_ecid_obj, - args.request_type); -} -#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED - -#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS -void _anjay_zephyr_agps_request_job(avs_sched_t *sched, const void *anjay_ptr) { - static const uint32_t full_mask = - LOC_ASSIST_A_GPS_MASK_UTC | LOC_ASSIST_A_GPS_MASK_KLOBUCHAR - | LOC_ASSIST_A_GPS_MASK_NEQUICK | LOC_ASSIST_A_GPS_MASK_TOW - | LOC_ASSIST_A_GPS_MASK_CLOCK | LOC_ASSIST_A_GPS_MASK_LOCATION - | LOC_ASSIST_A_GPS_MASK_INTEGRITY | LOC_ASSIST_A_GPS_MASK_EPHEMERIS - | LOC_ASSIST_A_GPS_MASK_ALMANAC; - LOG_INF("Manual request of A-GPS data"); - _anjay_zephyr_loc_assist_object_send_agps_request( - *(anjay_t *const *) anjay_ptr, anjay_zephyr_loc_assist_obj, - full_mask); -} -#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS diff --git a/src/nrf_lc_jobs.h b/src/nrf_lc_jobs.h deleted file mode 100644 index 92b0ba0..0000000 --- a/src/nrf_lc_jobs.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2020-2023 AVSystem - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "objects/objects.h" -#include - -#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED -struct anjay_zephyr_cell_request_job_args { - anjay_t *anjay; - enum anjay_zephyr_loc_assist_cell_request_type request_type; -}; -avs_sched_clb_t _anjay_zephyr_cell_request_job; -#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED -#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS -avs_sched_clb_t _anjay_zephyr_agps_request_job; -#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS diff --git a/src/objects/conn_mon.c b/src/objects/conn_mon.c index 0e7202c..e267dbb 100644 --- a/src/objects/conn_mon.c +++ b/src/objects/conn_mon.c @@ -26,9 +26,13 @@ #include #include #include +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES +# include +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES #include "../nrf_lc_info.h" #include "objects.h" +#include "utils.h" #define LTE_FDD_BEARER 6 #define NB_IOT_BEARER 7 @@ -39,6 +43,17 @@ */ #define RSRP_ADJ(rsrp) ((rsrp) - (((rsrp) <= 0) ? 140 : 141)) +#define SEND_RES_PATH(Oid, Iid, Rid) \ + { \ + .oid = (Oid), \ + .iid = (Iid), \ + .rid = (Rid) \ + } + +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES +LOG_MODULE_REGISTER(anjay_zephyr_conn_mon); +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES + struct conn_mon_object { const anjay_dm_object_def_t *def; @@ -50,6 +65,7 @@ struct conn_mon_object { uint32_t cell_id; uint32_t area_code; char ip_address[MODEM_INFO_MAX_RESPONSE_SIZE]; + struct k_mutex update_mutex; }; static inline struct conn_mon_object * @@ -217,7 +233,7 @@ const anjay_dm_object_def_t **_anjay_zephyr_conn_mon_object_create( ip_address_fields_compatible); memcpy(obj->ip_address, nrf_lc_info->ip_addr, sizeof(obj->ip_address)); } - + k_mutex_init(&obj->update_mutex); return &obj->def; } @@ -230,52 +246,56 @@ void _anjay_zephyr_conn_mon_object_update( } struct conn_mon_object *obj = get_obj(def); - - struct lte_lc_cell const *cell_info = &nrf_lc_info->cells.current_cell; - - if (cell_info->id != LTE_LC_CELL_EUTRAN_ID_INVALID) { - if (nrf_lc_info->lte_mode != obj->lte_mode) { - obj->lte_mode = nrf_lc_info->lte_mode; - anjay_notify_changed(anjay, obj->def->oid, 0, - RID_CONN_MON_NETWORK_BEARER); - } - - if (cell_info->rsrp != obj->rsrp) { - obj->rsrp = cell_info->rsrp; - anjay_notify_changed(anjay, obj->def->oid, 0, RID_CONN_MON_RSS); - } - - if (cell_info->rsrq != obj->rsrq) { - obj->rsrq = cell_info->rsrq; - anjay_notify_changed(anjay, obj->def->oid, 0, - RID_CONN_MON_LINK_QUALITY); - } - - if (strcmp(obj->ip_address, nrf_lc_info->ip_addr)) { - memcpy(obj->ip_address, nrf_lc_info->ip_addr, - sizeof(obj->ip_address)); - anjay_notify_changed(anjay, obj->def->oid, 0, - RID_CONN_MON_IP_ADDRESSES); - } - - if (cell_info->id != obj->cell_id) { - obj->cell_id = cell_info->id; - anjay_notify_changed(anjay, obj->def->oid, 0, RID_CONN_MON_CELL_ID); - } - - if (cell_info->mnc != obj->mnc) { - obj->mnc = cell_info->mnc; - anjay_notify_changed(anjay, obj->def->oid, 0, RID_CONN_MON_SMNC); - } - - if (cell_info->mcc != obj->mcc) { - obj->mcc = cell_info->mcc; - anjay_notify_changed(anjay, obj->def->oid, 0, RID_CONN_MON_SMCC); - } - - if (cell_info->tac != obj->area_code) { - obj->area_code = cell_info->tac; - anjay_notify_changed(anjay, obj->def->oid, 0, RID_CONN_MON_LAC); + SYNCHRONIZED(obj->update_mutex) { + struct lte_lc_cell const *cell_info = &nrf_lc_info->cells.current_cell; + + if (cell_info->id != LTE_LC_CELL_EUTRAN_ID_INVALID) { + if (nrf_lc_info->lte_mode != obj->lte_mode) { + obj->lte_mode = nrf_lc_info->lte_mode; + anjay_notify_changed(anjay, obj->def->oid, 0, + RID_CONN_MON_NETWORK_BEARER); + } + + if (cell_info->rsrp != obj->rsrp) { + obj->rsrp = cell_info->rsrp; + anjay_notify_changed(anjay, obj->def->oid, 0, RID_CONN_MON_RSS); + } + + if (cell_info->rsrq != obj->rsrq) { + obj->rsrq = cell_info->rsrq; + anjay_notify_changed(anjay, obj->def->oid, 0, + RID_CONN_MON_LINK_QUALITY); + } + + if (strcmp(obj->ip_address, nrf_lc_info->ip_addr)) { + memcpy(obj->ip_address, nrf_lc_info->ip_addr, + sizeof(obj->ip_address)); + anjay_notify_changed(anjay, obj->def->oid, 0, + RID_CONN_MON_IP_ADDRESSES); + } + + if (cell_info->id != obj->cell_id) { + obj->cell_id = cell_info->id; + anjay_notify_changed(anjay, obj->def->oid, 0, + RID_CONN_MON_CELL_ID); + } + + if (cell_info->mnc != obj->mnc) { + obj->mnc = cell_info->mnc; + anjay_notify_changed(anjay, obj->def->oid, 0, + RID_CONN_MON_SMNC); + } + + if (cell_info->mcc != obj->mcc) { + obj->mcc = cell_info->mcc; + anjay_notify_changed(anjay, obj->def->oid, 0, + RID_CONN_MON_SMCC); + } + + if (cell_info->tac != obj->area_code) { + obj->area_code = cell_info->tac; + anjay_notify_changed(anjay, obj->def->oid, 0, RID_CONN_MON_LAC); + } } } } @@ -289,3 +309,34 @@ void _anjay_zephyr_conn_mon_object_release( *out_def = NULL; } } + +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES +int _anjay_zephyr_conn_mon_object_add_to_batch( + anjay_t *anjay, + anjay_send_batch_builder_t *builder, + const anjay_dm_object_def_t *const *obj_ptr) { + static const anjay_send_resource_path_t conn_mon_paths[] = { + SEND_RES_PATH(OID_CONN_MON, 0, RID_CONN_MON_RSS), + SEND_RES_PATH(OID_CONN_MON, 0, RID_CONN_MON_LINK_QUALITY), + SEND_RES_PATH(OID_CONN_MON, 0, RID_CONN_MON_CELL_ID), + SEND_RES_PATH(OID_CONN_MON, 0, RID_CONN_MON_SMNC), + SEND_RES_PATH(OID_CONN_MON, 0, RID_CONN_MON_SMCC), + SEND_RES_PATH(OID_CONN_MON, 0, RID_CONN_MON_LAC) + }; + + int result = -1; + struct conn_mon_object *obj = get_obj(obj_ptr); + assert(obj); + + SYNCHRONIZED(obj->update_mutex) { + if ((result = anjay_send_batch_data_add_current_multiple( + builder, anjay, conn_mon_paths, + AVS_ARRAY_SIZE(conn_mon_paths)))) { + LOG_ERR("Failed to add Connectivity Monitoring " + "required resources to batch, err: %d", + result); + } + } + return result; +} +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES diff --git a/src/objects/device.c b/src/objects/device.c index 77012db..06e262b 100644 --- a/src/objects/device.c +++ b/src/objects/device.c @@ -119,9 +119,9 @@ struct device_object { const anjay_dm_object_def_t *def; struct anjay_zephyr_device_id serial_number; -#ifdef CONFIG_ANJAY_ZEPHYR_FOTA +#ifdef CONFIG_ANJAY_ZEPHYR_OTA_MCUBOOT char fw_version[BOOT_IMG_VER_STRLEN_MAX]; -#endif // CONFIG_ANJAY_ZEPHYR_FOTA +#endif // CONFIG_ANJAY_ZEPHYR_OTA_MCUBOOT bool do_reboot; }; @@ -187,11 +187,11 @@ static int resource_read(anjay_t *anjay, case RID_FIRMWARE_VERSION: assert(riid == ANJAY_ID_INVALID); -#ifdef CONFIG_ANJAY_ZEPHYR_FOTA +#ifdef CONFIG_ANJAY_ZEPHYR_OTA_MCUBOOT return anjay_ret_string(ctx, obj->fw_version); -#else // CONFIG_ANJAY_ZEPHYR_FOTA +#else // CONFIG_ANJAY_ZEPHYR_OTA_MCUBOOT return anjay_ret_string(ctx, CONFIG_ANJAY_ZEPHYR_VERSION); -#endif // CONFIG_ANJAY_ZEPHYR_FOTA +#endif // CONFIG_ANJAY_ZEPHYR_OTA_MCUBOOT case RID_ERROR_CODE: assert(riid == 0); @@ -317,12 +317,12 @@ const anjay_dm_object_def_t **_anjay_zephyr_device_object_create(void) { obj->serial_number.value[0] = '\0'; } -#ifdef CONFIG_ANJAY_ZEPHYR_FOTA +#ifdef CONFIG_ANJAY_ZEPHYR_OTA_MCUBOOT if (_anjay_zephyr_get_fw_version_image_0(obj->fw_version, sizeof(obj->fw_version))) { obj->fw_version[0] = '\0'; } -#endif // CONFIG_ANJAY_ZEPHYR_FOTA +#endif // CONFIG_ANJAY_ZEPHYR_OTA_MCUBOOT return &obj->def; } diff --git a/src/objects/ecid.c b/src/objects/ecid.c index e0d0ade..f4ba703 100644 --- a/src/objects/ecid.c +++ b/src/objects/ecid.c @@ -21,6 +21,9 @@ #include #include +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION +# include +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION #include #include @@ -29,11 +32,23 @@ #include "../utils.h" #include "objects.h" +#define SEND_RES_PATH(Oid, Iid, Rid) \ + { \ + .oid = (Oid), \ + .iid = (Iid), \ + .rid = (Rid) \ + } + +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION +LOG_MODULE_REGISTER(anjay_zephyr_ecid); +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION + struct ecid_object { const anjay_dm_object_def_t *def; uint8_t ncells_count_cached; struct lte_lc_ncell neighbor_cells_cached[CONFIG_LTE_NEIGHBOR_CELLS_MAX]; + struct k_mutex update_mutex; }; static inline struct ecid_object * @@ -143,7 +158,7 @@ const anjay_dm_object_def_t **_anjay_zephyr_ecid_object_create( nrf_lc_info->cells.ncells_count * sizeof(*nrf_lc_info->cells.neighbor_cells)); obj->ncells_count_cached = nrf_lc_info->cells.ncells_count; - + k_mutex_init(&obj->update_mutex); return &obj->def; } @@ -154,68 +169,65 @@ void _anjay_zephyr_ecid_object_update( if (!anjay || !def) { return; } - struct ecid_object *obj = get_obj(def); - - assert(nrf_lc_info->cells.ncells_count < CONFIG_LTE_NEIGHBOR_CELLS_MAX); - - // overwrite previously reported instances - for (anjay_iid_t iid = 0; iid < AVS_MIN(obj->ncells_count_cached, - nrf_lc_info->cells.ncells_count); - iid++) { - struct lte_lc_ncell *cached = &obj->neighbor_cells_cached[iid]; - struct lte_lc_ncell *last = &nrf_lc_info->cells.neighbor_cells[iid]; - - if (cached->phys_cell_id != last->phys_cell_id) { - cached->phys_cell_id = last->phys_cell_id; - anjay_notify_changed(anjay, obj->def->oid, iid, - RID_ECID_PHYSCELLID); - } - - if (cached->earfcn != last->earfcn) { - cached->earfcn = last->earfcn; - anjay_notify_changed(anjay, obj->def->oid, iid, - RID_ECID_ARFCNEUTRA); + SYNCHRONIZED(obj->update_mutex) { + assert(nrf_lc_info->cells.ncells_count < CONFIG_LTE_NEIGHBOR_CELLS_MAX); + + // overwrite previously reported instances + for (anjay_iid_t iid = 0; + iid < AVS_MIN(obj->ncells_count_cached, + nrf_lc_info->cells.ncells_count); + iid++) { + struct lte_lc_ncell *cached = &obj->neighbor_cells_cached[iid]; + struct lte_lc_ncell *last = &nrf_lc_info->cells.neighbor_cells[iid]; + + if (cached->phys_cell_id != last->phys_cell_id) { + cached->phys_cell_id = last->phys_cell_id; + anjay_notify_changed(anjay, obj->def->oid, iid, + RID_ECID_PHYSCELLID); + } + + if (cached->earfcn != last->earfcn) { + cached->earfcn = last->earfcn; + anjay_notify_changed(anjay, obj->def->oid, iid, + RID_ECID_ARFCNEUTRA); + } + + if (cached->rsrp != last->rsrp) { + cached->rsrp = last->rsrp; + anjay_notify_changed(anjay, obj->def->oid, iid, + RID_ECID_RSRP_RESULT); + } + + if (cached->rsrq != last->rsrq) { + cached->rsrq = last->rsrq; + anjay_notify_changed(anjay, obj->def->oid, iid, + RID_ECID_RSRQ_RESULT); + } + + if (cached->time_diff != last->time_diff) { + cached->time_diff = last->time_diff; + anjay_notify_changed(anjay, obj->def->oid, iid, + RID_ECID_UE_RXTXTIMEDIFF); + } } - if (cached->rsrp != last->rsrp) { - cached->rsrp = last->rsrp; - anjay_notify_changed(anjay, obj->def->oid, iid, - RID_ECID_RSRP_RESULT); + // if new instance count is higher, write the rest without any + // notifications + for (anjay_iid_t iid = obj->ncells_count_cached; + iid < nrf_lc_info->cells.ncells_count; + iid++) { + obj->neighbor_cells_cached[iid] = + nrf_lc_info->cells.neighbor_cells[iid]; } - if (cached->rsrq != last->rsrq) { - cached->rsrq = last->rsrq; - anjay_notify_changed(anjay, obj->def->oid, iid, - RID_ECID_RSRQ_RESULT); + if (obj->ncells_count_cached != nrf_lc_info->cells.ncells_count) { + obj->ncells_count_cached = nrf_lc_info->cells.ncells_count; + anjay_notify_instances_changed(anjay, obj->def->oid); } - - if (cached->time_diff != last->time_diff) { - cached->time_diff = last->time_diff; - anjay_notify_changed(anjay, obj->def->oid, iid, - RID_ECID_UE_RXTXTIMEDIFF); - } - } - - // if new instance count is higher, write the rest without any notifications - for (anjay_iid_t iid = obj->ncells_count_cached; - iid < nrf_lc_info->cells.ncells_count; - iid++) { - obj->neighbor_cells_cached[iid] = - nrf_lc_info->cells.neighbor_cells[iid]; - } - - if (obj->ncells_count_cached != nrf_lc_info->cells.ncells_count) { - obj->ncells_count_cached = nrf_lc_info->cells.ncells_count; - anjay_notify_instances_changed(anjay, obj->def->oid); } } -uint8_t _anjay_zephyr_ecid_object_instance_count( - const anjay_dm_object_def_t *const *def) { - return def ? get_obj(def)->ncells_count_cached : 0; -} - void _anjay_zephyr_ecid_object_release(const anjay_dm_object_def_t ***out_def) { if (out_def && *out_def) { struct ecid_object *obj = get_obj(*out_def); @@ -224,3 +236,36 @@ void _anjay_zephyr_ecid_object_release(const anjay_dm_object_def_t ***out_def) { *out_def = NULL; } } + +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION +int _anjay_zephyr_ecid_object_add_to_batch( + anjay_t *anjay, + anjay_send_batch_builder_t *builder, + const anjay_dm_object_def_t *const *obj_ptr) { + int result = 0; + struct ecid_object *obj = get_obj(obj_ptr); + assert(obj); + + SYNCHRONIZED(obj->update_mutex) { + for (anjay_iid_t iid = 0; iid < obj->ncells_count_cached; iid++) { + const anjay_send_resource_path_t ecid_paths[] = { + SEND_RES_PATH(OID_ECID, iid, RID_ECID_PHYSCELLID), + SEND_RES_PATH(OID_ECID, iid, RID_ECID_ARFCNEUTRA), + SEND_RES_PATH(OID_ECID, iid, RID_ECID_RSRP_RESULT), + SEND_RES_PATH(OID_ECID, iid, RID_ECID_RSRQ_RESULT), + SEND_RES_PATH(OID_ECID, iid, RID_ECID_UE_RXTXTIMEDIFF) + }; + + if ((result = anjay_send_batch_data_add_current_multiple( + builder, anjay, ecid_paths, + AVS_ARRAY_SIZE(ecid_paths)))) { + LOG_ERR("Failed to add ECID required resources to batch, iid: " + "%" PRIu16 ", err: %d", + iid, result); + break; + } + } + } + return result; +} +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION diff --git a/src/objects/gnss_assistance.c b/src/objects/gnss_assistance.c new file mode 100644 index 0000000..744bd3a --- /dev/null +++ b/src/objects/gnss_assistance.c @@ -0,0 +1,289 @@ +/* + * Copyright 2020-2023 AVSystem + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS +# include +#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS + +#include +#include +#include + +#include "../utils.h" +#include "location_services.h" +#include "objects.h" + +LOG_MODULE_REGISTER(anjay_zephyr_gnss_assistance); + +#define ASSISTANCE_DATA_BUF_SIZE 4096 + +#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_P_GPS +# error "P-GPS not implemented yet" +#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_P_GPS + +typedef struct gnss_assistance_object_struct { + const anjay_dm_object_def_t *def; + uint8_t assistance_data_buf[ASSISTANCE_DATA_BUF_SIZE]; + size_t assistance_data_len; + int32_t result_code; + int32_t result_code_backup; + + uint32_t exponential_backoff; + uint32_t positive_result_code_in_row; + + bool new_result_code; +} gnss_assistance_object_t; + +static inline gnss_assistance_object_t * +get_obj(const anjay_dm_object_def_t *const *obj_ptr) { + assert(obj_ptr); + return AVS_CONTAINER_OF(obj_ptr, gnss_assistance_object_t, def); +} + +static int instance_reset(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid) { + (void) anjay; + (void) iid; + + gnss_assistance_object_t *obj = get_obj(obj_ptr); + assert(obj); + assert(iid == 0); + + obj->positive_result_code_in_row = 0; + obj->exponential_backoff = 0; + obj->new_result_code = false; + if (obj->result_code > 0) { + obj->result_code = 0; + } + obj->result_code_backup = obj->result_code; + return 0; +} + +static int list_resources(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, + anjay_dm_resource_list_ctx_t *ctx) { + (void) anjay; + (void) iid; + + anjay_dm_emit_res(ctx, RID_GNSS_ASSISTANCE_ASSISTANCE_TYPE, ANJAY_DM_RES_R, + ANJAY_DM_RES_ABSENT); +#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS + anjay_dm_emit_res(ctx, RID_GNSS_ASSISTANCE_A_GPS_ASSISTANCE_MASK, + ANJAY_DM_RES_R, ANJAY_DM_RES_ABSENT); +#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS +#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_P_GPS + anjay_dm_emit_res(ctx, RID_GNSS_ASSISTANCE_P_GPS_PREDICTION_COUNT, + ANJAY_DM_RES_R, ANJAY_DM_RES_ABSENT); + anjay_dm_emit_res(ctx, RID_GNSS_ASSISTANCE_P_GPS_PREDICTION_INTERVAL, + ANJAY_DM_RES_R, ANJAY_DM_RES_ABSENT); + anjay_dm_emit_res(ctx, RID_GNSS_ASSISTANCE_P_GPS_START_GPS_DAY, + ANJAY_DM_RES_R, ANJAY_DM_RES_ABSENT); + anjay_dm_emit_res(ctx, RID_GNSS_ASSISTANCE_P_GPS_START_TIME, ANJAY_DM_RES_R, + ANJAY_DM_RES_ABSENT); +#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_P_GPS + anjay_dm_emit_res(ctx, RID_GNSS_ASSISTANCE_ASSISTANCE_DATA, ANJAY_DM_RES_W, + ANJAY_DM_RES_PRESENT); + anjay_dm_emit_res(ctx, RID_GNSS_ASSISTANCE_RESULT_CODE, ANJAY_DM_RES_W, + ANJAY_DM_RES_PRESENT); +#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS + anjay_dm_emit_res(ctx, RID_GNSS_ASSISTANCE_SATELLITE_ELEVATION_MASK, + ANJAY_DM_RES_R, ANJAY_DM_RES_PRESENT); +#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS + return 0; +} + +static int resource_write(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, + anjay_rid_t rid, + anjay_riid_t riid, + anjay_input_ctx_t *ctx) { + (void) anjay; + (void) iid; + + gnss_assistance_object_t *obj = get_obj(obj_ptr); + assert(obj); + assert(iid == 0); + + int result = ANJAY_ERR_METHOD_NOT_ALLOWED; + switch (rid) { + case RID_GNSS_ASSISTANCE_ASSISTANCE_DATA: { + assert(riid == ANJAY_ID_INVALID); + bool finished; + int err = anjay_get_bytes(ctx, &obj->assistance_data_len, &finished, + obj->assistance_data_buf, + sizeof(obj->assistance_data_buf)); + + if (err) { + result = ANJAY_ERR_INTERNAL; + } else if (!finished) { + result = ANJAY_ERR_BAD_REQUEST; + } else { + result = 0; + } + break; + } + + case RID_GNSS_ASSISTANCE_RESULT_CODE: { + assert(riid == ANJAY_ID_INVALID); + obj->new_result_code = true; + result = anjay_get_i32(ctx, &obj->result_code); + break; + } + } + return result; +} + +static int transaction_validate(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr) { + (void) anjay; + + gnss_assistance_object_t *obj = get_obj(obj_ptr); + // NOTE: we expect that if the server sends a non-zero result code then + // it will not send assistance data + if (obj->result_code && obj->assistance_data_len) { + return ANJAY_ERR_BAD_REQUEST; + } + return 0; +} + +static int transaction_commit(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr) { + (void) anjay; + + gnss_assistance_object_t *obj = get_obj(obj_ptr); + obj->result_code_backup = obj->result_code; + + if (obj->new_result_code) { + obj->new_result_code = false; + + if (obj->result_code) { + LOG_WRN("Received %" PRId32 " result code which means it is %s", + obj->result_code, + obj->result_code < 0 ? "permanent failure, further " + "requests will not be processed" + : "temporary failure"); + } + + if (obj->result_code > 0) { + obj->exponential_backoff = + _anjay_zephyr_location_services_calculate_backoff( + obj->positive_result_code_in_row++); + } else { + obj->exponential_backoff = 0; + obj->positive_result_code_in_row = 0; + } + +#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS + _anjay_zephyr_location_services_received_agps_req_response_from_server( + anjay, true); +#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS + } + +#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS + if (obj->assistance_data_len > 0) { + LOG_INF("Received %zu bytes of A-GPS data", obj->assistance_data_len); + + int err = nrf_cloud_agps_process(obj->assistance_data_buf, + obj->assistance_data_len); + obj->assistance_data_len = 0; + + if (err) { + LOG_ERR("Unable to process A-GPS data, error: %d", err); + return ANJAY_ERR_INTERNAL; + } else { + LOG_INF("A-GPS data processed"); + } + } +#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS + return 0; +} + +static int transaction_rollback(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr) { + (void) anjay; + + gnss_assistance_object_t *obj = get_obj(obj_ptr); + + obj->result_code = obj->result_code_backup; + obj->new_result_code = false; +#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS + obj->assistance_data_len = 0; + _anjay_zephyr_location_services_received_agps_req_response_from_server( + anjay, false); +#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS + + return 0; +} + +static const anjay_dm_object_def_t OBJ_DEF = { + .oid = OID_GNSS_ASSISTANCE, + .handlers = { + .list_instances = anjay_dm_list_instances_SINGLE, + .instance_reset = instance_reset, + + .list_resources = list_resources, + .resource_write = resource_write, + + .transaction_begin = anjay_dm_transaction_NOOP, + .transaction_commit = transaction_commit, + .transaction_validate = transaction_validate, + .transaction_rollback = transaction_rollback + } +}; + +int32_t _anjay_zephyr_gnss_assistance_get_result_code( + const anjay_dm_object_def_t *const *obj_ptr) { + gnss_assistance_object_t *obj = get_obj(obj_ptr); + assert(obj); + return obj->result_code_backup; +} + +uint32_t _anjay_zephyr_gnss_assistance_get_exponential_backoff_value( + const anjay_dm_object_def_t *const *obj_ptr) { + gnss_assistance_object_t *obj = get_obj(obj_ptr); + assert(obj); + return obj->exponential_backoff; +} + +const anjay_dm_object_def_t ** +_anjay_zephyr_gnss_assistance_object_create(void) { + gnss_assistance_object_t *obj = (gnss_assistance_object_t *) avs_calloc( + 1, sizeof(gnss_assistance_object_t)); + if (!obj) { + return NULL; + } + obj->def = &OBJ_DEF; + + return &obj->def; +} + +void _anjay_zephyr_gnss_assistance_object_release( + const anjay_dm_object_def_t ***def) { + if (def && *def) { + gnss_assistance_object_t *obj = get_obj(*def); + + avs_free(obj); + *def = NULL; + } +} diff --git a/src/objects/ground_fix_location.c b/src/objects/ground_fix_location.c new file mode 100644 index 0000000..a194180 --- /dev/null +++ b/src/objects/ground_fix_location.c @@ -0,0 +1,303 @@ +/* + * Copyright 2020-2023 AVSystem + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "../utils.h" +#include "location_services.h" +#include "objects.h" + +LOG_MODULE_REGISTER(anjay_zephyr_ground_fix_location); + +typedef struct ground_fix_location_object_struct { + const anjay_dm_object_def_t *def; + int32_t result_code; + int32_t result_code_backup; + anjay_zephyr_location_services_ground_fix_location_t location_current; + anjay_zephyr_location_services_ground_fix_location_t location_backup; + bool send_location_back; + bool send_location_back_set; + + uint32_t exponential_backoff; + uint32_t positive_result_code_in_row; + + bool new_result_code; + + struct k_mutex mutex; +} ground_fix_location_object_t; + +static inline ground_fix_location_object_t * +get_obj(const anjay_dm_object_def_t *const *obj_ptr) { + assert(obj_ptr); + return AVS_CONTAINER_OF(obj_ptr, ground_fix_location_object_t, def); +} + +static int instance_reset(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid) { + (void) anjay; + (void) iid; + + ground_fix_location_object_t *obj = get_obj(obj_ptr); + assert(obj); + assert(iid == 0); + SYNCHRONIZED(obj->mutex) { + memset(&obj->location_current, 0, sizeof(obj->location_current)); + obj->send_location_back_set = false; + obj->positive_result_code_in_row = 0; + obj->exponential_backoff = 0; + obj->new_result_code = false; + if (obj->result_code > 0) { + obj->result_code = 0; + } + obj->result_code_backup = obj->result_code; + } + return 0; +} + +static int list_resources(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, + anjay_dm_resource_list_ctx_t *ctx) { + (void) anjay; + (void) iid; + + anjay_dm_emit_res(ctx, RID_GROUND_FIX_LOC_SEND_LOCATION_BACK, + ANJAY_DM_RES_R, ANJAY_DM_RES_ABSENT); + anjay_dm_emit_res(ctx, RID_GROUND_FIX_LOC_RESULT_CODE, ANJAY_DM_RES_W, + ANJAY_DM_RES_PRESENT); + anjay_dm_emit_res(ctx, RID_GROUND_FIX_LOC_LATITUDE, ANJAY_DM_RES_W, + ANJAY_DM_RES_PRESENT); + anjay_dm_emit_res(ctx, RID_GROUND_FIX_LOC_LONGITUDE, ANJAY_DM_RES_W, + ANJAY_DM_RES_PRESENT); + anjay_dm_emit_res(ctx, RID_GROUND_FIX_LOC_ACCURACY, ANJAY_DM_RES_W, + ANJAY_DM_RES_PRESENT); + return 0; +} + +static int resource_write(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, + anjay_rid_t rid, + anjay_riid_t riid, + anjay_input_ctx_t *ctx) { + (void) anjay; + (void) iid; + + ground_fix_location_object_t *obj = get_obj(obj_ptr); + assert(obj); + assert(iid == 0); + int result = ANJAY_ERR_METHOD_NOT_ALLOWED; + SYNCHRONIZED(obj->mutex) { + switch (rid) { + case RID_GROUND_FIX_LOC_RESULT_CODE: { + assert(riid == ANJAY_ID_INVALID); + obj->new_result_code = true; + result = anjay_get_i32(ctx, &obj->result_code); + break; + } + + case RID_GROUND_FIX_LOC_LATITUDE: { + assert(riid == ANJAY_ID_INVALID); + result = anjay_get_double(ctx, &obj->location_current.latitude); + break; + } + + case RID_GROUND_FIX_LOC_LONGITUDE: { + assert(riid == ANJAY_ID_INVALID); + result = anjay_get_double(ctx, &obj->location_current.longitude); + break; + } + + case RID_GROUND_FIX_LOC_ACCURACY: { + assert(riid == ANJAY_ID_INVALID); + result = anjay_get_double(ctx, &obj->location_current.accuracy); + break; + } + } + } + return result; +} + +static int transaction_begin(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr) { + (void) anjay; + + ground_fix_location_object_t *obj = get_obj(obj_ptr); + SYNCHRONIZED(obj->mutex) { + obj->location_backup = obj->location_current; + } + return 0; +} + +static inline int validate_latitude_angle(double angle) { + return isfinite(angle) && angle >= -90.0 && angle <= 90.0 ? 0 : -1; +} + +static inline int validate_longitude_angle(double angle) { + return isfinite(angle) && angle >= -180.0 && angle <= 180.0 ? 0 : -1; +} + +static inline int accuracy_validate(double accuracy) { + return isfinite(accuracy) && accuracy >= 0 ? 0 : -1; +} + +static int transaction_validate(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr) { + (void) anjay; + + ground_fix_location_object_t *obj = get_obj(obj_ptr); + SYNCHRONIZED(obj->mutex) { + // NOTE: we expect that if the server sends a non-zero result code then + // it will not send the coordinates + if ((obj->location_current.latitude != obj->location_backup.latitude + || obj->location_current.longitude + != obj->location_backup.longitude + || obj->location_current.accuracy != obj->location_backup.accuracy) + && obj->result_code) { + return ANJAY_ERR_BAD_REQUEST; + } + + if (validate_latitude_angle(obj->location_current.latitude) + || validate_longitude_angle(obj->location_current.longitude) + || accuracy_validate(obj->location_current.accuracy)) { + return ANJAY_ERR_BAD_REQUEST; + } + } + return 0; +} + +static int transaction_commit(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr) { + (void) anjay; + + ground_fix_location_object_t *obj = get_obj(obj_ptr); + SYNCHRONIZED(obj->mutex) { + obj->result_code_backup = obj->result_code; + + if (obj->new_result_code) { + obj->new_result_code = false; + + if (obj->result_code) { + LOG_WRN("Received %" PRId32 " result code which means it is %s", + obj->result_code, + obj->result_code < 0 + ? "permanent failure, further requests " + "will not be processed" + : "temporary failure"); + } + + if (obj->result_code > 0) { + obj->exponential_backoff = + _anjay_zephyr_location_services_calculate_backoff( + obj->positive_result_code_in_row++); + } else { + obj->exponential_backoff = 0; + obj->positive_result_code_in_row = 0; + } + + _anjay_zephyr_location_services_received_gf_location_req_response_from_server( + anjay, true, &obj->location_current); + } + } + return 0; +} + +static int transaction_rollback(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr) { + (void) anjay; + + ground_fix_location_object_t *obj = get_obj(obj_ptr); + SYNCHRONIZED(obj->mutex) { + obj->location_current = obj->location_backup; + obj->result_code = obj->result_code_backup; + obj->new_result_code = false; + _anjay_zephyr_location_services_received_gf_location_req_response_from_server( + anjay, false, NULL); + } + return 0; +} + +static const anjay_dm_object_def_t OBJ_DEF = { + .oid = OID_GROUND_FIX_LOC, + .handlers = { + .list_instances = anjay_dm_list_instances_SINGLE, + .instance_reset = instance_reset, + + .list_resources = list_resources, + .resource_write = resource_write, + + .transaction_begin = transaction_begin, + .transaction_validate = transaction_validate, + .transaction_commit = transaction_commit, + .transaction_rollback = transaction_rollback + } +}; + +uint32_t _anjay_zephyr_ground_fix_location_get_exponential_backoff_value( + const anjay_dm_object_def_t *const *obj_ptr) { + uint32_t result = 0; + ground_fix_location_object_t *obj = get_obj(obj_ptr); + assert(obj); + SYNCHRONIZED(obj->mutex) { + result = obj->exponential_backoff; + } + return result; +} + +int32_t _anjay_zephyr_ground_fix_location_get_result_code( + const anjay_dm_object_def_t *const *obj_ptr) { + int32_t result = 0; + ground_fix_location_object_t *obj = get_obj(obj_ptr); + assert(obj); + SYNCHRONIZED(obj->mutex) { + result = obj->result_code_backup; + } + return result; +} + +const anjay_dm_object_def_t ** +_anjay_zephyr_ground_fix_location_object_create(void) { + ground_fix_location_object_t *obj = + (ground_fix_location_object_t *) avs_calloc( + 1, sizeof(ground_fix_location_object_t)); + if (!obj) { + return NULL; + } + obj->def = &OBJ_DEF; + k_mutex_init(&obj->mutex); + + return &obj->def; +} + +void _anjay_zephyr_ground_fix_location_object_release( + const anjay_dm_object_def_t ***def) { + if (def && *def) { + ground_fix_location_object_t *obj = get_obj(*def); + + avs_free(obj); + *def = NULL; + } +} diff --git a/src/objects/light_control.c b/src/objects/light_control.c new file mode 100644 index 0000000..f714f9c --- /dev/null +++ b/src/objects/light_control.c @@ -0,0 +1,240 @@ +/* + * Copyright 2020-2023 AVSystem + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "objects.h" + +/** + * On/Off value: RW, Single, Mandatory + * type: Boolean, range: N/A, unit: N/A + * On/off control. Boolean value where True is On and False is Off. + */ +#define RID_ON_OFF 5850 + +struct light_control_instance { + struct gpio_dt_spec led; + bool led_value; + bool led_backup_value; +}; + +struct light_control_object { + const anjay_dm_object_def_t *def; + uint16_t number_of_instances; + struct light_control_instance instances[]; +}; + +static inline struct light_control_object * +get_obj(const anjay_dm_object_def_t *const *obj_ptr) { + assert(obj_ptr); + return AVS_CONTAINER_OF(obj_ptr, struct light_control_object, def); +} + +static void led_set(struct light_control_instance *inst) { + gpio_pin_set_dt(&inst->led, inst->led_value); +} + +static int configure_led(struct light_control_object *obj, + const struct gpio_dt_spec *led, + anjay_iid_t iid) { + if (!device_is_ready(led->port)) { + return -1; + } + if (gpio_pin_configure_dt(led, GPIO_OUTPUT_INACTIVE)) { + return -1; + } + + struct light_control_instance *inst = &obj->instances[iid]; + inst->led = *led; + inst->led_value = false; + + return 0; +} + +static int resource_read(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, + anjay_rid_t rid, + anjay_riid_t riid, + anjay_output_ctx_t *ctx) { + (void) anjay; + + struct light_control_object *obj = get_obj(obj_ptr); + assert(obj); + + struct light_control_instance *inst = &obj->instances[iid]; + + switch (rid) { + case RID_ON_OFF: + assert(riid == ANJAY_ID_INVALID); + return anjay_ret_bool(ctx, inst->led_value); + + default: + return ANJAY_ERR_METHOD_NOT_ALLOWED; + } +} + +static int resource_write(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, + anjay_rid_t rid, + anjay_riid_t riid, + anjay_input_ctx_t *ctx) { + (void) anjay; + + struct light_control_object *obj = get_obj(obj_ptr); + assert(obj); + + struct light_control_instance *inst = &obj->instances[iid]; + + switch (rid) { + case RID_ON_OFF: { + assert(riid == ANJAY_ID_INVALID); + return anjay_get_bool(ctx, &inst->led_value); + } + default: + return ANJAY_ERR_METHOD_NOT_ALLOWED; + } +} + +static int list_resources(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_iid_t iid, + anjay_dm_resource_list_ctx_t *ctx) { + (void) anjay; + (void) obj_ptr; + (void) iid; + + anjay_dm_emit_res(ctx, RID_ON_OFF, ANJAY_DM_RES_RW, ANJAY_DM_RES_PRESENT); + return 0; +} + +static int list_instances(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr, + anjay_dm_list_ctx_t *ctx) { + (void) anjay; + + struct light_control_object *obj = get_obj(obj_ptr); + for (uint16_t i = 0; i < obj->number_of_instances; i++) { + anjay_dm_emit(ctx, i); + } + + return 0; +} + +static int transaction_begin(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr) { + (void) anjay; + + struct light_control_object *obj = get_obj(obj_ptr); + + for (uint16_t i = 0; i < obj->number_of_instances; i++) { + obj->instances[i].led_backup_value = obj->instances[i].led_value; + } + + return 0; +} + +static int transaction_commit(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr) { + (void) anjay; + + struct light_control_object *obj = get_obj(obj_ptr); + + for (uint16_t i = 0; i < obj->number_of_instances; i++) { + led_set(&obj->instances[i]); + } + + return 0; +} + +static int transaction_rollback(anjay_t *anjay, + const anjay_dm_object_def_t *const *obj_ptr) { + (void) anjay; + + struct light_control_object *obj = get_obj(obj_ptr); + + for (uint16_t i = 0; i < obj->number_of_instances; i++) { + obj->instances[i].led_value = obj->instances[i].led_backup_value; + } + + return 0; +} + +static const anjay_dm_object_def_t obj_def = { + .oid = 3311, + .handlers = { + .list_instances = list_instances, + .list_resources = list_resources, + .resource_read = resource_read, + .resource_write = resource_write, + + .transaction_begin = transaction_begin, + .transaction_validate = anjay_dm_transaction_NOOP, + .transaction_commit = transaction_commit, + .transaction_rollback = transaction_rollback + } +}; + +static const anjay_dm_object_def_t *obj_def_ptr = &obj_def; + +const anjay_dm_object_def_t ** +anjay_zephyr_light_control_object_create(const struct gpio_dt_spec *user_leds, + uint16_t user_leds_len) { + struct light_control_object *obj = + (struct light_control_object *) avs_calloc( + 1, + sizeof(struct light_control_object) + + sizeof(struct light_control_instance) + * user_leds_len); + if (!obj) { + return NULL; + } + obj->def = obj_def_ptr; + + for (anjay_iid_t iid = 0; iid < user_leds_len; iid++) { + if (configure_led(obj, &user_leds[iid], iid)) { + const anjay_dm_object_def_t **out_def = &obj_def_ptr; + anjay_zephyr_light_control_object_release(&out_def); + return NULL; + } + } + obj->number_of_instances = user_leds_len; + + return &obj->def; +} + +void anjay_zephyr_light_control_object_release( + const anjay_dm_object_def_t ***out_def) { + const anjay_dm_object_def_t **def = *out_def; + + if (def) { + struct light_control_object *obj = get_obj(def); + avs_free(obj); + *out_def = NULL; + } +} diff --git a/src/objects/loc_assist.c b/src/objects/loc_assist.c deleted file mode 100644 index 11f22b8..0000000 --- a/src/objects/loc_assist.c +++ /dev/null @@ -1,665 +0,0 @@ -/* - * Copyright 2020-2023 AVSystem - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include - -#include - -#include -#include -#include - -#include -#include - -#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS -# include -#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS - -#include "../nrf_lc_info.h" -#include "objects.h" - -LOG_MODULE_REGISTER(anjay_zephyr_loc_assist); - -/** - * Assistance type: R, Single, Mandatory - * type: integer, range: 0..6, unit: N/A - * 0 = Idle, not waiting for assistance 1 = single cell location inform - * 2 = multi cell location inform 3 = single cell request, send location - * back 4 = multi cell request, send location back 5 = A-GPS 6 = P-GPS - */ -#define RID_ASSISTANCE_TYPE 0 - -/** - * A-GPS assistance mask: R, Single, Optional - * type: integer, range: N/A, unit: N/A - * Bitfield to define what kind of assistance the A-GPS module needs bit - * 0 = needs UTC parameters bit 1 = requires ephemeries data bit 2 = - * requires almanac data ... To be defined later - */ -#define RID_A_GPS_ASSISTANCE_MASK 1 - -/** - * P-GPS predictionCount: R, Single, Optional - * type: integer, range: N/A, unit: N/A - * How many GPS predictions are needed - */ -#define RID_P_GPS_PREDICTIONCOUNT 2 - -/** - * P-GPS predictionIntervalMinutes: R, Single, Optional - * type: integer, range: N/A, unit: min - * The time between predictions (in minutes). - */ -#define RID_P_GPS_PREDICTIONINTERVALMINUTES 3 - -/** - * P-GPS startGpsDay: R, Single, Optional - * type: integer, range: N/A, unit: N/A - * The start day of the prediction set as GPS Day - */ -#define RID_P_GPS_STARTGPSDAY 4 - -/** - * P-GPS startGpsTimeOfDaySeconds: R, Single, Optional - * type: integer, range: N/A, unit: N/A - * The start time of the prediction set as seconds in day. - */ -#define RID_P_GPS_STARTGPSTIMEOFDAYSECONDS 5 - -/** - * assistance_data: W, Single, Optional - * type: opaque, range: N/A, unit: N/A - * Binary assistance data which is going to be written to GNSS module - */ -#define RID_ASSISTANCE_DATA 6 - -/** - * result code: W, Single, Mandatory - * type: string, range: N/A, unit: N/A - * Description. - */ -#define RID_RESULT_CODE 7 - -/** - * latitude: W, Single, Optional - * type: float, range: N/A, unit: N/A - * Response latitude - */ -#define RID_LATITUDE 8 - -/** - * longitude: W, Single, Optional - * type: float, range: N/A, unit: N/A - * Response longitude - */ -#define RID_LONGITUDE 9 - -/** - * altitude: W, Single, Optional - * type: float, range: N/A, unit: N/A - * Response altitude - */ -#define RID_ALTITUDE 10 - -/** - * accuracy: W, Single, Optional - * type: float, range: N/A, unit: N/A - * Response accuracy - */ -#define RID_ACCURACY 11 - -#define ASSISTANCE_DATA_BUF_SIZE 4096 - -#define SEND_RES_PATH(Oid, Iid, Rid) \ - { \ - .oid = (Oid), \ - .iid = (Iid), \ - .rid = (Rid) \ - } - -#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED -struct loc_assist_location { - double latitude; - double longitude; - double altitude; - double accuracy; -}; -#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED - -struct loc_assist_object { - const anjay_dm_object_def_t *def; -#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS - uint8_t assistance_data_buf[ASSISTANCE_DATA_BUF_SIZE]; - size_t assistance_data_len; -#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS -#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED - struct loc_assist_location location_current; - struct loc_assist_location location_backup; -#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED -}; - -static inline struct loc_assist_object * -get_obj(const anjay_dm_object_def_t *const *obj_ptr) { - assert(obj_ptr); - return AVS_CONTAINER_OF(obj_ptr, struct loc_assist_object, def); -} - -static int instance_reset(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr, - anjay_iid_t iid) { - (void) anjay; - (void) iid; - -#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED - struct loc_assist_object *obj = get_obj(obj_ptr); - - assert(obj); - assert(iid == 0); - - memset(&obj->location_current, 0, sizeof(obj->location_current)); -#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED - - return 0; -} - -static int list_resources(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr, - anjay_iid_t iid, - anjay_dm_resource_list_ctx_t *ctx) { - (void) anjay; - (void) obj_ptr; - (void) iid; - - anjay_dm_emit_res(ctx, RID_ASSISTANCE_TYPE, ANJAY_DM_RES_R, - ANJAY_DM_RES_PRESENT); - -#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS - anjay_dm_emit_res(ctx, RID_A_GPS_ASSISTANCE_MASK, ANJAY_DM_RES_R, - ANJAY_DM_RES_PRESENT); -#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS - -#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_P_GPS -# error "P-GPS not implemented yet" -#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_P_GPS - -#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_ASSISTANCE - anjay_dm_emit_res(ctx, RID_ASSISTANCE_DATA, ANJAY_DM_RES_W, - ANJAY_DM_RES_PRESENT); -#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERIVCES_ASSISTANCE - - anjay_dm_emit_res(ctx, RID_RESULT_CODE, ANJAY_DM_RES_W, - ANJAY_DM_RES_PRESENT); - -#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED - anjay_dm_emit_res(ctx, RID_LATITUDE, ANJAY_DM_RES_W, ANJAY_DM_RES_PRESENT); - anjay_dm_emit_res(ctx, RID_LONGITUDE, ANJAY_DM_RES_W, ANJAY_DM_RES_PRESENT); - anjay_dm_emit_res(ctx, RID_ALTITUDE, ANJAY_DM_RES_W, ANJAY_DM_RES_PRESENT); - anjay_dm_emit_res(ctx, RID_ACCURACY, ANJAY_DM_RES_W, ANJAY_DM_RES_PRESENT); -#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED - - return 0; -} - -static int resource_write(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr, - anjay_iid_t iid, - anjay_rid_t rid, - anjay_riid_t riid, - anjay_input_ctx_t *ctx) { - (void) anjay; - (void) iid; - - struct loc_assist_object *obj = get_obj(obj_ptr); - - assert(obj); - assert(iid == 0); - - switch (rid) { -#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_ASSISTANCE - case RID_ASSISTANCE_DATA: { - assert(riid == ANJAY_ID_INVALID); - - bool finished; - int err = anjay_get_bytes(ctx, &obj->assistance_data_len, &finished, - obj->assistance_data_buf, - sizeof(obj->assistance_data_buf)); - - if (err) { - return ANJAY_ERR_INTERNAL; - } - - if (!finished) { - return ANJAY_ERR_BAD_REQUEST; - } - - return 0; - } -#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_ASSISTANCE - case RID_RESULT_CODE: { - assert(riid == ANJAY_ID_INVALID); - - char value[64]; - int err = anjay_get_string(ctx, value, sizeof(value)); - - if (err == ANJAY_BUFFER_TOO_SHORT) { - return ANJAY_ERR_BAD_REQUEST; - } - if (err) { - return ANJAY_ERR_INTERNAL; - } - LOG_WRN("Received result code: %s", value); - return 0; - } - -#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED - case RID_LATITUDE: { - assert(riid == ANJAY_ID_INVALID); - return anjay_get_double(ctx, &obj->location_current.latitude); - } - - case RID_LONGITUDE: { - assert(riid == ANJAY_ID_INVALID); - return anjay_get_double(ctx, &obj->location_current.longitude); - } - - case RID_ALTITUDE: { - assert(riid == ANJAY_ID_INVALID); - return anjay_get_double(ctx, &obj->location_current.altitude); - } - - case RID_ACCURACY: { - assert(riid == ANJAY_ID_INVALID); - return anjay_get_double(ctx, &obj->location_current.accuracy); - } -#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED - - default: - return ANJAY_ERR_METHOD_NOT_ALLOWED; - } -} - -static int transaction_begin(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr) { - (void) anjay; - -#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED - struct loc_assist_object *obj = get_obj(obj_ptr); - - memcpy(&obj->location_backup, &obj->location_current, - sizeof(obj->location_current)); -#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED - - return 0; -} - -static inline int angle_validate(double angle) { - return isfinite(angle) && angle >= -180.0 && angle <= 180.0 ? 0 : -1; -} - -static inline int accuracy_validate(double accuracy) { - return isfinite(accuracy) && accuracy >= 0 ? 0 : -1; -} - -static int transaction_validate(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr) { - (void) anjay; - -#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED - struct loc_assist_object *obj = get_obj(obj_ptr); - - if (angle_validate(obj->location_current.latitude) - || angle_validate(obj->location_current.longitude) - || !isfinite(obj->location_current.altitude) - || accuracy_validate(obj->location_current.accuracy)) { - return ANJAY_ERR_BAD_REQUEST; - } -#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED - return 0; -} - -static int transaction_commit(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr) { - (void) anjay; - - struct loc_assist_object *obj = get_obj(obj_ptr); - -#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS - if (obj->assistance_data_len > 0) { - LOG_INF("Received %zu bytes of A-GPS data", obj->assistance_data_len); - - int err = nrf_cloud_agps_process(obj->assistance_data_buf, - obj->assistance_data_len); - obj->assistance_data_len = 0; - - if (err) { - LOG_ERR("Unable to process A-GPS data, error: %d", err); - return ANJAY_ERR_INTERNAL; - } - - LOG_INF("A-GPS data processed"); - } -#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS - -#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED - - if (obj->location_current.latitude != obj->location_backup.latitude - || obj->location_current.longitude != obj->location_backup.longitude - || obj->location_current.altitude != obj->location_backup.altitude - || obj->location_current.accuracy - != obj->location_backup.accuracy) { - LOG_INF("Updated cell-based location" - ", lat: %.3f deg, lon: %.3f deg, alt: %.3f m, acc: %.3f m", - obj->location_current.latitude, obj->location_current.longitude, - obj->location_current.altitude, obj->location_current.accuracy); - } -#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED - - return 0; -} - -static int transaction_rollback(anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_ptr) { - (void) anjay; - - struct loc_assist_object *obj = get_obj(obj_ptr); - -#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS - obj->assistance_data_len = 0; -#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS - -#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED - memcpy(&obj->location_current, &obj->location_backup, - sizeof(obj->location_backup)); -#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED - - return 0; -} - -static const anjay_dm_object_def_t OBJ_DEF = { - .oid = 50001, - .handlers = { - .list_instances = anjay_dm_list_instances_SINGLE, - .instance_reset = instance_reset, - - .list_resources = list_resources, - .resource_write = resource_write, - - .transaction_commit = transaction_commit, - .transaction_begin = transaction_begin, - .transaction_validate = transaction_validate, - .transaction_rollback = transaction_rollback - } -}; - -const anjay_dm_object_def_t **_anjay_zephyr_loc_assist_object_create(void) { - struct loc_assist_object *obj = (struct loc_assist_object *) avs_calloc( - 1, sizeof(struct loc_assist_object)); - - if (!obj) { - return NULL; - } - obj->def = &OBJ_DEF; - - return &obj->def; -} - -static inline bool is_deferrable_condition(anjay_send_result_t condition) { - return condition == ANJAY_SEND_ERR_OFFLINE - || condition == ANJAY_SEND_ERR_BOOTSTRAP; -} - -static int batch_compile_and_send(anjay_t *anjay, - anjay_send_batch_builder_t **builder_ptr, - const char *req_kind) { - int result = -1; - anjay_send_batch_t *batch = anjay_send_batch_builder_compile(builder_ptr); - - if (!batch) { - LOG_ERR("Batch compilation failed"); - return result; - } - - // best effort - we're sending the request to all servers, although some of - // them might not answer with correct location at all - AVS_LIST(const anjay_ssid_t) ssids = anjay_server_get_ssids(anjay); - - if (!ssids) { - LOG_ERR("No servers to send the batch to"); - } - - AVS_LIST(const anjay_ssid_t) ssid; - - AVS_LIST_FOREACH(ssid, ssids) { - anjay_send_result_t send_result = - anjay_send(anjay, *ssid, batch, NULL, NULL); - - if (is_deferrable_condition(send_result)) { - LOG_WRN("Target SSID=%" PRIu16 " is offline, attempting " - "deferred send", - *ssid); - send_result = - anjay_send_deferrable(anjay, *ssid, batch, NULL, NULL); - } - - if (send_result) { - LOG_ERR("Couldn't send the %s request to SSID=%" PRIu16 ", err: %d", - req_kind, *ssid, send_result); - } else { - LOG_INF("Sent the %s request to SSID=%" PRIu16, req_kind, *ssid); - result = 0; - } - } - - anjay_send_batch_release(&batch); - return result; -} - -static int add_conn_mon_to_batch(anjay_t *anjay, - anjay_send_batch_builder_t *builder) { - static const anjay_send_resource_path_t conn_mon_paths[] = { - SEND_RES_PATH(OID_CONN_MON, 0, RID_CONN_MON_RSS), - SEND_RES_PATH(OID_CONN_MON, 0, RID_CONN_MON_LINK_QUALITY), - SEND_RES_PATH(OID_CONN_MON, 0, RID_CONN_MON_CELL_ID), - SEND_RES_PATH(OID_CONN_MON, 0, RID_CONN_MON_SMNC), - SEND_RES_PATH(OID_CONN_MON, 0, RID_CONN_MON_SMCC), - SEND_RES_PATH(OID_CONN_MON, 0, RID_CONN_MON_LAC) - }; - int result = anjay_send_batch_data_add_current_multiple( - builder, anjay, conn_mon_paths, AVS_ARRAY_SIZE(conn_mon_paths)); - if (result) { - LOG_ERR("Failed to add Connectivity Monitoring " - "required resources to batch, err: %d", - result); - } - return result; -} - -#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS -void _anjay_zephyr_loc_assist_object_send_agps_request( - anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_def, - uint32_t request_mask) { - if (!anjay || !obj_def) { - return; - } - assert(*obj_def == &OBJ_DEF); - - static const struct { - uint32_t req_flag; - const char *name; - } agps_flag_names[] = { - { - .req_flag = LOC_ASSIST_A_GPS_MASK_UTC, - .name = "UTC parameters" - }, - { - .req_flag = LOC_ASSIST_A_GPS_MASK_KLOBUCHAR, - .name = "Klobuchar ionospheric correction parameters" - }, - { - .req_flag = LOC_ASSIST_A_GPS_MASK_NEQUICK, - .name = "NeQuick ionospheric correction parameters" - }, - { - .req_flag = LOC_ASSIST_A_GPS_MASK_TOW, - .name = "SV time of week" - }, - { - .req_flag = LOC_ASSIST_A_GPS_MASK_CLOCK, - .name = "GPS system time" - }, - { - .req_flag = LOC_ASSIST_A_GPS_MASK_LOCATION, - .name = "Position assistance parameters" - }, - { - .req_flag = LOC_ASSIST_A_GPS_MASK_INTEGRITY, - .name = "Integrity assistance parameters" - }, - { - .req_flag = LOC_ASSIST_A_GPS_MASK_EPHEMERIS, - .name = "GPS ephemeris" - }, - { - .req_flag = LOC_ASSIST_A_GPS_MASK_ALMANAC, - .name = "GPS almanac" - } - }; - - LOG_INF("Requesting following types of A-GPS data:"); - for (size_t i = 0; i < AVS_ARRAY_SIZE(agps_flag_names); i++) { - if (agps_flag_names[i].req_flag & request_mask) { - LOG_INF("%s", agps_flag_names[i].name); - } - } - - anjay_send_batch_builder_t *builder = anjay_send_batch_builder_new(); - - if (!builder) { - LOG_ERR("Failed to allocate batch builder"); - return; - } - - int result = anjay_send_batch_add_int(builder, OBJ_DEF.oid, 0, - RID_ASSISTANCE_TYPE, UINT16_MAX, - avs_time_real_now(), 5); - if (result) { - LOG_ERR("Failed to add assistance type to batch, err: %d", result); - goto finalize_batch; - } - - // FIXME: current spec uses int for this resource, but uint is more - // appropriate - result = anjay_send_batch_add_int(builder, OBJ_DEF.oid, 0, - RID_A_GPS_ASSISTANCE_MASK, UINT16_MAX, - avs_time_real_now(), request_mask); - if (result) { - LOG_ERR("Failed to add assistance type to batch, err: %d", result); - goto finalize_batch; - } - - result = add_conn_mon_to_batch(anjay, builder); - if (result) { - goto finalize_batch; - } - -finalize_batch: - if (result) { - anjay_send_batch_builder_cleanup(&builder); - return; - } - - batch_compile_and_send(anjay, &builder, "A-GPS"); - anjay_send_batch_builder_cleanup(&builder); -} -#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS - -#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED -void _anjay_zephyr_loc_assist_object_send_cell_request( - anjay_t *anjay, - const anjay_dm_object_def_t *const *loc_assist_def, - const anjay_dm_object_def_t *const *ecid_def, - enum anjay_zephyr_loc_assist_cell_request_type request_type) { - if (!anjay || !loc_assist_def) { - return; - } - - anjay_send_batch_builder_t *builder = anjay_send_batch_builder_new(); - - if (!builder) { - LOG_ERR("Failed to allocate batch builder"); - return; - } - - int result = anjay_send_batch_add_int(builder, OBJ_DEF.oid, 0, - RID_ASSISTANCE_TYPE, UINT16_MAX, - avs_time_real_now(), request_type); - if (result) { - LOG_ERR("Failed to add assistance type to batch, err: %d", result); - goto finalize_batch; - } - - result = add_conn_mon_to_batch(anjay, builder); - if (result) { - goto finalize_batch; - } - - if (request_type == LOC_ASSIST_CELL_REQUEST_INFORM_MULTI - || request_type == LOC_ASSIST_CELL_REQUEST_REQUEST_MULTI) { - uint8_t ecid_instance_count = - _anjay_zephyr_ecid_object_instance_count(ecid_def); - - for (anjay_iid_t iid = 0; iid < ecid_instance_count; iid++) { - const anjay_send_resource_path_t ecid_paths[] = { - SEND_RES_PATH(OID_ECID, iid, RID_ECID_PHYSCELLID), - SEND_RES_PATH(OID_ECID, iid, RID_ECID_ARFCNEUTRA), - SEND_RES_PATH(OID_ECID, iid, RID_ECID_RSRP_RESULT), - SEND_RES_PATH(OID_ECID, iid, RID_ECID_RSRQ_RESULT), - SEND_RES_PATH(OID_ECID, iid, RID_ECID_UE_RXTXTIMEDIFF) - }; - - result = anjay_send_batch_data_add_current_multiple( - builder, anjay, ecid_paths, AVS_ARRAY_SIZE(ecid_paths)); - if (result) { - LOG_ERR("Failed to add ECID required resources, iid: %" PRIu16 - ", err: %d", - iid, result); - goto finalize_batch; - } - } - } - -finalize_batch: - if (result) { - anjay_send_batch_builder_cleanup(&builder); - return; - } - - batch_compile_and_send(anjay, &builder, "cell-based location"); - anjay_send_batch_builder_cleanup(&builder); -} -#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED - -void _anjay_zephyr_loc_assist_object_release( - const anjay_dm_object_def_t ***out_def) { - if (out_def && *out_def) { - struct loc_assist_object *obj = get_obj(*out_def); - - avs_free(obj); - *out_def = NULL; - } -} diff --git a/src/objects/objects.h b/src/objects/objects.h index 65f6ef4..5303cd6 100644 --- a/src/objects/objects.h +++ b/src/objects/objects.h @@ -17,6 +17,7 @@ #pragma once #include +#include #include #include @@ -62,7 +63,12 @@ void _anjay_zephyr_conn_mon_object_update( anjay_t *anjay, const anjay_dm_object_def_t *const *def, const struct anjay_zephyr_nrf_lc_info *nrf_lc_info); - +# ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES +int _anjay_zephyr_conn_mon_object_add_to_batch( + anjay_t *anjay, + anjay_send_batch_builder_t *builder, + const anjay_dm_object_def_t *const *obj_ptr); +# endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES # define OID_ECID 10256 # define RID_ECID_PHYSCELLID 0 @@ -78,44 +84,73 @@ void _anjay_zephyr_ecid_object_update( anjay_t *anjay, const anjay_dm_object_def_t *const *def, const struct anjay_zephyr_nrf_lc_info *nrf_lc_info); -uint8_t _anjay_zephyr_ecid_object_instance_count( - const anjay_dm_object_def_t *const *def); -#endif // CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO - -#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES -const anjay_dm_object_def_t **_anjay_zephyr_loc_assist_object_create(void); -void _anjay_zephyr_loc_assist_object_release( - const anjay_dm_object_def_t ***out_def); - -# ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS -# define LOC_ASSIST_A_GPS_MASK_UTC BIT(0) -# define LOC_ASSIST_A_GPS_MASK_EPHEMERIS BIT(1) -# define LOC_ASSIST_A_GPS_MASK_ALMANAC BIT(2) -# define LOC_ASSIST_A_GPS_MASK_KLOBUCHAR BIT(3) -# define LOC_ASSIST_A_GPS_MASK_NEQUICK BIT(4) -# define LOC_ASSIST_A_GPS_MASK_TOW BIT(5) -# define LOC_ASSIST_A_GPS_MASK_CLOCK BIT(6) -# define LOC_ASSIST_A_GPS_MASK_LOCATION BIT(7) -# define LOC_ASSIST_A_GPS_MASK_INTEGRITY BIT(8) - -void _anjay_zephyr_loc_assist_object_send_agps_request( - anjay_t *anjay, - const anjay_dm_object_def_t *const *obj_def, - uint32_t request_mask); -# endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS - -# ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED -enum anjay_zephyr_loc_assist_cell_request_type { - LOC_ASSIST_CELL_REQUEST_INFORM_SINGLE = 1, - LOC_ASSIST_CELL_REQUEST_INFORM_MULTI = 2, - LOC_ASSIST_CELL_REQUEST_REQUEST_SINGLE = 3, - LOC_ASSIST_CELL_REQUEST_REQUEST_MULTI = 4 -}; - -void _anjay_zephyr_loc_assist_object_send_cell_request( +# ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION +int _anjay_zephyr_ecid_object_add_to_batch( anjay_t *anjay, - const anjay_dm_object_def_t *const *loc_assist_def, - const anjay_dm_object_def_t *const *ecid_def, - enum anjay_zephyr_loc_assist_cell_request_type request_type); -# endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_MANUAL_CELL_BASED -#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES + anjay_send_batch_builder_t *builder, + const anjay_dm_object_def_t *const *obj_ptr); +# endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION +#endif // CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO + +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION +# define OID_GROUND_FIX_LOC 33626 + +# define RID_GROUND_FIX_LOC_SEND_LOCATION_BACK 0 +# define RID_GROUND_FIX_LOC_RESULT_CODE 1 +# define RID_GROUND_FIX_LOC_LATITUDE 2 +# define RID_GROUND_FIX_LOC_LONGITUDE 3 +# define RID_GROUND_FIX_LOC_ACCURACY 4 + +const anjay_dm_object_def_t ** +_anjay_zephyr_ground_fix_location_object_create(void); +void _anjay_zephyr_ground_fix_location_object_release( + const anjay_dm_object_def_t ***def); +int32_t _anjay_zephyr_ground_fix_location_get_result_code( + const anjay_dm_object_def_t *const *obj_ptr); +uint32_t _anjay_zephyr_ground_fix_location_get_exponential_backoff_value( + const anjay_dm_object_def_t *const *obj_ptr); +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_GROUND_FIX_LOCATION + +#ifdef CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS +# define LOC_SERVICES_A_GPS_MASK_UTC BIT(0) +# define LOC_SERVICES_A_GPS_MASK_EPHEMERIS BIT(1) +# define LOC_SERVICES_A_GPS_MASK_ALMANAC BIT(2) +# define LOC_SERVICES_A_GPS_MASK_KLOBUCHAR BIT(3) +# define LOC_SERVICES_A_GPS_MASK_NEQUICK BIT(4) +# define LOC_SERVICES_A_GPS_MASK_TOW BIT(5) +# define LOC_SERVICES_A_GPS_MASK_CLOCK BIT(6) +# define LOC_SERVICES_A_GPS_MASK_LOCATION BIT(7) +# define LOC_SERVICES_A_GPS_MASK_INTEGRITY BIT(8) + +# define LOC_SERVICES_A_GPS_FULL_MASK \ + (LOC_SERVICES_A_GPS_MASK_UTC | LOC_SERVICES_A_GPS_MASK_KLOBUCHAR \ + | LOC_SERVICES_A_GPS_MASK_NEQUICK | LOC_SERVICES_A_GPS_MASK_TOW \ + | LOC_SERVICES_A_GPS_MASK_CLOCK | LOC_SERVICES_A_GPS_MASK_LOCATION \ + | LOC_SERVICES_A_GPS_MASK_INTEGRITY \ + | LOC_SERVICES_A_GPS_MASK_EPHEMERIS \ + | LOC_SERVICES_A_GPS_MASK_ALMANAC) + +#endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF_A_GPS + +#ifdef CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_ASSISTANCE + +# define OID_GNSS_ASSISTANCE 33625 + +# define RID_GNSS_ASSISTANCE_ASSISTANCE_TYPE 0 +# define RID_GNSS_ASSISTANCE_A_GPS_ASSISTANCE_MASK 1 +# define RID_GNSS_ASSISTANCE_P_GPS_PREDICTION_COUNT 2 +# define RID_GNSS_ASSISTANCE_P_GPS_PREDICTION_INTERVAL 3 +# define RID_GNSS_ASSISTANCE_P_GPS_START_GPS_DAY 4 +# define RID_GNSS_ASSISTANCE_P_GPS_START_TIME 5 +# define RID_GNSS_ASSISTANCE_ASSISTANCE_DATA 6 +# define RID_GNSS_ASSISTANCE_RESULT_CODE 7 +# define RID_GNSS_ASSISTANCE_SATELLITE_ELEVATION_MASK 8 + +const anjay_dm_object_def_t **_anjay_zephyr_gnss_assistance_object_create(void); +void _anjay_zephyr_gnss_assistance_object_release( + const anjay_dm_object_def_t ***def); +uint32_t _anjay_zephyr_gnss_assistance_get_exponential_backoff_value( + const anjay_dm_object_def_t *const *obj_ptr); +int32_t _anjay_zephyr_gnss_assistance_get_result_code( + const anjay_dm_object_def_t *const *obj_ptr); +#endif // CONFIG_ANJAY_ZEPHYR_LOCATION_SERVICES_ASSISTANCE diff --git a/src/persistence.c b/src/persistence.c index bbab267..83250cf 100644 --- a/src/persistence.c +++ b/src/persistence.c @@ -19,6 +19,9 @@ #include #include +#ifdef CONFIG_ANJAY_ZEPHYR_PERSISTENCE_ATTR_STORAGE +# include +#endif // CONFIG_ANJAY_ZEPHYR_PERSISTENCE_ATTR_STORAGE #include #include #include @@ -66,6 +69,9 @@ struct persistence_target { static bool previous_attempt_failed; static const struct persistence_target targets[] = { +#ifdef CONFIG_ANJAY_ZEPHYR_PERSISTENCE_ATTR_STORAGE + DECL_TARGET(attr_storage), +#endif // CONFIG_ANJAY_ZEPHYR_PERSISTENCE_ATTR_STORAGE DECL_TARGET(access_control), DECL_TARGET(security_object), DECL_TARGET(server_object) }; @@ -80,24 +86,32 @@ int anjay_zephyr_persistence_init(void) { return 0; } +static int clear_key(const char *root_name, const char *key) { + char key_buf[64]; + if (avs_simple_snprintf(key_buf, sizeof(key_buf), "%s/%s", root_name, key) + < 0 + || settings_save_one(key_buf, NULL, 0)) { + LOG_ERR("Couldn't delete %s/%s from storage", root_name, key); + return -1; + } + LOG_INF("deleted %s/%s from storage", root_name, key); + + return 0; +} + static int settings_purge(const char *root_name) { + int ret = 0; for (size_t i = 0; i < AVS_ARRAY_SIZE(targets); i++) { - char key_buf[64]; - - if (avs_simple_snprintf(key_buf, sizeof(key_buf), "%s/%s", root_name, - targets[i].name) - < 0 - || settings_save_one(key_buf, NULL, 0)) { - LOG_ERR("Couldn't delete %s/%s from storage", root_name, - targets[i].name); - return -1; + if (clear_key(root_name, targets[i].name)) { + ret = -1; } } - return 0; + return ret; } int _anjay_zephyr_persistence_purge(void) { - return settings_purge(PERSISTENCE_ROOT_NAME); + int ret = settings_purge(PERSISTENCE_ROOT_NAME); + return ret; } static int persistence_load(const char *key, @@ -187,7 +201,8 @@ int _anjay_zephyr_restore_anjay_from_factory_provisioning(anjay_t *anjay) { static int persist_target_to_settings(anjay_t *anjay, const char *root_name, - const struct persistence_target *target) { + const char *target_name, + persist_fn_t *persist_cb) { avs_stream_t *stream = avs_stream_membuf_create(); void *collected_stream = NULL; size_t collected_stream_len; @@ -198,26 +213,26 @@ static int persist_target_to_settings(anjay_t *anjay, int result = -1; - if (avs_is_err(target->persist(anjay, stream)) + if (avs_is_err(persist_cb(anjay, stream)) || avs_is_err(avs_stream_membuf_take_ownership( stream, &collected_stream, &collected_stream_len))) { - LOG_ERR("Couldn't persist %s", target->name); + LOG_ERR("Couldn't persist %s", target_name); goto cleanup; } char key_buf[64]; if (avs_simple_snprintf(key_buf, sizeof(key_buf), "%s/%s", root_name, - target->name) + target_name) < 0 || settings_save_one(key_buf, collected_stream, collected_stream_len)) { - LOG_ERR("Couldn't save %s/%s to storage", root_name, target->name); + LOG_ERR("Couldn't save %s/%s to storage", root_name, target_name); previous_attempt_failed = true; goto cleanup; } - LOG_INF("%s persisted, len: %zu", target->name, collected_stream_len); + LOG_INF("%s persisted, len: %zu", target_name, collected_stream_len); result = 0; @@ -237,8 +252,9 @@ int _anjay_zephyr_persist_anjay_if_required(anjay_t *anjay) { continue; } - int result = persist_target_to_settings(anjay, PERSISTENCE_ROOT_NAME, - &targets[i]); + int result = + persist_target_to_settings(anjay, PERSISTENCE_ROOT_NAME, + targets[i].name, targets[i].persist); if (result) { return result; @@ -291,8 +307,10 @@ int anjay_zephyr_persist_factory_provisioning_info(anjay_t *anjay) { assert(anjay); for (size_t i = 0; i < AVS_ARRAY_SIZE(targets); i++) { - int result = persist_target_to_settings( - anjay, FACTORY_PROVISIONING_ROOT_NAME, &targets[i]); + int result = + persist_target_to_settings(anjay, + FACTORY_PROVISIONING_ROOT_NAME, + targets[i].name, targets[i].persist); if (result) { settings_purge(FACTORY_PROVISIONING_ROOT_NAME); diff --git a/src/persistence.h b/src/persistence.h index 4a97ef0..6e86258 100644 --- a/src/persistence.h +++ b/src/persistence.h @@ -25,6 +25,12 @@ int _anjay_zephyr_persistence_purge(void); int _anjay_zephyr_restore_anjay_from_persistence(anjay_t *anjay); int _anjay_zephyr_persist_anjay_if_required(anjay_t *anjay); +# ifdef CONFIG_ANJAY_ZEPHYR_CORE_PERSISTENCE +anjay_t *_anjay_zephyr_anjay_new_try_from_core_persistence( + const anjay_configuration_t *config); +void _anjay_zephyr_anjay_delete_try_with_core_persistence(anjay_t *anjay); +# endif // CONFIG_ANJAY_ZEPHYR_CORE_PERSISTENCE + # ifdef CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING int _anjay_zephyr_restore_anjay_from_factory_provisioning(anjay_t *anjay); # endif // CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING diff --git a/src/utils.c b/src/utils.c index 2f330d9..9ae6449 100644 --- a/src/utils.c +++ b/src/utils.c @@ -21,12 +21,12 @@ #include #include -#ifdef CONFIG_ANJAY_ZEPHYR_FOTA +#ifdef CONFIG_ANJAY_ZEPHYR_OTA_MCUBOOT # include # include # include -#endif // CONFIG_ANJAY_ZEPHYR_FOTA +#endif // CONFIG_ANJAY_ZEPHYR_OTA_MCUBOOT #ifdef CONFIG_NRF_MODEM_LIB # include @@ -62,7 +62,7 @@ int _anjay_zephyr_get_device_id(struct anjay_zephyr_device_id *out_id) { #endif // CONFIG_NRF_MODEM_LIB } -#ifdef CONFIG_ANJAY_ZEPHYR_FOTA +#ifdef CONFIG_ANJAY_ZEPHYR_OTA_MCUBOOT static int get_fw_version(char *out_buf, size_t buf_size, uint8_t area_id) { // apparently BOOT_IMG_VER_STRLEN_MAX accounts for the nullchar too if (buf_size < BOOT_IMG_VER_STRLEN_MAX) { @@ -112,7 +112,7 @@ int _anjay_zephyr_get_fw_version_image_1(char *out_buf, size_t buf_size) { # endif return get_fw_version(out_buf, buf_size, area_id); } -#endif // CONFIG_ANJAY_ZEPHYR_FOTA +#endif // CONFIG_ANJAY_ZEPHYR_OTA_MCUBOOT #if defined(CONFIG_NRF_MODEM_LIB) && defined(CONFIG_MODEM_KEY_MGMT) int _anjay_zephyr_tls_session_cache_purge(void) { diff --git a/src/utils.h b/src/utils.h index e12a7e4..20bd9f3 100644 --- a/src/utils.h +++ b/src/utils.h @@ -20,9 +20,9 @@ #include -#ifdef CONFIG_ANJAY_ZEPHYR_FOTA +#ifdef CONFIG_ANJAY_ZEPHYR_OTA_MCUBOOT # include -#endif // CONFIG_ANJAY_ZEPHYR_FOTA +#endif // CONFIG_ANJAY_ZEPHYR_OTA_MCUBOOT struct anjay_zephyr_device_id { // 96 bits as hex + NULL-byte @@ -31,10 +31,10 @@ struct anjay_zephyr_device_id { int _anjay_zephyr_get_device_id(struct anjay_zephyr_device_id *out_id); -#ifdef CONFIG_ANJAY_ZEPHYR_FOTA +#ifdef CONFIG_ANJAY_ZEPHYR_OTA_MCUBOOT int _anjay_zephyr_get_fw_version_image_0(char *out_buf, size_t buf_size); int _anjay_zephyr_get_fw_version_image_1(char *out_buf, size_t buf_size); -#endif // CONFIG_ANJAY_ZEPHYR_FOTA +#endif // CONFIG_ANJAY_ZEPHYR_OTA_MCUBOOT #define SYNCHRONIZED(Mtx) \ for (int _synchronized_exit = k_mutex_lock(&(Mtx), K_FOREVER); \