diff --git a/.gitlint b/.gitlint index 6eed20999836..cf3be07a93c4 100644 --- a/.gitlint +++ b/.gitlint @@ -13,7 +13,7 @@ debug = false extra-path=../zephyr/scripts/gitlint [title-max-length-no-revert] -line-length=72 +line-length=75 [body-min-line-count] min-line-count=1 @@ -22,7 +22,7 @@ min-line-count=1 max-line-count=200 [title-starts-with-subsystem] -regex = ^(([^:]+):)(\s([^:]+):)*\s(.+)$ +regex = ^(?!subsys:)(([^:]+):)(\s([^:]+):)*\s(.+)$ [title-must-not-contain-word] # Comma-separated list of words that should not occur in the title. Matching is case diff --git a/CODEOWNERS b/CODEOWNERS index 381b6b47de7d..3f3ff63b7fa9 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -32,12 +32,14 @@ /include/debug/ppi_trace.h @nordic-krch /include/ @anangl @rlubos @pizi-nordic /include/bluetooth/ @joerchan +/include/bluetooth/mesh/ @trond-snekvik @joerchan /include/drivers/ @anangl /include/net/ @rlubos /include/nfc/ @anangl @grochu /include/shell/ @nordic-krch @jarz-nordic /lib/at_cmd_parser/ @rlubos /lib/at_host/ @rlubos +/lib/at_notif/ @pkchan /lib/bsdlib/ @rlubos /lib/modem_info/ @rlubos /lib/pdn_management/ @rlubos @@ -57,10 +59,12 @@ subsys/debug/CMakeLists.txt @nordic-krch /scripts/ @mbolivar @tejlmand /scripts/hid_configurator/ @pdunaj /subsys/bluetooth/ @joerchan @carlescufi +/subsys/bluetooth/mesh/ @trond-snekvik @joerchan /subsys/bootloader/ @hakonfam @ioannisg +/subsys/dfu/ @hakonfam @sigvartmh /subsys/enhanced_shockburst/ @Raane @lemrey /subsys/event_manager/ @pdunaj -/subsys/fw_metadata/ @hakonfam +/subsys/fw_info/ @hakonfam /subsys/net/ @rlubos /subsys/nfc/ @grochu @anangl /subsys/partition_manager/ @hakonfam diff --git a/applications/asset_tracker/CMakeLists.txt b/applications/asset_tracker/CMakeLists.txt index 7f64cc13c677..d72875bca93c 100644 --- a/applications/asset_tracker/CMakeLists.txt +++ b/applications/asset_tracker/CMakeLists.txt @@ -23,6 +23,7 @@ zephyr_library_include_directories( src/ui src/cloud_codec src/env_sensors + src/light_sensor ) # Application sources @@ -31,6 +32,7 @@ add_subdirectory(src/ui) add_subdirectory(src/cloud_codec) add_subdirectory(src/gps_controller) add_subdirectory(src/env_sensors) +add_subdirectory_ifdef(CONFIG_LIGHT_SENSOR src/light_sensor) if (CONFIG_USE_BME680_BSEC) target_link_libraries(app PUBLIC bsec_lib) diff --git a/applications/asset_tracker/Kconfig b/applications/asset_tracker/Kconfig index 7b630bc1a49c..444459ed82e7 100644 --- a/applications/asset_tracker/Kconfig +++ b/applications/asset_tracker/Kconfig @@ -233,6 +233,18 @@ config CLOUD_UA_CONSOLE endchoice +config CLOUD_FOTA_APP + bool "Enable firmware over-the-air upgrades for application" + depends on AWS_FOTA + depends on DFU_TARGET_MCUBOOT + default y + +config CLOUD_FOTA_MODEM + bool "Enable firmware over-the-air upgrades for modem" + depends on AWS_FOTA + depends on DFU_TARGET_MODEM + default y + endmenu # Cloud menu "Environment sensors" @@ -323,6 +335,27 @@ endif endmenu # Environment sensors +menuconfig LIGHT_SENSOR + bool "Light sensor" + select BH1749 if BOARD_NRF9160_PCA20035NS + default y if BOARD_NRF9160_PCA20035NS + +if LIGHT_SENSOR + +config LIGHT_SENSOR_DEV_NAME + string "Light sensor device name" + default "BH1749" if BOARD_NRF9160_PCA20035NS + +config LIGHT_SENSOR_DATA_SEND_INTERVAL + int "Interval in seconds for sending light sensor data" + default 60 + help + Interval between each sampling and sending of light sensor data. + If the GPS is active, sensor data will not be sent before it is + stopped. The unit is seconds. + +endif # LIGHT_SENSOR + config USE_AT_HOST bool "Enable AT commands" default y diff --git a/applications/asset_tracker/README.rst b/applications/asset_tracker/README.rst index db51d34ce0bd..c01eac0d6d48 100644 --- a/applications/asset_tracker/README.rst +++ b/applications/asset_tracker/README.rst @@ -76,7 +76,6 @@ LED 3 and LED 4: Application state indicated by LEDs All LEDs (1-4): - * Blinking simultaneously: Irrecoverable error in the BSD library. * Blinking in groups of two (LED 1 and 3, LED 2 and 4): Recoverable error in the BSD library. * Blinking in cross pattern (LED 1 and 4, LED 2 and 3): Communication error with the nRF Cloud. diff --git a/applications/asset_tracker/prj.conf b/applications/asset_tracker/prj.conf index 82cc5f0d67d9..a3c2a27dc903 100644 --- a/applications/asset_tracker/prj.conf +++ b/applications/asset_tracker/prj.conf @@ -18,11 +18,6 @@ CONFIG_NET_NATIVE=n CONFIG_NET_SOCKETS=y CONFIG_NET_SOCKETS_OFFLOAD=y -# MQTT -CONFIG_MQTT_SOCKET_LIB=y -CONFIG_MQTT_LIB_TLS=y -CONFIG_MQTT_MAX_PACKET_LENGTH=2048 - # LTE link control CONFIG_POWER_OPTIMIZATION_ENABLE=n CONFIG_LTE_LINK_CONTROL=y @@ -81,3 +76,22 @@ CONFIG_HEAP_MEM_POOL_SIZE=16384 CONFIG_MAIN_STACK_SIZE=8192 CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 CONFIG_HW_STACK_PROTECTION=y + +# MCUBOOT +CONFIG_BOOTLOADER_MCUBOOT=y +CONFIG_IMG_MANAGER=y +CONFIG_MCUBOOT_IMG_MANAGER=y + +# Flash +CONFIG_FLASH=y + +# AWS FOTA +CONFIG_AWS_FOTA=y +CONFIG_AWS_FOTA_LOG_LEVEL_DBG=y +CONFIG_AWS_JOBS_LOG_LEVEL_DBG=y +CONFIG_AWS_FOTA_VERSION_STRING_MAX_LEN=128 +CONFIG_DFU_TARGET=y + +# Download client (needed by AWS FOTA) +CONFIG_DOWNLOAD_CLIENT=y +CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 diff --git a/applications/asset_tracker/prj_nrf9160_pca20035ns.conf b/applications/asset_tracker/prj_nrf9160_pca20035ns.conf index b236f1954b29..20357e37e0f5 100644 --- a/applications/asset_tracker/prj_nrf9160_pca20035ns.conf +++ b/applications/asset_tracker/prj_nrf9160_pca20035ns.conf @@ -18,11 +18,6 @@ CONFIG_NET_NATIVE=n CONFIG_NET_SOCKETS=y CONFIG_NET_SOCKETS_OFFLOAD=y -# MQTT -CONFIG_MQTT_SOCKET_LIB=y -CONFIG_MQTT_LIB_TLS=y -CONFIG_MQTT_MAX_PACKET_LENGTH=2048 - # LTE link control CONFIG_POWER_OPTIMIZATION_ENABLE=n CONFIG_LTE_LINK_CONTROL=y @@ -116,3 +111,14 @@ CONFIG_MPU_ALLOW_FLASH_WRITE=y CONFIG_BOOTLOADER_MCUBOOT=y CONFIG_IMG_MANAGER=y CONFIG_MCUBOOT_IMG_MANAGER=y + +# AWS FOTA +CONFIG_AWS_FOTA=y +CONFIG_AWS_FOTA_LOG_LEVEL_DBG=y +CONFIG_AWS_JOBS_LOG_LEVEL_DBG=y +CONFIG_AWS_FOTA_VERSION_STRING_MAX_LEN=128 +CONFIG_DFU_TARGET=y + +# Download client (needed by AWS FOTA) +CONFIG_DOWNLOAD_CLIENT=y +CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 diff --git a/applications/asset_tracker/prj_qemu_x86.conf b/applications/asset_tracker/prj_qemu_x86.conf index d8e2868360e3..b98823483472 100644 --- a/applications/asset_tracker/prj_qemu_x86.conf +++ b/applications/asset_tracker/prj_qemu_x86.conf @@ -33,9 +33,6 @@ CONFIG_CONSOLE_SUBSYS=y CONFIG_CONSOLE_HANDLER=y CONFIG_CONSOLE_GETCHAR=y -CONFIG_MQTT_SOCKET_LIB=y -CONFIG_MQTT_MAX_PACKET_LENGTH=2048 -CONFIG_MQTT_LIB_TLS=y CONFIG_NET_SOCKETS_SOCKOPT_TLS=y # TLS configuration diff --git a/applications/asset_tracker/src/cloud_codec/CMakeLists.txt b/applications/asset_tracker/src/cloud_codec/CMakeLists.txt index 6a13686c5ebb..71d67ed4a6e6 100644 --- a/applications/asset_tracker/src/cloud_codec/CMakeLists.txt +++ b/applications/asset_tracker/src/cloud_codec/CMakeLists.txt @@ -6,3 +6,4 @@ zephyr_include_directories(.) target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/cloud_codec.c) +target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/service_info.c) diff --git a/applications/asset_tracker/src/cloud_codec/cloud_codec.c b/applications/asset_tracker/src/cloud_codec/cloud_codec.c index b6fef82c067d..f8a64c0ba8ce 100644 --- a/applications/asset_tracker/src/cloud_codec/cloud_codec.c +++ b/applications/asset_tracker/src/cloud_codec/cloud_codec.c @@ -4,7 +4,6 @@ * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic */ - #include #include #include @@ -185,18 +184,16 @@ static CMD_NEW_GROUP(group_get, CLOUD_CMD_GROUP_GET, CMD_ARRAY( ); static const char *const channel_type_str[] = { - [CLOUD_CHANNEL_GPS] = "GPS", - [CLOUD_CHANNEL_FLIP] = "FLIP", - [CLOUD_CHANNEL_BUTTON] = "BUTTON", - [CLOUD_CHANNEL_TEMP] = "TEMP", - [CLOUD_CHANNEL_HUMID] = "HUMID", - [CLOUD_CHANNEL_AIR_PRESS] = "AIR_PRESS", - [CLOUD_CHANNEL_AIR_QUAL] = "AIR_QUAL", - [CLOUD_CHANNEL_LTE_LINK_RSRP] = "RSRP", - /* The "device" is intended for the shadow, which expects its objects - * to have lowercase keys. - */ - [CLOUD_CHANNEL_DEVICE_INFO] = "device", + [CLOUD_CHANNEL_GPS] = CLOUD_CHANNEL_STR_GPS, + [CLOUD_CHANNEL_FLIP] = CLOUD_CHANNEL_STR_FLIP, + [CLOUD_CHANNEL_BUTTON] = CLOUD_CHANNEL_STR_BUTTON, + [CLOUD_CHANNEL_TEMP] = CLOUD_CHANNEL_STR_TEMP, + [CLOUD_CHANNEL_HUMID] = CLOUD_CHANNEL_STR_HUMID, + [CLOUD_CHANNEL_AIR_PRESS] = CLOUD_CHANNEL_STR_AIR_PRESS, + [CLOUD_CHANNEL_AIR_QUAL] = CLOUD_CHANNEL_STR_AIR_QUAL, + [CLOUD_CHANNEL_LTE_LINK_RSRP] = CLOUD_CHANNEL_STR_LTE_LINK_RSRP, + [CLOUD_CHANNEL_DEVICE_INFO] = CLOUD_CHANNEL_STR_DEVICE_INFO, + [CLOUD_CHANNEL_LIGHT_SENSOR] = CLOUD_CHANNEL_STR_LIGHT_SENSOR, }; static cloud_cmd_cb_t cloud_command_cb; @@ -460,3 +457,28 @@ int cloud_encode_env_sensors_data(const env_sensor_data_t *sensor_data, return cloud_encode_data(&cloud_sensor, output); } + +#if CONFIG_LIGHT_SENSOR +/* 4 32-bit ints, 3 spaces, NULL */ +#define LIGHT_SENSOR_DATA_STRING_MAX_LEN ((4 * 11) + 3 + 1) +int cloud_encode_light_sensor_data(const struct light_sensor_data *sensor_data, + struct cloud_msg *output) +{ + char buf[LIGHT_SENSOR_DATA_STRING_MAX_LEN]; + u8_t len; + struct cloud_channel_data cloud_sensor; + + if ((sensor_data == NULL) || (output == NULL)) { + return -EINVAL; + } + + len = snprintf(buf, sizeof(buf), "%d %d %d %d", sensor_data->red, + sensor_data->green, sensor_data->blue, sensor_data->ir); + + cloud_sensor.data.buf = buf; + cloud_sensor.data.len = len; + cloud_sensor.type = CLOUD_CHANNEL_LIGHT_SENSOR; + + return cloud_encode_data(&cloud_sensor, output); +} +#endif /* CONFIG_LIGHT_SENSOR */ diff --git a/applications/asset_tracker/src/cloud_codec/cloud_codec.h b/applications/asset_tracker/src/cloud_codec/cloud_codec.h index 259c963c563b..74ef608a777a 100644 --- a/applications/asset_tracker/src/cloud_codec/cloud_codec.h +++ b/applications/asset_tracker/src/cloud_codec/cloud_codec.h @@ -13,8 +13,9 @@ #ifndef CLOUD_CODEC_H__ #define CLOUD_CODEC_H__ -#include "env_sensors.h" #include +#include "env_sensors.h" +#include "light_sensor.h" #ifdef __cplusplus extern "C" { @@ -48,8 +49,24 @@ enum cloud_channel { CLOUD_CHANNEL_LTE_LINK_RSRP, /** The descriptive DEVICE data indicating its status. */ CLOUD_CHANNEL_DEVICE_INFO, + /** The RBG IR light levels on the device. */ + CLOUD_CHANNEL_LIGHT_SENSOR, }; +#define CLOUD_CHANNEL_STR_GPS "GPS" +#define CLOUD_CHANNEL_STR_FLIP "FLIP" +#define CLOUD_CHANNEL_STR_BUTTON "BUTTON" +#define CLOUD_CHANNEL_STR_TEMP "TEMP" +#define CLOUD_CHANNEL_STR_HUMID "HUMID" +#define CLOUD_CHANNEL_STR_AIR_PRESS "AIR_PRESS" +#define CLOUD_CHANNEL_STR_AIR_QUAL "AIR_QUAL" +#define CLOUD_CHANNEL_STR_LTE_LINK_RSRP "RSRP" +/* The "device" is intended for the shadow, which expects its objects + * to have lowercase keys. + */ +#define CLOUD_CHANNEL_STR_DEVICE_INFO "device" +#define CLOUD_CHANNEL_STR_LIGHT_SENSOR "LIGHT" + struct cloud_data { char *buf; size_t len; @@ -164,6 +181,11 @@ static inline void cloud_release_data(struct cloud_msg *data) int cloud_encode_env_sensors_data(const env_sensor_data_t *sensor_data, struct cloud_msg *output); +#if CONFIG_LIGHT_SENSOR +int cloud_encode_light_sensor_data(const struct light_sensor_data *sensor_data, + struct cloud_msg *output); +#endif /* CONFIG_LIGHT_SENSOR */ + /** * @} */ diff --git a/applications/asset_tracker/src/cloud_codec/service_info.c b/applications/asset_tracker/src/cloud_codec/service_info.c new file mode 100644 index 000000000000..a4b61c9a0b3d --- /dev/null +++ b/applications/asset_tracker/src/cloud_codec/service_info.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ +#include +#include "service_info.h" + +#define SERVICE_INFO_JSON_NAME "serviceInfo" +#define UI_JSON_NAME "ui" +#define FOTAS_JSON_NAME "fota_v" +#define FOTAS_JSON_NAME_SIZE (sizeof(FOTAS_JSON_NAME) + 5) + +static int add_array_obj(const char * const items[], const u32_t item_cnt, + const char * const item_name, cJSON * const obj) +{ + cJSON *obj_to_add = NULL; + cJSON *str = NULL; + + if ((obj == NULL) || (item_name == NULL)) { + return -EINVAL; + } + + obj_to_add = cJSON_CreateArray(); + if (obj_to_add == NULL) { + return -ENOMEM; + } + + for (u32_t cnt = 0; cnt < item_cnt; ++cnt) { + if (items[cnt] != NULL) { + str = cJSON_CreateString(items[cnt]); + if (str == NULL) { + cJSON_Delete(obj_to_add); + return -ENOMEM; + } + cJSON_AddItemToArray(obj_to_add, str); + } + } + + /* if no strings were added, use NULL object */ + if (cJSON_GetArraySize(obj_to_add) == 0) { + obj_to_add->type = cJSON_NULL; + } + + cJSON_AddItemToObject(obj, item_name, obj_to_add); + + return 0; +} + +int service_info_json_object_encode( + const char * const ui[], const u32_t ui_count, const char * const fota[], + const u32_t fota_count, const u16_t fota_version, cJSON * const obj_out) +{ + int err = 0; + cJSON *service_info_obj = NULL; + char fota_name[FOTAS_JSON_NAME_SIZE]; + + if ((obj_out == NULL) || ((ui == NULL) && ui_count) || + ((fota == NULL) && fota_count)) { + return -EINVAL; + } + + service_info_obj = cJSON_CreateObject(); + if (service_info_obj == NULL) { + return -ENOMEM; + } + + if (!err) { + err = add_array_obj(ui, ui_count, UI_JSON_NAME, + service_info_obj); + } + + if (!err) { + snprintf(fota_name, sizeof(fota_name), "%s%hu", FOTAS_JSON_NAME, + fota_version); + err = add_array_obj(fota, fota_count, fota_name, + service_info_obj); + } + + if (!err) { + cJSON_AddItemToObject(obj_out, SERVICE_INFO_JSON_NAME, + service_info_obj); + } else { + cJSON_Delete(service_info_obj); + } + + return err; +} diff --git a/applications/asset_tracker/src/cloud_codec/service_info.h b/applications/asset_tracker/src/cloud_codec/service_info.h new file mode 100644 index 000000000000..00c4bb098807 --- /dev/null +++ b/applications/asset_tracker/src/cloud_codec/service_info.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +#ifndef SERVICE_INFO_H__ +#define SERVICE_INFO_H__ + +#include +#include + +/** + * @file service_info.h + * + * @brief API for generating service info JSON for the device shadow + * @defgroup service_info API to generate service info JSON for device shadow. + * @{ + */ +#define SERVICE_INFO_FOTA_VER_CURRENT 1 +#define SERVICE_INFO_FOTA_STR_BOOTLOADER "BOOT" +#define SERVICE_INFO_FOTA_STR_MODEM "MODEM" +#define SERVICE_INFO_FOTA_STR_APP "APP" + +/** @brief Encode the service info to JSON. + * + * Service info is written to the JSON object. + * + * @param ui Array of UI strings. + * @param ui_count Number of ui strings in the array. + * @param fota Array of FOTA strings. + * @param fota_count Number of FOTA strings in the array. + * @param fota_version FOTA version number. + * @param obj_out The JSON object where the data is stored. + * + * @return 0 If the operation was successful. + * Otherwise, a (negative) error code is returned. + */ +int service_info_json_object_encode(const char *const ui[], + const u32_t ui_count, + const char *const fota[], + const u32_t fota_count, + const u16_t fota_version, + cJSON *obj_out); + +/** @} */ + +#endif /* SERVICE_INFO_H__ */ diff --git a/applications/asset_tracker/src/light_sensor/CMakeLists.txt b/applications/asset_tracker/src/light_sensor/CMakeLists.txt new file mode 100644 index 000000000000..4df7b33508df --- /dev/null +++ b/applications/asset_tracker/src/light_sensor/CMakeLists.txt @@ -0,0 +1,9 @@ +# +# Copyright (c) 2019 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic +# + +zephyr_include_directories(.) + +target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/light_sensor.c) diff --git a/applications/asset_tracker/src/light_sensor/light_sensor.c b/applications/asset_tracker/src/light_sensor/light_sensor.c new file mode 100644 index 000000000000..5292ac6b678b --- /dev/null +++ b/applications/asset_tracker/src/light_sensor/light_sensor.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +#include +#include +#include "light_sensor.h" + +enum ls_ch_type { + LS_CH__BEGIN = 0, + + LS_CH_RED = LS_CH__BEGIN, + LS_CH_GREEN, + LS_CH_BLUE, + LS_CH_IR, + + LS_CH__END +}; + +struct ls_ch_data { + enum sensor_channel type; + struct sensor_value data; +}; + +static struct ls_ch_data ls_ch_red = { .type = SENSOR_CHAN_RED }; +static struct ls_ch_data ls_ch_green = { .type = SENSOR_CHAN_GREEN }; +static struct ls_ch_data ls_ch_blue = { .type = SENSOR_CHAN_BLUE }; +static struct ls_ch_data ls_ch_ir = { .type = SENSOR_CHAN_IR }; +static char *ls_dev_name = CONFIG_LIGHT_SENSOR_DEV_NAME; +static struct device *ls_dev; +static struct k_spinlock ls_lock; +static struct ls_ch_data *ls_data[LS_CH__END] = { [LS_CH_RED] = &ls_ch_red, + [LS_CH_GREEN] = &ls_ch_green, + [LS_CH_BLUE] = &ls_ch_blue, + [LS_CH_IR] = &ls_ch_ir }; +static light_sensor_data_ready_cb ls_cb; +static struct k_delayed_work ls_poller; + +static void light_sensor_poll_fn(struct k_work *work); + +int light_sensor_init_and_start(const light_sensor_data_ready_cb cb) +{ + if (!IS_ENABLED(CONFIG_LIGHT_SENSOR)) { + return -ENXIO; + } + + ls_dev = device_get_binding(ls_dev_name); + + if (ls_dev == NULL) { + return -ENODEV; + } + + ls_cb = cb; + + k_delayed_work_init(&ls_poller, light_sensor_poll_fn); + + return k_delayed_work_submit(&ls_poller, K_SECONDS(5)); +} + +int light_sensor_get_data(struct light_sensor_data *const data) +{ + if (data == NULL) { + return -EINVAL; + } + + k_spinlock_key_t key = k_spin_lock(&ls_lock); + + data->red = ls_data[LS_CH_RED]->data.val1; + data->green = ls_data[LS_CH_GREEN]->data.val1; + data->blue = ls_data[LS_CH_BLUE]->data.val1; + data->ir = ls_data[LS_CH_IR]->data.val1; + + k_spin_unlock(&ls_lock, key); + + return 0; +} + +void light_sensor_poll_fn(struct k_work *work) +{ + k_spinlock_key_t key; + + int err = sensor_sample_fetch_chan(ls_dev, SENSOR_CHAN_ALL); + + if (err) { + printk("Failed to fetch data from %s, error: %d\n", ls_dev_name, + err); + } + + key = k_spin_lock(&ls_lock); + + for (enum ls_ch_type ch = LS_CH__BEGIN; ch < LS_CH__END; ++ch) { + err = sensor_channel_get(ls_dev, ls_data[ch]->type, + &ls_data[ch]->data); + if (err) { + printk("Failed to get data from %s, sensor ch: %d , error: %d\n", + ls_dev_name, ls_data[ch]->type, err); + } + } + + k_spin_unlock(&ls_lock, key); + + if (ls_cb != NULL) { + ls_cb(); + } + + k_delayed_work_submit( + &ls_poller, K_SECONDS(CONFIG_LIGHT_SENSOR_DATA_SEND_INTERVAL)); +} diff --git a/applications/asset_tracker/src/light_sensor/light_sensor.h b/applications/asset_tracker/src/light_sensor/light_sensor.h new file mode 100644 index 000000000000..ccf7410f1bd0 --- /dev/null +++ b/applications/asset_tracker/src/light_sensor/light_sensor.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +#ifndef LIGHT_SENSOR_H_ +#define LIGHT_SENSOR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file light_sensor.h + * @defgroup light_sensors Light sensor interface + * @{ + * @brief Module for interfacing light sensor for asset tracker + * + * @details Interface for RGB IR light sensor for asset tracker. + * + */ +#include +#include + +struct light_sensor_data { + /* light levels in lux */ + s32_t red; + s32_t green; + s32_t blue; + s32_t ir; +}; + +typedef void (*light_sensor_data_ready_cb)(void); + +/** + * @brief Initialize and start sampling the light sensor. + * + * @return 0 if the operation was successful, otherwise a (negative) error code. + */ +int light_sensor_init_and_start(const light_sensor_data_ready_cb cb); + +/** + * @brief Get latest sampled light data. + * + * @param data Pointer to memory to store latest light sensor data. + * + * @return 0 if the operation was successful, otherwise a (negative) error code. + */ +int light_sensor_get_data(struct light_sensor_data *const data); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* LIGHT_SENSOR_H_ */ diff --git a/applications/asset_tracker/src/main.c b/applications/asset_tracker/src/main.c index b5b70810e97b..7c7e84ef1e8e 100644 --- a/applications/asset_tracker/src/main.c +++ b/applications/asset_tracker/src/main.c @@ -15,6 +15,7 @@ #include #if defined(CONFIG_BSD_LIBRARY) #include +#include #include #include #endif /* CONFIG_BSD_LIBRARY */ @@ -22,11 +23,16 @@ #include #include +#if defined(CONFIG_BOOTLOADER_MCUBOOT) +#include +#endif + #include "cloud_codec.h" #include "env_sensors.h" #include "orientation_detector.h" #include "ui.h" #include "gps_controller.h" +#include "service_info.h" #define CALIBRATION_PRESS_DURATION K_SECONDS(5) #define CLOUD_CONNACK_WAIT_DURATION K_SECONDS(CONFIG_CLOUD_WAIT_DURATION) @@ -89,11 +95,14 @@ static struct gps_data gps_data; static struct cloud_channel_data flip_cloud_data; static struct cloud_channel_data gps_cloud_data; static struct cloud_channel_data button_cloud_data; +static struct cloud_channel_data device_cloud_data = { + .type = CLOUD_CHANNEL_DEVICE_INFO, + .tag = 0x1 +}; #if CONFIG_MODEM_INFO static struct modem_param_info modem_param; static struct cloud_channel_data signal_strength_cloud_data; -static struct cloud_channel_data device_cloud_data; #endif /* CONFIG_MODEM_INFO */ static atomic_val_t send_data_enable; @@ -108,15 +117,14 @@ static struct k_work send_flip_data_work; static struct k_delayed_work send_env_data_work; static struct k_delayed_work long_press_button_work; static struct k_delayed_work cloud_reboot_work; -#if CONFIG_MODEM_INFO static struct k_work device_status_work; +#if CONFIG_MODEM_INFO static struct k_work rsrp_work; #endif /* CONFIG_MODEM_INFO */ enum error_type { ERROR_CLOUD, ERROR_BSD_RECOVERABLE, - ERROR_BSD_IRRECOVERABLE, ERROR_LTE_LC, ERROR_SYSTEM_FAULT }; @@ -125,12 +133,13 @@ enum error_type { static void app_connect(struct k_work *work); static void flip_send(struct k_work *work); static void env_data_send(void); +#if CONFIG_LIGHT_SENSOR +static void light_sensor_data_send(void); +#endif /* CONFIG_LIGHT_SENSOR */ static void sensors_init(void); static void work_init(void); static void sensor_data_send(struct cloud_channel_data *data); -#if CONFIG_MODEM_INFO static void device_status_send(struct k_work *work); -#endif /**@brief nRF Cloud error handler. */ void error_handler(enum error_type err_type, int err_code) @@ -173,13 +182,6 @@ void error_handler(enum error_type err_type, int err_code) ui_led_set_pattern(UI_LED_ERROR_BSD_REC); printk("Error of type ERROR_BSD_RECOVERABLE: %d\n", err_code); break; - case ERROR_BSD_IRRECOVERABLE: - /* Blinking all LEDs ON/OFF if there is an - * irrecoverable error. - */ - ui_led_set_pattern(UI_LED_ERROR_BSD_IRREC); - printk("Error of type ERROR_BSD_IRRECOVERABLE: %d\n", err_code); - break; default: /* Blinking all LEDs ON/OFF in pairs (1 and 2, 3 and 4) * undefined error. @@ -218,12 +220,6 @@ void bsd_recoverable_error_handler(uint32_t err) error_handler(ERROR_BSD_RECOVERABLE, (int)err); } -/**@brief Irrecoverable BSD library error. */ -void bsd_irrecoverable_error_handler(uint32_t err) -{ - error_handler(ERROR_BSD_IRRECOVERABLE, (int)err); -} - static void send_gps_data_work_fn(struct k_work *work) { sensor_data_send(&gps_cloud_data); @@ -378,6 +374,13 @@ static void modem_rsrp_handler(char rsrp_value) { rsrp.value = rsrp_value; + /* If the RSRP value is 255, it's documented as 'not known or not + * detectable'. Therefore, we should not send those values. + */ + if (rsrp.value == 255) { + return; + } + k_work_submit(&rsrp_work); } @@ -411,12 +414,14 @@ static void modem_rsrp_data_send(struct k_work *work) sensor_data_send(&signal_strength_cloud_data); timestamp_prev = k_uptime_get_32(); } +#endif /* CONFIG_MODEM_INFO */ /**@brief Poll device info and send data to the cloud. */ static void device_status_send(struct k_work *work) { - int len; - int ret; + if (!atomic_get(&send_data_enable)) { + return; + } cJSON *root_obj = cJSON_CreateObject(); @@ -425,25 +430,58 @@ static void device_status_send(struct k_work *work) return; } - if (!atomic_get(&send_data_enable)) { - return; - } + size_t item_cnt = 0; - ret = modem_info_params_get(&modem_param); +#ifdef CONFIG_MODEM_INFO + int ret = modem_info_params_get(&modem_param); if (ret < 0) { printk("Unable to obtain modem parameters: %d\n", ret); - return; + } else { + ret = modem_info_json_object_encode(&modem_param, root_obj); + if (ret > 0) { + item_cnt = (size_t)ret; + } } +#endif /* CONFIG_MODEM_INFO */ + + const char *const ui[] = { + CLOUD_CHANNEL_STR_GPS, + CLOUD_CHANNEL_STR_FLIP, + CLOUD_CHANNEL_STR_TEMP, + CLOUD_CHANNEL_STR_HUMID, + CLOUD_CHANNEL_STR_AIR_PRESS, +#if IS_ENABLED(CONFIG_CLOUD_BUTTON) + CLOUD_CHANNEL_STR_BUTTON, +#endif +#if IS_ENABLED(CONFIG_LIGHT_SENSOR) + CLOUD_CHANNEL_STR_LIGHT_SENSOR, +#endif + }; + + const char *const fota[] = { +#if defined(CONFIG_CLOUD_FOTA_APP) + SERVICE_INFO_FOTA_STR_APP, +#endif +#if defined(CONFIG_CLOUD_FOTA_MODEM) + SERVICE_INFO_FOTA_STR_MODEM +#endif + }; - len = modem_info_json_object_encode(&modem_param, root_obj); + if (service_info_json_object_encode(ui, ARRAY_SIZE(ui), + fota, ARRAY_SIZE(fota), + SERVICE_INFO_FOTA_VER_CURRENT, + root_obj) == 0) { + ++item_cnt; + } - if (len < 0) { + if (item_cnt == 0) { + cJSON_Delete(root_obj); return; } device_cloud_data.data.buf = (char *)root_obj; - device_cloud_data.data.len = len; + device_cloud_data.data.len = item_cnt; device_cloud_data.tag += 1; if (device_cloud_data.tag == 0) { @@ -453,7 +491,6 @@ static void device_status_send(struct k_work *work) /* Transmits the data to the cloud. Frees the JSON object. */ sensor_data_send(&device_cloud_data); } -#endif /* CONFIG_MODEM_INFO */ /**@brief Get environment data from sensors and send to cloud. */ static void env_data_send(void) @@ -524,6 +561,41 @@ static void env_data_send(void) cloud_error_handler(err); } +#if defined(CONFIG_LIGHT_SENSOR) +void light_sensor_data_send(void) +{ + int err; + struct light_sensor_data light_data; + struct cloud_msg msg = { .qos = CLOUD_QOS_AT_MOST_ONCE, + .endpoint.type = CLOUD_EP_TOPIC_MSG }; + + if (!atomic_get(&send_data_enable) || gps_control_is_active()) { + return; + } + + err = light_sensor_get_data(&light_data); + if (err) { + printk("Failed to get light sensor data, error %d\n", err); + return; + } + + err = cloud_encode_light_sensor_data(&light_data, &msg); + if (err) { + printk("Failed to encode light sensor data, error %d\n", err); + return; + } + + err = cloud_send(cloud_backend, &msg); + cloud_release_data(&msg); + + if (err) { + printk("Failed to send light sensor data to cloud, error: %d\n", + err); + cloud_error_handler(err); + } +} +#endif /* CONFIG_LIGHT_SENSOR */ + /**@brief Send sensor data to nRF Cloud. **/ static void sensor_data_send(struct cloud_channel_data *data) { @@ -686,6 +758,12 @@ void cloud_event_handler(const struct cloud_backend *const backend, case CLOUD_EVT_READY: printk("CLOUD_EVT_READY\n"); ui_led_set_pattern(UI_CLOUD_CONNECTED); + +#if defined(CONFIG_BOOTLOADER_MCUBOOT) + /* Mark image as good to avoid rolling back after update */ + boot_write_img_confirmed(); +#endif + sensors_start(); break; case CLOUD_EVT_DISCONNECTED: @@ -710,6 +788,10 @@ void cloud_event_handler(const struct cloud_backend *const backend, printk("CLOUD_EVT_PAIR_DONE\n"); on_pairing_done(); break; + case CLOUD_EVT_FOTA_DONE: + printk("CLOUD_EVT_FOTA_DONE\n"); + sys_reboot(SYS_REBOOT_COLD); + break; default: printk("Unknown cloud event type: %d\n", evt->type); break; @@ -792,8 +874,8 @@ static void work_init(void) k_delayed_work_init(&send_env_data_work, send_env_data_work_fn); k_delayed_work_init(&long_press_button_work, long_press_handler); k_delayed_work_init(&cloud_reboot_work, cloud_reboot_handler); -#if CONFIG_MODEM_INFO k_work_init(&device_status_work, device_status_send); +#if CONFIG_MODEM_INFO k_work_init(&rsrp_work, modem_rsrp_data_send); #endif /* CONFIG_MODEM_INFO */ } @@ -909,11 +991,6 @@ static void modem_data_init(void) signal_strength_cloud_data.type = CLOUD_CHANNEL_LTE_LINK_RSRP; signal_strength_cloud_data.tag = 0x1; - device_cloud_data.type = CLOUD_CHANNEL_DEVICE_INFO; - device_cloud_data.tag = 0x1; - - k_work_submit(&device_status_work); - modem_info_rsrp_register(modem_rsrp_handler); } #endif /* CONFIG_MODEM_INFO */ @@ -921,13 +998,26 @@ static void modem_data_init(void) /**@brief Initializes the sensors that are used by the application. */ static void sensors_init(void) { + int err; + accelerometer_init(); flip_detection_init(); - env_sensors_init_and_start(); - + err = env_sensors_init_and_start(); + if (err) { + printk("Environmental sensors init failed, error: %d\n", err); + } +#if CONFIG_LIGHT_SENSOR + err = light_sensor_init_and_start(light_sensor_data_send); + if (err) { + printk("Light sensor init failed, error: %d\n", err); + } +#endif /* CONFIG_LIGHT_SENSOR */ #if CONFIG_MODEM_INFO modem_data_init(); #endif /* CONFIG_MODEM_INFO */ + + k_work_submit(&device_status_work); + if (IS_ENABLED(CONFIG_CLOUD_BUTTON)) { button_sensor_init(); } @@ -999,12 +1089,41 @@ static void ui_evt_handler(struct ui_evt evt) } #endif /* defined(CONFIG_USE_UI_MODULE) */ +void handle_bsdlib_init_ret(void) +{ + #if defined(CONFIG_BSD_LIBRARY) + int ret = bsdlib_get_init_ret(); + + /* Handle return values relating to modem firmware update */ + switch (ret) { + case MODEM_DFU_RESULT_OK: + printk("MODEM UPDATE OK. Will run new firmware\n"); + sys_reboot(SYS_REBOOT_COLD); + break; + case MODEM_DFU_RESULT_UUID_ERROR: + case MODEM_DFU_RESULT_AUTH_ERROR: + printk("MODEM UPDATE ERROR %d. Will run old firmware\n", ret); + sys_reboot(SYS_REBOOT_COLD); + break; + case MODEM_DFU_RESULT_HARDWARE_ERROR: + case MODEM_DFU_RESULT_INTERNAL_ERROR: + printk("MODEM UPDATE FATAL ERROR %d. Modem failiure\n", ret); + sys_reboot(SYS_REBOOT_COLD); + break; + default: + break; + } + #endif /* CONFIG_BSD_LIBRARY */ +} + void main(void) { int ret; printk("Asset tracker started\n"); + handle_bsdlib_init_ret(); + cloud_backend = cloud_get_binding("NRF_CLOUD"); __ASSERT(cloud_backend != NULL, "nRF Cloud backend not found"); diff --git a/applications/nrf_desktop/configuration/common/led_state.h b/applications/nrf_desktop/configuration/common/led_state.h index e80831fa08af..116663ffaea5 100644 --- a/applications/nrf_desktop/configuration/common/led_state.h +++ b/applications/nrf_desktop/configuration/common/led_state.h @@ -31,6 +31,7 @@ enum led_system_state { enum led_peer_state { LED_PEER_STATE_DISCONNECTED, LED_PEER_STATE_CONNECTED, + LED_PEER_STATE_PEER_SEARCH, LED_PEER_STATE_CONFIRM_SELECT, LED_PEER_STATE_CONFIRM_ERASE, LED_PEER_STATE_ERASE_ADV, diff --git a/applications/nrf_desktop/configuration/nrf52810_pca20045/app_ZDebug.conf b/applications/nrf_desktop/configuration/nrf52810_pca20045/app_ZDebug.conf index 049f7c1aa1e0..6cec5b201589 100644 --- a/applications/nrf_desktop/configuration/nrf52810_pca20045/app_ZDebug.conf +++ b/applications/nrf_desktop/configuration/nrf52810_pca20045/app_ZDebug.conf @@ -137,6 +137,8 @@ CONFIG_BT_PERIPHERAL_PREF_MAX_INT=6 CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY=99 CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400 +CONFIG_BT_WHITELIST=y + CONFIG_BT_GATT_DIS=y CONFIG_BT_GATT_DIS_MANUF="Nordic Semiconductor ASA" CONFIG_BT_GATT_DIS_MODEL="Mouse nRF52 Desktop" @@ -155,6 +157,7 @@ CONFIG_BT_GATT_HIDS_ATTR_MAX=19 CONFIG_BT_GATT_HIDS_DEFAULT_PERM_RW_ENCRYPT=y CONFIG_BT_CONN_CTX=y +CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT=1000 CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/configuration/nrf52810_pca20045/app_ZRelease.conf b/applications/nrf_desktop/configuration/nrf52810_pca20045/app_ZRelease.conf index aba864681b21..94bf5cc43092 100644 --- a/applications/nrf_desktop/configuration/nrf52810_pca20045/app_ZRelease.conf +++ b/applications/nrf_desktop/configuration/nrf52810_pca20045/app_ZRelease.conf @@ -130,6 +130,8 @@ CONFIG_BT_PERIPHERAL_PREF_MAX_INT=6 CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY=99 CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400 +CONFIG_BT_WHITELIST=y + CONFIG_BT_GATT_DIS=y CONFIG_BT_GATT_DIS_MANUF="Nordic Semiconductor ASA" CONFIG_BT_GATT_DIS_MODEL="Mouse nRF52 Desktop" @@ -148,6 +150,7 @@ CONFIG_BT_GATT_HIDS_ATTR_MAX=19 CONFIG_BT_GATT_HIDS_DEFAULT_PERM_RW_ENCRYPT=y CONFIG_BT_CONN_CTX=y +CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT=1000 CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/configuration/nrf52840_pca10056/app_ZDebug.conf b/applications/nrf_desktop/configuration/nrf52840_pca10056/app_ZDebug.conf index 962477b61f5e..aa0eeb0da5c1 100644 --- a/applications/nrf_desktop/configuration/nrf52840_pca10056/app_ZDebug.conf +++ b/applications/nrf_desktop/configuration/nrf52840_pca10056/app_ZDebug.conf @@ -109,6 +109,8 @@ CONFIG_BT_PERIPHERAL_PREF_MAX_INT=6 CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY=99 CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400 +CONFIG_BT_WHITELIST=y + CONFIG_BT_GATT_DIS=y CONFIG_BT_GATT_DIS_MANUF="Nordic Semiconductor ASA" CONFIG_BT_GATT_DIS_MODEL="Mouse nRF52 Desktop" @@ -125,6 +127,9 @@ CONFIG_BT_GATT_HIDS_ATTR_MAX=45 CONFIG_BT_GATT_HIDS_DEFAULT_PERM_RW_ENCRYPT=y CONFIG_BT_CONN_CTX=y +CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT=1000 + +CONFIG_ENTROPY_CC310=n CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/configuration/nrf52840_pca10056/app_ZDebugWithShell.conf b/applications/nrf_desktop/configuration/nrf52840_pca10056/app_ZDebugWithShell.conf index 2ae2bab34c30..fdaa2e55eacf 100644 --- a/applications/nrf_desktop/configuration/nrf52840_pca10056/app_ZDebugWithShell.conf +++ b/applications/nrf_desktop/configuration/nrf52840_pca10056/app_ZDebugWithShell.conf @@ -113,6 +113,8 @@ CONFIG_BT_PERIPHERAL_PREF_MAX_INT=6 CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY=99 CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400 +CONFIG_BT_WHITELIST=y + CONFIG_BT_GATT_DIS=y CONFIG_BT_GATT_DIS_MANUF="Nordic Semiconductor ASA" CONFIG_BT_GATT_DIS_MODEL="Mouse nRF52 Desktop" @@ -129,6 +131,9 @@ CONFIG_BT_GATT_HIDS_ATTR_MAX=45 CONFIG_BT_GATT_HIDS_DEFAULT_PERM_RW_ENCRYPT=y CONFIG_BT_CONN_CTX=y +CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT=1000 + +CONFIG_ENTROPY_CC310=n CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/configuration/nrf52840_pca10056/app_ZRelease.conf b/applications/nrf_desktop/configuration/nrf52840_pca10056/app_ZRelease.conf index 467b3168d30f..9c84c2b865af 100644 --- a/applications/nrf_desktop/configuration/nrf52840_pca10056/app_ZRelease.conf +++ b/applications/nrf_desktop/configuration/nrf52840_pca10056/app_ZRelease.conf @@ -105,6 +105,8 @@ CONFIG_BT_PERIPHERAL_PREF_MAX_INT=6 CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY=99 CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400 +CONFIG_BT_WHITELIST=y + CONFIG_BT_GATT_DIS=y CONFIG_BT_GATT_DIS_MANUF="Nordic Semiconductor ASA" CONFIG_BT_GATT_DIS_MODEL="Mouse nRF52 Desktop" @@ -121,6 +123,9 @@ CONFIG_BT_GATT_HIDS_ATTR_MAX=45 CONFIG_BT_GATT_HIDS_DEFAULT_PERM_RW_ENCRYPT=y CONFIG_BT_CONN_CTX=y +CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT=1000 + +CONFIG_ENTROPY_CC310=n CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/configuration/nrf52840_pca10056/led_state_def.h b/applications/nrf_desktop/configuration/nrf52840_pca10056/led_state_def.h index 8e6a58947f45..9c166cf0d49f 100644 --- a/applications/nrf_desktop/configuration/nrf52840_pca10056/led_state_def.h +++ b/applications/nrf_desktop/configuration/nrf52840_pca10056/led_state_def.h @@ -31,8 +31,9 @@ static const struct led_effect led_system_state_effect[LED_SYSTEM_STATE_COUNT] = static const struct led_effect led_peer_state_effect[LED_PEER_CNT][LED_PEER_STATE_COUNT] = { { - [LED_PEER_STATE_DISCONNECTED] = LED_EFFECT_LED_BREATH(1000, LED_COLOR(100)), + [LED_PEER_STATE_DISCONNECTED] = LED_EFFECT_LED_OFF(), [LED_PEER_STATE_CONNECTED] = LED_EFFECT_LED_ON(LED_COLOR(100)), + [LED_PEER_STATE_PEER_SEARCH] = LED_EFFECT_LED_BREATH(1000, LED_COLOR(100)), [LED_PEER_STATE_CONFIRM_SELECT] = LED_EFFECT_LED_BLINK(50, LED_COLOR(100)), [LED_PEER_STATE_CONFIRM_ERASE] = LED_EFFECT_LED_BLINK(25, LED_COLOR(100)), [LED_PEER_STATE_ERASE_ADV] = LED_EFFECT_LED_BREATH(100, LED_COLOR(100)), diff --git a/applications/nrf_desktop/configuration/nrf52840_pca10059/app_ZDebug.conf b/applications/nrf_desktop/configuration/nrf52840_pca10059/app_ZDebug.conf index 7c75ee82b420..eb9d78f9dfbf 100644 --- a/applications/nrf_desktop/configuration/nrf52840_pca10059/app_ZDebug.conf +++ b/applications/nrf_desktop/configuration/nrf52840_pca10059/app_ZDebug.conf @@ -29,6 +29,8 @@ CONFIG_DESKTOP_USB_ENABLE=y CONFIG_DESKTOP_BLE_PEER_CONTROL=y CONFIG_DESKTOP_BLE_PEER_CONTROL_BUTTON=0x0000 +CONFIG_DESKTOP_BLE_NEW_PEER_SCAN_REQUEST=y +CONFIG_DESKTOP_BLE_NEW_PEER_SCAN_ON_BOOT=y CONFIG_DESKTOP_BLE_PEER_ERASE=y CONFIG_DESKTOP_BLE_SCANNING_ENABLE=y @@ -40,17 +42,20 @@ CONFIG_DESKTOP_BLE_DISCOVERY_ENABLE=y CONFIG_DESKTOP_CONFIG_ENABLE=y CONFIG_DESKTOP_CONFIG_CHANNEL_ENABLE=y +CONFIG_DESKTOP_BLE_QOS_ENABLE=y + ################################################################################ # Zephyr Configuration CONFIG_TEXT_SECTION_OFFSET=0 CONFIG_BOARD_HAS_NRF5_BOOTLOADER=n -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1280 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1536 CONFIG_ISR_STACK_SIZE=1280 CONFIG_MAIN_STACK_SIZE=768 CONFIG_IDLE_STACK_SIZE=128 CONFIG_BT_RX_STACK_SIZE=2048 +CONFIG_BT_HCI_TX_STACK_SIZE=1536 CONFIG_BOOT_BANNER=n @@ -120,8 +125,6 @@ CONFIG_BT_CTLR_PRIVACY=n CONFIG_BT_CTLR_ADV_EXT=n CONFIG_BT_CTLR_TX_PWR_PLUS_8=y -CONFIG_BT_TICKER_COMPATIBILITY_MODE=y - CONFIG_BT_CENTRAL=y CONFIG_BT_SCAN=y @@ -136,6 +139,12 @@ CONFIG_BT_GATT_DM=y CONFIG_BT_GATT_HIDS_C=y CONFIG_BT_GATT_HIDS_C_REPORTS_MAX=9 +CONFIG_BT_LL_NRFXLIB=y +CONFIG_BLECTRL_MASTER_COUNT=2 +CONFIG_BLECTRL_SLAVE_COUNT=0 + +CONFIG_ENTROPY_CC310=n + CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/configuration/nrf52840_pca10059/app_ZDebugMCUBoot.conf b/applications/nrf_desktop/configuration/nrf52840_pca10059/app_ZDebugMCUBoot.conf index 7be1c2a88522..b0f462d169d9 100644 --- a/applications/nrf_desktop/configuration/nrf52840_pca10059/app_ZDebugMCUBoot.conf +++ b/applications/nrf_desktop/configuration/nrf52840_pca10059/app_ZDebugMCUBoot.conf @@ -29,6 +29,8 @@ CONFIG_DESKTOP_USB_ENABLE=y CONFIG_DESKTOP_BLE_PEER_CONTROL=y CONFIG_DESKTOP_BLE_PEER_CONTROL_BUTTON=0x0000 +CONFIG_DESKTOP_BLE_NEW_PEER_SCAN_REQUEST=y +CONFIG_DESKTOP_BLE_NEW_PEER_SCAN_ON_BOOT=y CONFIG_DESKTOP_BLE_PEER_ERASE=y CONFIG_DESKTOP_BLE_SCANNING_ENABLE=y @@ -41,17 +43,20 @@ CONFIG_DESKTOP_CONFIG_ENABLE=y CONFIG_DESKTOP_CONFIG_CHANNEL_ENABLE=y CONFIG_DESKTOP_CONFIG_CHANNEL_DFU_ENABLE=y +CONFIG_DESKTOP_BLE_QOS_ENABLE=y + ################################################################################ # Zephyr Configuration CONFIG_TEXT_SECTION_OFFSET=0 CONFIG_BOARD_HAS_NRF5_BOOTLOADER=n -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1280 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1536 CONFIG_ISR_STACK_SIZE=1280 CONFIG_MAIN_STACK_SIZE=768 CONFIG_IDLE_STACK_SIZE=128 CONFIG_BT_RX_STACK_SIZE=2048 +CONFIG_BT_HCI_TX_STACK_SIZE=1536 CONFIG_BOOT_BANNER=n @@ -121,8 +126,6 @@ CONFIG_BT_CTLR_PRIVACY=n CONFIG_BT_CTLR_ADV_EXT=n CONFIG_BT_CTLR_TX_PWR_PLUS_8=y -CONFIG_BT_TICKER_COMPATIBILITY_MODE=y - CONFIG_BT_CENTRAL=y CONFIG_BT_SCAN=y @@ -137,6 +140,12 @@ CONFIG_BT_GATT_DM=y CONFIG_BT_GATT_HIDS_C=y CONFIG_BT_GATT_HIDS_C_REPORTS_MAX=9 +CONFIG_BT_LL_NRFXLIB=y +CONFIG_BLECTRL_MASTER_COUNT=2 +CONFIG_BLECTRL_SLAVE_COUNT=0 + +CONFIG_ENTROPY_CC310=n + CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/configuration/nrf52840_pca10059/app_ZDebugWithShell.conf b/applications/nrf_desktop/configuration/nrf52840_pca10059/app_ZDebugWithShell.conf index c9f88389b354..743400487e95 100644 --- a/applications/nrf_desktop/configuration/nrf52840_pca10059/app_ZDebugWithShell.conf +++ b/applications/nrf_desktop/configuration/nrf52840_pca10059/app_ZDebugWithShell.conf @@ -29,6 +29,8 @@ CONFIG_DESKTOP_USB_ENABLE=y CONFIG_DESKTOP_BLE_PEER_CONTROL=y CONFIG_DESKTOP_BLE_PEER_CONTROL_BUTTON=0x0000 +CONFIG_DESKTOP_BLE_NEW_PEER_SCAN_REQUEST=y +CONFIG_DESKTOP_BLE_NEW_PEER_SCAN_ON_BOOT=y CONFIG_DESKTOP_BLE_PEER_ERASE=y CONFIG_DESKTOP_BLE_SCANNING_ENABLE=y @@ -40,6 +42,8 @@ CONFIG_DESKTOP_BLE_DISCOVERY_ENABLE=y CONFIG_DESKTOP_CONFIG_ENABLE=y CONFIG_DESKTOP_CONFIG_CHANNEL_ENABLE=y +CONFIG_DESKTOP_BLE_QOS_ENABLE=y + CONFIG_DESKTOP_SHELL_ENABLE=y CONFIG_DESKTOP_DEVICE_SHELL=y CONFIG_DESKTOP_KERNEL_SHELL=y @@ -50,11 +54,12 @@ CONFIG_DESKTOP_KERNEL_SHELL=y CONFIG_TEXT_SECTION_OFFSET=0 CONFIG_BOARD_HAS_NRF5_BOOTLOADER=n -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1280 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1536 CONFIG_ISR_STACK_SIZE=1280 CONFIG_MAIN_STACK_SIZE=768 CONFIG_IDLE_STACK_SIZE=128 CONFIG_BT_RX_STACK_SIZE=2048 +CONFIG_BT_HCI_TX_STACK_SIZE=1536 CONFIG_BOOT_BANNER=n @@ -124,8 +129,6 @@ CONFIG_BT_CTLR_PRIVACY=n CONFIG_BT_CTLR_ADV_EXT=n CONFIG_BT_CTLR_TX_PWR_PLUS_8=y -CONFIG_BT_TICKER_COMPATIBILITY_MODE=y - CONFIG_BT_CENTRAL=y CONFIG_BT_SCAN=y @@ -140,6 +143,12 @@ CONFIG_BT_GATT_DM=y CONFIG_BT_GATT_HIDS_C=y CONFIG_BT_GATT_HIDS_C_REPORTS_MAX=9 +CONFIG_BT_LL_NRFXLIB=y +CONFIG_BLECTRL_MASTER_COUNT=2 +CONFIG_BLECTRL_SLAVE_COUNT=0 + +CONFIG_ENTROPY_CC310=n + CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/configuration/nrf52840_pca10059/app_ZRelease.conf b/applications/nrf_desktop/configuration/nrf52840_pca10059/app_ZRelease.conf index 381a877ddacc..95b07890279c 100644 --- a/applications/nrf_desktop/configuration/nrf52840_pca10059/app_ZRelease.conf +++ b/applications/nrf_desktop/configuration/nrf52840_pca10059/app_ZRelease.conf @@ -25,6 +25,8 @@ CONFIG_DESKTOP_USB_ENABLE=y CONFIG_DESKTOP_BLE_PEER_CONTROL=y CONFIG_DESKTOP_BLE_PEER_CONTROL_BUTTON=0x0000 +CONFIG_DESKTOP_BLE_NEW_PEER_SCAN_REQUEST=y +CONFIG_DESKTOP_BLE_NEW_PEER_SCAN_ON_BOOT=y CONFIG_DESKTOP_BLE_PEER_ERASE=y CONFIG_DESKTOP_BLE_SCANNING_ENABLE=y @@ -38,17 +40,20 @@ CONFIG_DESKTOP_WATCHDOG_ENABLE=y CONFIG_DESKTOP_CONFIG_ENABLE=y CONFIG_DESKTOP_CONFIG_CHANNEL_ENABLE=y +CONFIG_DESKTOP_BLE_QOS_ENABLE=y + ################################################################################ # Zephyr Configuration CONFIG_TEXT_SECTION_OFFSET=0 CONFIG_BOARD_HAS_NRF5_BOOTLOADER=n -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1280 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1536 CONFIG_ISR_STACK_SIZE=1280 CONFIG_MAIN_STACK_SIZE=768 CONFIG_IDLE_STACK_SIZE=128 CONFIG_BT_RX_STACK_SIZE=2048 +CONFIG_BT_HCI_TX_STACK_SIZE=1536 CONFIG_BOOT_BANNER=n @@ -114,8 +119,6 @@ CONFIG_BT_CTLR_PRIVACY=n CONFIG_BT_CTLR_ADV_EXT=n CONFIG_BT_CTLR_TX_PWR_PLUS_8=y -CONFIG_BT_TICKER_COMPATIBILITY_MODE=y - CONFIG_BT_CENTRAL=y CONFIG_BT_SCAN=y @@ -130,6 +133,12 @@ CONFIG_BT_GATT_DM=y CONFIG_BT_GATT_HIDS_C=y CONFIG_BT_GATT_HIDS_C_REPORTS_MAX=9 +CONFIG_BT_LL_NRFXLIB=y +CONFIG_BLECTRL_MASTER_COUNT=2 +CONFIG_BLECTRL_SLAVE_COUNT=0 + +CONFIG_ENTROPY_CC310=n + CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/configuration/nrf52840_pca10059/app_ZReleaseMCUBoot.conf b/applications/nrf_desktop/configuration/nrf52840_pca10059/app_ZReleaseMCUBoot.conf index d2ff5c5be4fc..79fa7ccd65c4 100644 --- a/applications/nrf_desktop/configuration/nrf52840_pca10059/app_ZReleaseMCUBoot.conf +++ b/applications/nrf_desktop/configuration/nrf52840_pca10059/app_ZReleaseMCUBoot.conf @@ -25,6 +25,8 @@ CONFIG_DESKTOP_USB_ENABLE=y CONFIG_DESKTOP_BLE_PEER_CONTROL=y CONFIG_DESKTOP_BLE_PEER_CONTROL_BUTTON=0x0000 +CONFIG_DESKTOP_BLE_NEW_PEER_SCAN_REQUEST=y +CONFIG_DESKTOP_BLE_NEW_PEER_SCAN_ON_BOOT=y CONFIG_DESKTOP_BLE_PEER_ERASE=y CONFIG_DESKTOP_BLE_SCANNING_ENABLE=y @@ -39,17 +41,20 @@ CONFIG_DESKTOP_CONFIG_ENABLE=y CONFIG_DESKTOP_CONFIG_CHANNEL_ENABLE=y CONFIG_DESKTOP_CONFIG_CHANNEL_DFU_ENABLE=y +CONFIG_DESKTOP_BLE_QOS_ENABLE=y + ################################################################################ # Zephyr Configuration CONFIG_TEXT_SECTION_OFFSET=0 CONFIG_BOARD_HAS_NRF5_BOOTLOADER=n -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1280 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1536 CONFIG_ISR_STACK_SIZE=1280 CONFIG_MAIN_STACK_SIZE=768 CONFIG_IDLE_STACK_SIZE=128 CONFIG_BT_RX_STACK_SIZE=2048 +CONFIG_BT_HCI_TX_STACK_SIZE=1536 CONFIG_BOOT_BANNER=n @@ -115,8 +120,6 @@ CONFIG_BT_CTLR_PRIVACY=n CONFIG_BT_CTLR_ADV_EXT=n CONFIG_BT_CTLR_TX_PWR_PLUS_8=y -CONFIG_BT_TICKER_COMPATIBILITY_MODE=y - CONFIG_BT_CENTRAL=y CONFIG_BT_SCAN=y @@ -131,6 +134,12 @@ CONFIG_BT_GATT_DM=y CONFIG_BT_GATT_HIDS_C=y CONFIG_BT_GATT_HIDS_C_REPORTS_MAX=9 +CONFIG_BT_LL_NRFXLIB=y +CONFIG_BLECTRL_MASTER_COUNT=2 +CONFIG_BLECTRL_SLAVE_COUNT=0 + +CONFIG_ENTROPY_CC310=n + CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/configuration/nrf52840_pca10059/dts.overlay b/applications/nrf_desktop/configuration/nrf52840_pca10059/dts.overlay index 641137037603..ba107ab204e1 100644 --- a/applications/nrf_desktop/configuration/nrf52840_pca10059/dts.overlay +++ b/applications/nrf_desktop/configuration/nrf52840_pca10059/dts.overlay @@ -2,7 +2,7 @@ compatible = "nordic,nrf-usbd"; status = "okay"; num-bidir-endpoints = <0>; - num-in-endpoints = <2>; + num-in-endpoints = <4>; num-out-endpoints = <2>; num-isoin-endpoints = <0>; num-isoout-endpoints = <0>; diff --git a/applications/nrf_desktop/configuration/nrf52840_pca10059/led_state_def.h b/applications/nrf_desktop/configuration/nrf52840_pca10059/led_state_def.h index dada8bd4e85d..37a1a598852f 100644 --- a/applications/nrf_desktop/configuration/nrf52840_pca10059/led_state_def.h +++ b/applications/nrf_desktop/configuration/nrf52840_pca10059/led_state_def.h @@ -31,15 +31,17 @@ static const struct led_effect led_system_state_effect[LED_SYSTEM_STATE_COUNT] = static const struct led_effect led_peer_state_effect[LED_PEER_CNT][LED_PEER_STATE_COUNT] = { { - [LED_PEER_STATE_DISCONNECTED] = LED_EFFECT_LED_BREATH(1000, LED_COLOR(100)), + [LED_PEER_STATE_DISCONNECTED] = LED_EFFECT_LED_OFF(), [LED_PEER_STATE_CONNECTED] = LED_EFFECT_LED_ON(LED_COLOR(100)), + [LED_PEER_STATE_PEER_SEARCH] = LED_EFFECT_LED_BREATH(500, LED_COLOR(100)), [LED_PEER_STATE_CONFIRM_SELECT] = LED_EFFECT_LED_BLINK(50, LED_COLOR(100)), [LED_PEER_STATE_CONFIRM_ERASE] = LED_EFFECT_LED_BLINK(25, LED_COLOR(100)), [LED_PEER_STATE_ERASE_ADV] = LED_EFFECT_LED_BREATH(100, LED_COLOR(100)), }, { - [LED_PEER_STATE_DISCONNECTED] = LED_EFFECT_LED_BREATH(1000, LED_COLOR(100)), + [LED_PEER_STATE_DISCONNECTED] = LED_EFFECT_LED_OFF(), [LED_PEER_STATE_CONNECTED] = LED_EFFECT_LED_ON(LED_COLOR(100)), + [LED_PEER_STATE_PEER_SEARCH] = LED_EFFECT_LED_BREATH(500, LED_COLOR(100)), [LED_PEER_STATE_CONFIRM_SELECT] = LED_EFFECT_LED_BLINK(50, LED_COLOR(100)), [LED_PEER_STATE_CONFIRM_ERASE] = LED_EFFECT_LED_BLINK(25, LED_COLOR(100)), [LED_PEER_STATE_ERASE_ADV] = LED_EFFECT_LED_BREATH(100, LED_COLOR(100)), diff --git a/applications/nrf_desktop/configuration/nrf52840_pca20041/app_ZDebug.conf b/applications/nrf_desktop/configuration/nrf52840_pca20041/app_ZDebug.conf index 39973bde6e98..c4589836d6db 100644 --- a/applications/nrf_desktop/configuration/nrf52840_pca20041/app_ZDebug.conf +++ b/applications/nrf_desktop/configuration/nrf52840_pca20041/app_ZDebug.conf @@ -83,11 +83,12 @@ CONFIG_DESKTOP_CONFIG_CHANNEL_ENABLE=y ################################################################################ # Zephyr Configuration -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1280 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1536 CONFIG_ISR_STACK_SIZE=1536 CONFIG_MAIN_STACK_SIZE=768 CONFIG_IDLE_STACK_SIZE=512 CONFIG_BT_RX_STACK_SIZE=2048 +CONFIG_BT_HCI_TX_STACK_SIZE=1536 CONFIG_BOOT_BANNER=n @@ -172,8 +173,6 @@ CONFIG_BT_CTLR_CONN_PARAM_REQ=n CONFIG_BT_CTLR_PRIVACY=n CONFIG_BT_CTLR_TX_PWR_PLUS_8=y -CONFIG_BT_TICKER_COMPATIBILITY_MODE=y - CONFIG_BT_DATA_LEN_UPDATE=n CONFIG_BT_PERIPHERAL=y @@ -182,6 +181,8 @@ CONFIG_BT_PERIPHERAL_PREF_MAX_INT=6 CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY=99 CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400 +CONFIG_BT_WHITELIST=y + CONFIG_BT_GATT_DIS=y CONFIG_BT_GATT_DIS_MANUF="Nordic Semiconductor ASA" CONFIG_BT_GATT_DIS_MODEL="Mouse nRF52 Desktop" @@ -200,6 +201,13 @@ CONFIG_BT_GATT_HIDS_ATTR_MAX=19 CONFIG_BT_GATT_HIDS_DEFAULT_PERM_RW_ENCRYPT=y CONFIG_BT_CONN_CTX=y +CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT=1000 + +CONFIG_BT_LL_NRFXLIB=y +CONFIG_BLECTRL_MASTER_COUNT=0 +CONFIG_BLECTRL_SLAVE_COUNT=1 + +CONFIG_ENTROPY_CC310=n CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/configuration/nrf52840_pca20041/app_ZDebugMCUBoot.conf b/applications/nrf_desktop/configuration/nrf52840_pca20041/app_ZDebugMCUBoot.conf index 03e2e67e14d1..0cbc8452dbf8 100644 --- a/applications/nrf_desktop/configuration/nrf52840_pca20041/app_ZDebugMCUBoot.conf +++ b/applications/nrf_desktop/configuration/nrf52840_pca20041/app_ZDebugMCUBoot.conf @@ -84,11 +84,12 @@ CONFIG_DESKTOP_CONFIG_CHANNEL_DFU_ENABLE=y ################################################################################ # Zephyr Configuration -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1280 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1536 CONFIG_ISR_STACK_SIZE=1536 CONFIG_MAIN_STACK_SIZE=768 CONFIG_IDLE_STACK_SIZE=512 CONFIG_BT_RX_STACK_SIZE=2048 +CONFIG_BT_HCI_TX_STACK_SIZE=1536 CONFIG_BOOT_BANNER=n @@ -173,8 +174,6 @@ CONFIG_BT_CTLR_CONN_PARAM_REQ=n CONFIG_BT_CTLR_PRIVACY=n CONFIG_BT_CTLR_TX_PWR_PLUS_8=y -CONFIG_BT_TICKER_COMPATIBILITY_MODE=y - CONFIG_BT_DATA_LEN_UPDATE=n CONFIG_BT_PERIPHERAL=y @@ -183,6 +182,8 @@ CONFIG_BT_PERIPHERAL_PREF_MAX_INT=6 CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY=99 CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400 +CONFIG_BT_WHITELIST=y + CONFIG_BT_GATT_DIS=y CONFIG_BT_GATT_DIS_MANUF="Nordic Semiconductor ASA" CONFIG_BT_GATT_DIS_MODEL="Mouse nRF52 Desktop" @@ -201,6 +202,13 @@ CONFIG_BT_GATT_HIDS_ATTR_MAX=19 CONFIG_BT_GATT_HIDS_DEFAULT_PERM_RW_ENCRYPT=y CONFIG_BT_CONN_CTX=y +CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT=1000 + +CONFIG_BT_LL_NRFXLIB=y +CONFIG_BLECTRL_MASTER_COUNT=0 +CONFIG_BLECTRL_SLAVE_COUNT=1 + +CONFIG_ENTROPY_CC310=n CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/configuration/nrf52840_pca20041/app_ZDebugWithShell.conf b/applications/nrf_desktop/configuration/nrf52840_pca20041/app_ZDebugWithShell.conf index da4692a3e0ff..91acf97cf292 100644 --- a/applications/nrf_desktop/configuration/nrf52840_pca20041/app_ZDebugWithShell.conf +++ b/applications/nrf_desktop/configuration/nrf52840_pca20041/app_ZDebugWithShell.conf @@ -87,11 +87,12 @@ CONFIG_DESKTOP_KERNEL_SHELL=y ################################################################################ # Zephyr Configuration -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1280 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1536 CONFIG_ISR_STACK_SIZE=1536 CONFIG_MAIN_STACK_SIZE=768 CONFIG_IDLE_STACK_SIZE=512 CONFIG_BT_RX_STACK_SIZE=2048 +CONFIG_BT_HCI_TX_STACK_SIZE=1536 CONFIG_BOOT_BANNER=n @@ -176,8 +177,6 @@ CONFIG_BT_CTLR_CONN_PARAM_REQ=n CONFIG_BT_CTLR_PRIVACY=n CONFIG_BT_CTLR_TX_PWR_PLUS_8=y -CONFIG_BT_TICKER_COMPATIBILITY_MODE=y - CONFIG_BT_DATA_LEN_UPDATE=n CONFIG_BT_PERIPHERAL=y @@ -186,6 +185,8 @@ CONFIG_BT_PERIPHERAL_PREF_MAX_INT=6 CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY=99 CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400 +CONFIG_BT_WHITELIST=y + CONFIG_BT_GATT_DIS=y CONFIG_BT_GATT_DIS_MANUF="Nordic Semiconductor ASA" CONFIG_BT_GATT_DIS_MODEL="Mouse nRF52 Desktop" @@ -204,6 +205,13 @@ CONFIG_BT_GATT_HIDS_ATTR_MAX=19 CONFIG_BT_GATT_HIDS_DEFAULT_PERM_RW_ENCRYPT=y CONFIG_BT_CONN_CTX=y +CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT=1000 + +CONFIG_BT_LL_NRFXLIB=y +CONFIG_BLECTRL_MASTER_COUNT=0 +CONFIG_BLECTRL_SLAVE_COUNT=1 + +CONFIG_ENTROPY_CC310=n CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/configuration/nrf52840_pca20041/app_ZRelease.conf b/applications/nrf_desktop/configuration/nrf52840_pca20041/app_ZRelease.conf index 27021c813f0e..fb510c642acf 100644 --- a/applications/nrf_desktop/configuration/nrf52840_pca20041/app_ZRelease.conf +++ b/applications/nrf_desktop/configuration/nrf52840_pca20041/app_ZRelease.conf @@ -78,11 +78,12 @@ CONFIG_DESKTOP_CONFIG_CHANNEL_ENABLE=y ################################################################################ # Zephyr Configuration -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1280 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1536 CONFIG_ISR_STACK_SIZE=1536 CONFIG_MAIN_STACK_SIZE=768 CONFIG_IDLE_STACK_SIZE=128 CONFIG_BT_RX_STACK_SIZE=2048 +CONFIG_BT_HCI_TX_STACK_SIZE=1536 CONFIG_BOOT_BANNER=n @@ -163,8 +164,6 @@ CONFIG_BT_CTLR_CONN_PARAM_REQ=n CONFIG_BT_CTLR_PRIVACY=n CONFIG_BT_CTLR_TX_PWR_PLUS_8=y -CONFIG_BT_TICKER_COMPATIBILITY_MODE=y - CONFIG_BT_DATA_LEN_UPDATE=n CONFIG_BT_PERIPHERAL=y @@ -173,6 +172,8 @@ CONFIG_BT_PERIPHERAL_PREF_MAX_INT=6 CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY=99 CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400 +CONFIG_BT_WHITELIST=y + CONFIG_BT_GATT_DIS=y CONFIG_BT_GATT_DIS_MANUF="Nordic Semiconductor ASA" CONFIG_BT_GATT_DIS_MODEL="Mouse nRF52 Desktop" @@ -191,6 +192,13 @@ CONFIG_BT_GATT_HIDS_ATTR_MAX=19 CONFIG_BT_GATT_HIDS_DEFAULT_PERM_RW_ENCRYPT=y CONFIG_BT_CONN_CTX=y +CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT=1000 + +CONFIG_BT_LL_NRFXLIB=y +CONFIG_BLECTRL_MASTER_COUNT=0 +CONFIG_BLECTRL_SLAVE_COUNT=1 + +CONFIG_ENTROPY_CC310=n CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/configuration/nrf52840_pca20041/app_ZReleaseMCUBoot.conf b/applications/nrf_desktop/configuration/nrf52840_pca20041/app_ZReleaseMCUBoot.conf index 1f8ef3365220..6e532a8905bc 100644 --- a/applications/nrf_desktop/configuration/nrf52840_pca20041/app_ZReleaseMCUBoot.conf +++ b/applications/nrf_desktop/configuration/nrf52840_pca20041/app_ZReleaseMCUBoot.conf @@ -79,11 +79,12 @@ CONFIG_DESKTOP_CONFIG_CHANNEL_DFU_ENABLE=y ################################################################################ # Zephyr Configuration -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1280 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1536 CONFIG_ISR_STACK_SIZE=1536 CONFIG_MAIN_STACK_SIZE=768 CONFIG_IDLE_STACK_SIZE=128 CONFIG_BT_RX_STACK_SIZE=2048 +CONFIG_BT_HCI_TX_STACK_SIZE=1536 CONFIG_BOOT_BANNER=n @@ -164,8 +165,6 @@ CONFIG_BT_CTLR_CONN_PARAM_REQ=n CONFIG_BT_CTLR_PRIVACY=n CONFIG_BT_CTLR_TX_PWR_PLUS_8=y -CONFIG_BT_TICKER_COMPATIBILITY_MODE=y - CONFIG_BT_DATA_LEN_UPDATE=n CONFIG_BT_PERIPHERAL=y @@ -174,6 +173,8 @@ CONFIG_BT_PERIPHERAL_PREF_MAX_INT=6 CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY=99 CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400 +CONFIG_BT_WHITELIST=y + CONFIG_BT_GATT_DIS=y CONFIG_BT_GATT_DIS_MANUF="Nordic Semiconductor ASA" CONFIG_BT_GATT_DIS_MODEL="Mouse nRF52 Desktop" @@ -192,6 +193,13 @@ CONFIG_BT_GATT_HIDS_ATTR_MAX=19 CONFIG_BT_GATT_HIDS_DEFAULT_PERM_RW_ENCRYPT=y CONFIG_BT_CONN_CTX=y +CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT=1000 + +CONFIG_BT_LL_NRFXLIB=y +CONFIG_BLECTRL_MASTER_COUNT=0 +CONFIG_BLECTRL_SLAVE_COUNT=1 + +CONFIG_ENTROPY_CC310=n CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/configuration/nrf52840_pca20041/led_state_def.h b/applications/nrf_desktop/configuration/nrf52840_pca20041/led_state_def.h index a65018ef3454..bf7b4bf86f90 100644 --- a/applications/nrf_desktop/configuration/nrf52840_pca20041/led_state_def.h +++ b/applications/nrf_desktop/configuration/nrf52840_pca20041/led_state_def.h @@ -31,29 +31,33 @@ static const struct led_effect led_system_state_effect[LED_SYSTEM_STATE_COUNT] = static const struct led_effect led_peer_state_effect[LED_PEER_CNT][LED_PEER_STATE_COUNT] = { { - [LED_PEER_STATE_DISCONNECTED] = LED_EFFECT_LED_BREATH(1000, LED_COLOR(150, 0, 0)), + [LED_PEER_STATE_DISCONNECTED] = LED_EFFECT_LED_OFF(), [LED_PEER_STATE_CONNECTED] = LED_EFFECT_LED_ON(LED_COLOR(150, 0, 0)), + [LED_PEER_STATE_PEER_SEARCH] = LED_EFFECT_LED_BREATH(500, LED_COLOR(150, 0, 0)), [LED_PEER_STATE_CONFIRM_SELECT] = LED_EFFECT_LED_BLINK(50, LED_COLOR(150, 0, 0)), [LED_PEER_STATE_CONFIRM_ERASE] = LED_EFFECT_LED_BLINK(25, LED_COLOR(150, 0, 0)), [LED_PEER_STATE_ERASE_ADV] = LED_EFFECT_LED_BREATH(100, LED_COLOR(150, 0, 0)), }, { - [LED_PEER_STATE_DISCONNECTED] = LED_EFFECT_LED_BREATH(1000, LED_COLOR(0, 150, 0)), + [LED_PEER_STATE_DISCONNECTED] = LED_EFFECT_LED_OFF(), [LED_PEER_STATE_CONNECTED] = LED_EFFECT_LED_ON(LED_COLOR(0, 150, 0)), + [LED_PEER_STATE_PEER_SEARCH] = LED_EFFECT_LED_BREATH(500, LED_COLOR(0, 150, 0)), [LED_PEER_STATE_CONFIRM_SELECT] = LED_EFFECT_LED_BLINK(50, LED_COLOR(0, 150, 0)), [LED_PEER_STATE_CONFIRM_ERASE] = LED_EFFECT_LED_BLINK(25, LED_COLOR(0, 150, 0)), [LED_PEER_STATE_ERASE_ADV] = LED_EFFECT_LED_BREATH(100, LED_COLOR(0, 150, 0)), }, { - [LED_PEER_STATE_DISCONNECTED] = LED_EFFECT_LED_BREATH(1000, LED_COLOR(0, 0, 150)), + [LED_PEER_STATE_DISCONNECTED] = LED_EFFECT_LED_OFF(), [LED_PEER_STATE_CONNECTED] = LED_EFFECT_LED_ON(LED_COLOR(0, 0, 150)), + [LED_PEER_STATE_PEER_SEARCH] = LED_EFFECT_LED_BREATH(500, LED_COLOR(0, 0, 150)), [LED_PEER_STATE_CONFIRM_SELECT] = LED_EFFECT_LED_BLINK(50, LED_COLOR(0, 0, 150)), [LED_PEER_STATE_CONFIRM_ERASE] = LED_EFFECT_LED_BLINK(25, LED_COLOR(0, 0, 150)), [LED_PEER_STATE_ERASE_ADV] = LED_EFFECT_LED_BREATH(100, LED_COLOR(0, 0, 150)), }, { - [LED_PEER_STATE_DISCONNECTED] = LED_EFFECT_LED_BREATH(1000, LED_COLOR(150, 150, 150)), + [LED_PEER_STATE_DISCONNECTED] = LED_EFFECT_LED_OFF(), [LED_PEER_STATE_CONNECTED] = LED_EFFECT_LED_ON(LED_COLOR(150, 150, 150)), + [LED_PEER_STATE_PEER_SEARCH] = LED_EFFECT_LED_BREATH(500, LED_COLOR(150, 150, 150)), [LED_PEER_STATE_CONFIRM_SELECT] = LED_EFFECT_LED_BLINK(50, LED_COLOR(150, 150, 150)), [LED_PEER_STATE_CONFIRM_ERASE] = LED_EFFECT_LED_BLINK(25, LED_COLOR(150, 150, 150)), [LED_PEER_STATE_ERASE_ADV] = LED_EFFECT_LED_BREATH(100, LED_COLOR(150, 150, 150)), diff --git a/applications/nrf_desktop/configuration/nrf52_pca20037/app_ZDebug.conf b/applications/nrf_desktop/configuration/nrf52_pca20037/app_ZDebug.conf index 94d961060150..e06781f2a554 100644 --- a/applications/nrf_desktop/configuration/nrf52_pca20037/app_ZDebug.conf +++ b/applications/nrf_desktop/configuration/nrf52_pca20037/app_ZDebug.conf @@ -16,7 +16,7 @@ CONFIG_DESKTOP_HID_KEYBOARD=y CONFIG_DESKTOP_HID_CONSUMER_CTRL=y CONFIG_DESKTOP_HID_STATE_ENABLE=y CONFIG_DESKTOP_HID_REPORT_EXPIRATION=10000 -CONFIG_DESKTOP_HID_EVENT_QUEUE_SIZE=24 +CONFIG_DESKTOP_HID_EVENT_QUEUE_SIZE=60 CONFIG_DESKTOP_BUTTONS_ENABLE=y @@ -32,6 +32,7 @@ CONFIG_DESKTOP_BATTERY_MEAS_MAX_LEVEL=3200 CONFIG_DESKTOP_VOLTAGE_TO_SOC_DELTA=10 CONFIG_DESKTOP_POWER_MANAGER_ENABLE=y +CONFIG_DESKTOP_POWER_MANAGER_CONSTLAT=y CONFIG_DESKTOP_BLE_USE_DEFAULT_ID=y @@ -57,18 +58,19 @@ CONFIG_DESKTOP_FN_KEYS_LOCK=0x4181 ################################################################################ # Zephyr Configuration -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1280 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1536 CONFIG_ISR_STACK_SIZE=1536 CONFIG_MAIN_STACK_SIZE=768 CONFIG_IDLE_STACK_SIZE=512 CONFIG_BT_RX_STACK_SIZE=2048 +CONFIG_BT_HCI_TX_STACK_SIZE=1536 CONFIG_BOOT_BANNER=n CONFIG_NUM_COOP_PRIORITIES=10 CONFIG_NUM_PREEMPT_PRIORITIES=11 -CONFIG_HEAP_MEM_POOL_SIZE=1024 +CONFIG_HEAP_MEM_POOL_SIZE=4096 CONFIG_HEAP_MEM_POOL_MIN_SIZE=16 CONFIG_SYS_CLOCK_TICKS_PER_SEC=1000 @@ -121,8 +123,6 @@ CONFIG_BT_CTLR_CONN_PARAM_REQ=n CONFIG_BT_CTLR_PRIVACY=n CONFIG_BT_CTLR_TX_PWR_PLUS_4=y -CONFIG_BT_TICKER_COMPATIBILITY_MODE=y - CONFIG_BT_DATA_LEN_UPDATE=n CONFIG_BT_PERIPHERAL=y @@ -131,6 +131,8 @@ CONFIG_BT_PERIPHERAL_PREF_MAX_INT=6 CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY=99 CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400 +CONFIG_BT_WHITELIST=y + CONFIG_BT_GATT_DIS=y CONFIG_BT_GATT_DIS_MANUF="Nordic Semiconductor ASA" CONFIG_BT_GATT_DIS_MODEL="Keyboard nRF52 Desktop" @@ -149,6 +151,11 @@ CONFIG_BT_GATT_HIDS_ATTR_MAX=30 CONFIG_BT_GATT_HIDS_DEFAULT_PERM_RW_ENCRYPT=y CONFIG_BT_CONN_CTX=y +CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT=1000 + +CONFIG_BT_LL_NRFXLIB=y +CONFIG_BLECTRL_MASTER_COUNT=0 +CONFIG_BLECTRL_SLAVE_COUNT=1 CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/configuration/nrf52_pca20037/app_ZDebugMCUBoot.conf b/applications/nrf_desktop/configuration/nrf52_pca20037/app_ZDebugMCUBoot.conf index 91877d26d966..2ac83f39c22b 100644 --- a/applications/nrf_desktop/configuration/nrf52_pca20037/app_ZDebugMCUBoot.conf +++ b/applications/nrf_desktop/configuration/nrf52_pca20037/app_ZDebugMCUBoot.conf @@ -16,7 +16,7 @@ CONFIG_DESKTOP_HID_KEYBOARD=y CONFIG_DESKTOP_HID_CONSUMER_CTRL=y CONFIG_DESKTOP_HID_STATE_ENABLE=y CONFIG_DESKTOP_HID_REPORT_EXPIRATION=10000 -CONFIG_DESKTOP_HID_EVENT_QUEUE_SIZE=24 +CONFIG_DESKTOP_HID_EVENT_QUEUE_SIZE=60 CONFIG_DESKTOP_BUTTONS_ENABLE=y @@ -32,6 +32,7 @@ CONFIG_DESKTOP_BATTERY_MEAS_MAX_LEVEL=3200 CONFIG_DESKTOP_VOLTAGE_TO_SOC_DELTA=10 CONFIG_DESKTOP_POWER_MANAGER_ENABLE=y +CONFIG_DESKTOP_POWER_MANAGER_CONSTLAT=y CONFIG_DESKTOP_BLE_USE_DEFAULT_ID=y @@ -58,18 +59,19 @@ CONFIG_DESKTOP_FN_KEYS_LOCK=0x4181 ################################################################################ # Zephyr Configuration -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1280 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1536 CONFIG_ISR_STACK_SIZE=1536 CONFIG_MAIN_STACK_SIZE=768 CONFIG_IDLE_STACK_SIZE=512 CONFIG_BT_RX_STACK_SIZE=2048 +CONFIG_BT_HCI_TX_STACK_SIZE=1536 CONFIG_BOOT_BANNER=n CONFIG_NUM_COOP_PRIORITIES=10 CONFIG_NUM_PREEMPT_PRIORITIES=11 -CONFIG_HEAP_MEM_POOL_SIZE=1024 +CONFIG_HEAP_MEM_POOL_SIZE=4096 CONFIG_HEAP_MEM_POOL_MIN_SIZE=16 CONFIG_SYS_CLOCK_TICKS_PER_SEC=1000 @@ -122,8 +124,6 @@ CONFIG_BT_CTLR_CONN_PARAM_REQ=n CONFIG_BT_CTLR_PRIVACY=n CONFIG_BT_CTLR_TX_PWR_PLUS_4=y -CONFIG_BT_TICKER_COMPATIBILITY_MODE=y - CONFIG_BT_DATA_LEN_UPDATE=n CONFIG_BT_PERIPHERAL=y @@ -132,6 +132,8 @@ CONFIG_BT_PERIPHERAL_PREF_MAX_INT=6 CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY=99 CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400 +CONFIG_BT_WHITELIST=y + CONFIG_BT_GATT_DIS=y CONFIG_BT_GATT_DIS_MANUF="Nordic Semiconductor ASA" CONFIG_BT_GATT_DIS_MODEL="Keyboard nRF52 Desktop" @@ -150,6 +152,11 @@ CONFIG_BT_GATT_HIDS_ATTR_MAX=30 CONFIG_BT_GATT_HIDS_DEFAULT_PERM_RW_ENCRYPT=y CONFIG_BT_CONN_CTX=y +CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT=1000 + +CONFIG_BT_LL_NRFXLIB=y +CONFIG_BLECTRL_MASTER_COUNT=0 +CONFIG_BLECTRL_SLAVE_COUNT=1 CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/configuration/nrf52_pca20037/app_ZDebugWithShell.conf b/applications/nrf_desktop/configuration/nrf52_pca20037/app_ZDebugWithShell.conf index 4dd5249751af..baf237a2ba38 100644 --- a/applications/nrf_desktop/configuration/nrf52_pca20037/app_ZDebugWithShell.conf +++ b/applications/nrf_desktop/configuration/nrf52_pca20037/app_ZDebugWithShell.conf @@ -16,7 +16,7 @@ CONFIG_DESKTOP_HID_KEYBOARD=y CONFIG_DESKTOP_HID_CONSUMER_CTRL=y CONFIG_DESKTOP_HID_STATE_ENABLE=y CONFIG_DESKTOP_HID_REPORT_EXPIRATION=10000 -CONFIG_DESKTOP_HID_EVENT_QUEUE_SIZE=24 +CONFIG_DESKTOP_HID_EVENT_QUEUE_SIZE=60 CONFIG_DESKTOP_BUTTONS_ENABLE=y @@ -32,6 +32,7 @@ CONFIG_DESKTOP_BATTERY_MEAS_MAX_LEVEL=3200 CONFIG_DESKTOP_VOLTAGE_TO_SOC_DELTA=10 CONFIG_DESKTOP_POWER_MANAGER_ENABLE=y +CONFIG_DESKTOP_POWER_MANAGER_CONSTLAT=y CONFIG_DESKTOP_BLE_USE_DEFAULT_ID=y @@ -61,18 +62,19 @@ CONFIG_DESKTOP_KERNEL_SHELL=y ################################################################################ # Zephyr Configuration -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1280 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1536 CONFIG_ISR_STACK_SIZE=1536 CONFIG_MAIN_STACK_SIZE=768 CONFIG_IDLE_STACK_SIZE=512 CONFIG_BT_RX_STACK_SIZE=2048 +CONFIG_BT_HCI_TX_STACK_SIZE=1536 CONFIG_BOOT_BANNER=n CONFIG_NUM_COOP_PRIORITIES=10 CONFIG_NUM_PREEMPT_PRIORITIES=11 -CONFIG_HEAP_MEM_POOL_SIZE=1024 +CONFIG_HEAP_MEM_POOL_SIZE=4096 CONFIG_HEAP_MEM_POOL_MIN_SIZE=16 CONFIG_SYS_CLOCK_TICKS_PER_SEC=1000 @@ -125,8 +127,6 @@ CONFIG_BT_CTLR_CONN_PARAM_REQ=n CONFIG_BT_CTLR_PRIVACY=n CONFIG_BT_CTLR_TX_PWR_PLUS_4=y -CONFIG_BT_TICKER_COMPATIBILITY_MODE=y - CONFIG_BT_DATA_LEN_UPDATE=n CONFIG_BT_PERIPHERAL=y @@ -135,6 +135,8 @@ CONFIG_BT_PERIPHERAL_PREF_MAX_INT=6 CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY=99 CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400 +CONFIG_BT_WHITELIST=y + CONFIG_BT_GATT_DIS=y CONFIG_BT_GATT_DIS_MANUF="Nordic Semiconductor ASA" CONFIG_BT_GATT_DIS_MODEL="Keyboard nRF52 Desktop" @@ -153,6 +155,11 @@ CONFIG_BT_GATT_HIDS_ATTR_MAX=30 CONFIG_BT_GATT_HIDS_DEFAULT_PERM_RW_ENCRYPT=y CONFIG_BT_CONN_CTX=y +CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT=1000 + +CONFIG_BT_LL_NRFXLIB=y +CONFIG_BLECTRL_MASTER_COUNT=0 +CONFIG_BLECTRL_SLAVE_COUNT=1 CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/configuration/nrf52_pca20037/app_ZRelease.conf b/applications/nrf_desktop/configuration/nrf52_pca20037/app_ZRelease.conf index 05217ddc5fe5..11e8ac7633ff 100644 --- a/applications/nrf_desktop/configuration/nrf52_pca20037/app_ZRelease.conf +++ b/applications/nrf_desktop/configuration/nrf52_pca20037/app_ZRelease.conf @@ -12,7 +12,7 @@ CONFIG_DESKTOP_HID_KEYBOARD=y CONFIG_DESKTOP_HID_CONSUMER_CTRL=y CONFIG_DESKTOP_HID_STATE_ENABLE=y CONFIG_DESKTOP_HID_REPORT_EXPIRATION=10000 -CONFIG_DESKTOP_HID_EVENT_QUEUE_SIZE=24 +CONFIG_DESKTOP_HID_EVENT_QUEUE_SIZE=60 CONFIG_DESKTOP_BUTTONS_ENABLE=y @@ -28,6 +28,7 @@ CONFIG_DESKTOP_BATTERY_MEAS_MAX_LEVEL=3200 CONFIG_DESKTOP_VOLTAGE_TO_SOC_DELTA=10 CONFIG_DESKTOP_POWER_MANAGER_ENABLE=y +CONFIG_DESKTOP_POWER_MANAGER_CONSTLAT=y CONFIG_DESKTOP_BLE_PEER_CONTROL=y CONFIG_DESKTOP_BLE_PEER_CONTROL_BUTTON=0x438d @@ -53,18 +54,19 @@ CONFIG_DESKTOP_FN_KEYS_LOCK=0x4181 ################################################################################ # Zephyr Configuration -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1280 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1536 CONFIG_ISR_STACK_SIZE=1536 CONFIG_MAIN_STACK_SIZE=768 CONFIG_IDLE_STACK_SIZE=128 CONFIG_BT_RX_STACK_SIZE=2048 +CONFIG_BT_HCI_TX_STACK_SIZE=1536 CONFIG_BOOT_BANNER=n CONFIG_NUM_COOP_PRIORITIES=10 CONFIG_NUM_PREEMPT_PRIORITIES=11 -CONFIG_HEAP_MEM_POOL_SIZE=1024 +CONFIG_HEAP_MEM_POOL_SIZE=4096 CONFIG_HEAP_MEM_POOL_MIN_SIZE=16 CONFIG_SYS_CLOCK_TICKS_PER_SEC=1000 @@ -115,8 +117,6 @@ CONFIG_BT_CTLR_CONN_PARAM_REQ=n CONFIG_BT_CTLR_PRIVACY=n CONFIG_BT_CTLR_TX_PWR_PLUS_4=y -CONFIG_BT_TICKER_COMPATIBILITY_MODE=y - CONFIG_BT_DATA_LEN_UPDATE=n CONFIG_BT_PERIPHERAL=y @@ -125,6 +125,8 @@ CONFIG_BT_PERIPHERAL_PREF_MAX_INT=6 CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY=99 CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400 +CONFIG_BT_WHITELIST=y + CONFIG_BT_GATT_DIS=y CONFIG_BT_GATT_DIS_MANUF="Nordic Semiconductor ASA" CONFIG_BT_GATT_DIS_MODEL="Keyboard nRF52 Desktop" @@ -143,6 +145,11 @@ CONFIG_BT_GATT_HIDS_ATTR_MAX=30 CONFIG_BT_GATT_HIDS_DEFAULT_PERM_RW_ENCRYPT=y CONFIG_BT_CONN_CTX=y +CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT=1000 + +CONFIG_BT_LL_NRFXLIB=y +CONFIG_BLECTRL_MASTER_COUNT=0 +CONFIG_BLECTRL_SLAVE_COUNT=1 CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/configuration/nrf52_pca20037/app_ZReleaseMCUBoot.conf b/applications/nrf_desktop/configuration/nrf52_pca20037/app_ZReleaseMCUBoot.conf index bd52ba918077..e60f6153ebaf 100644 --- a/applications/nrf_desktop/configuration/nrf52_pca20037/app_ZReleaseMCUBoot.conf +++ b/applications/nrf_desktop/configuration/nrf52_pca20037/app_ZReleaseMCUBoot.conf @@ -12,7 +12,7 @@ CONFIG_DESKTOP_HID_KEYBOARD=y CONFIG_DESKTOP_HID_CONSUMER_CTRL=y CONFIG_DESKTOP_HID_STATE_ENABLE=y CONFIG_DESKTOP_HID_REPORT_EXPIRATION=10000 -CONFIG_DESKTOP_HID_EVENT_QUEUE_SIZE=24 +CONFIG_DESKTOP_HID_EVENT_QUEUE_SIZE=60 CONFIG_DESKTOP_BUTTONS_ENABLE=y @@ -28,6 +28,7 @@ CONFIG_DESKTOP_BATTERY_MEAS_MAX_LEVEL=3200 CONFIG_DESKTOP_VOLTAGE_TO_SOC_DELTA=10 CONFIG_DESKTOP_POWER_MANAGER_ENABLE=y +CONFIG_DESKTOP_POWER_MANAGER_CONSTLAT=y CONFIG_DESKTOP_BLE_PEER_CONTROL=y CONFIG_DESKTOP_BLE_PEER_CONTROL_BUTTON=0x438d @@ -45,6 +46,7 @@ CONFIG_DESKTOP_WATCHDOG_ENABLE=y CONFIG_DESKTOP_CONFIG_ENABLE=y CONFIG_DESKTOP_CONFIG_CHANNEL_ENABLE=y +CONFIG_DESKTOP_CONFIG_CHANNEL_DFU_ENABLE=y CONFIG_DESKTOP_FN_KEYS_ENABLE=y CONFIG_DESKTOP_FN_KEYS_SWITCH=0x111 @@ -53,18 +55,19 @@ CONFIG_DESKTOP_FN_KEYS_LOCK=0x4181 ################################################################################ # Zephyr Configuration -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1280 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1536 CONFIG_ISR_STACK_SIZE=1536 CONFIG_MAIN_STACK_SIZE=768 CONFIG_IDLE_STACK_SIZE=128 CONFIG_BT_RX_STACK_SIZE=2048 +CONFIG_BT_HCI_TX_STACK_SIZE=1536 CONFIG_BOOT_BANNER=n CONFIG_NUM_COOP_PRIORITIES=10 CONFIG_NUM_PREEMPT_PRIORITIES=11 -CONFIG_HEAP_MEM_POOL_SIZE=1024 +CONFIG_HEAP_MEM_POOL_SIZE=4096 CONFIG_HEAP_MEM_POOL_MIN_SIZE=16 CONFIG_SYS_CLOCK_TICKS_PER_SEC=1000 @@ -115,8 +118,6 @@ CONFIG_BT_CTLR_CONN_PARAM_REQ=n CONFIG_BT_CTLR_PRIVACY=n CONFIG_BT_CTLR_TX_PWR_PLUS_4=y -CONFIG_BT_TICKER_COMPATIBILITY_MODE=y - CONFIG_BT_DATA_LEN_UPDATE=n CONFIG_BT_PERIPHERAL=y @@ -125,6 +126,8 @@ CONFIG_BT_PERIPHERAL_PREF_MAX_INT=6 CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY=99 CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400 +CONFIG_BT_WHITELIST=y + CONFIG_BT_GATT_DIS=y CONFIG_BT_GATT_DIS_MANUF="Nordic Semiconductor ASA" CONFIG_BT_GATT_DIS_MODEL="Keyboard nRF52 Desktop" @@ -143,6 +146,11 @@ CONFIG_BT_GATT_HIDS_ATTR_MAX=30 CONFIG_BT_GATT_HIDS_DEFAULT_PERM_RW_ENCRYPT=y CONFIG_BT_CONN_CTX=y +CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT=1000 + +CONFIG_BT_LL_NRFXLIB=y +CONFIG_BLECTRL_MASTER_COUNT=0 +CONFIG_BLECTRL_SLAVE_COUNT=1 CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/configuration/nrf52_pca20037/led_state_def.h b/applications/nrf_desktop/configuration/nrf52_pca20037/led_state_def.h index 916c6b73c682..dd58e9cc7598 100644 --- a/applications/nrf_desktop/configuration/nrf52_pca20037/led_state_def.h +++ b/applications/nrf_desktop/configuration/nrf52_pca20037/led_state_def.h @@ -28,22 +28,25 @@ static const struct led_effect led_system_state_effect[LED_SYSTEM_STATE_COUNT]; static const struct led_effect led_peer_state_effect[LED_PEER_CNT][LED_PEER_STATE_COUNT] = { { - [LED_PEER_STATE_DISCONNECTED] = LED_EFFECT_LED_BREATH(1000, LED_COLOR(255)), + [LED_PEER_STATE_DISCONNECTED] = LED_EFFECT_LED_OFF(), [LED_PEER_STATE_CONNECTED] = LED_EFFECT_LED_ON(LED_COLOR(255)), + [LED_PEER_STATE_PEER_SEARCH] = LED_EFFECT_LED_BREATH(500, LED_COLOR(255)), [LED_PEER_STATE_CONFIRM_SELECT] = LED_EFFECT_LED_CLOCK(1, LED_COLOR(255)), [LED_PEER_STATE_CONFIRM_ERASE] = LED_EFFECT_LED_BLINK(25, LED_COLOR(255)), [LED_PEER_STATE_ERASE_ADV] = LED_EFFECT_LED_BREATH(100, LED_COLOR(255)), }, { - [LED_PEER_STATE_DISCONNECTED] = LED_EFFECT_LED_BREATH(1000, LED_COLOR(255)), + [LED_PEER_STATE_DISCONNECTED] = LED_EFFECT_LED_OFF(), [LED_PEER_STATE_CONNECTED] = LED_EFFECT_LED_ON(LED_COLOR(255)), + [LED_PEER_STATE_PEER_SEARCH] = LED_EFFECT_LED_BREATH(500, LED_COLOR(255)), [LED_PEER_STATE_CONFIRM_SELECT] = LED_EFFECT_LED_CLOCK(2, LED_COLOR(255)), [LED_PEER_STATE_CONFIRM_ERASE] = LED_EFFECT_LED_BLINK(25, LED_COLOR(255)), [LED_PEER_STATE_ERASE_ADV] = LED_EFFECT_LED_BREATH(100, LED_COLOR(255)), }, { - [LED_PEER_STATE_DISCONNECTED] = LED_EFFECT_LED_BREATH(1000, LED_COLOR(255)), + [LED_PEER_STATE_DISCONNECTED] = LED_EFFECT_LED_OFF(), [LED_PEER_STATE_CONNECTED] = LED_EFFECT_LED_ON(LED_COLOR(255)), + [LED_PEER_STATE_PEER_SEARCH] = LED_EFFECT_LED_BREATH(500, LED_COLOR(255)), [LED_PEER_STATE_CONFIRM_SELECT] = LED_EFFECT_LED_CLOCK(3, LED_COLOR(255)), [LED_PEER_STATE_CONFIRM_ERASE] = LED_EFFECT_LED_BLINK(25, LED_COLOR(255)), [LED_PEER_STATE_ERASE_ADV] = LED_EFFECT_LED_BREATH(100, LED_COLOR(255)), diff --git a/applications/nrf_desktop/configuration/nrf52_pca20037/mcuboot_ZReleaseMCUBoot.conf b/applications/nrf_desktop/configuration/nrf52_pca20037/mcuboot_ZReleaseMCUBoot.conf index dd316a95698c..b73382f62a30 100644 --- a/applications/nrf_desktop/configuration/nrf52_pca20037/mcuboot_ZReleaseMCUBoot.conf +++ b/applications/nrf_desktop/configuration/nrf52_pca20037/mcuboot_ZReleaseMCUBoot.conf @@ -21,5 +21,6 @@ CONFIG_BOOT_SIGNATURE_KEY_FILE="root-rsa-2048.pem" CONFIG_FLASH=y -CONFIG_PM_PARTITION_SIZE_MCUBOOT_SCRATCH=0x4000 -CONFIG_PM_PARTITION_SIZE_MCUBOOT_STORAGE=0x4000 +CONFIG_PM_PARTITION_SIZE_MCUBOOT=0x6000 +CONFIG_PM_PARTITION_SIZE_MCUBOOT_SCRATCH=0x2000 +CONFIG_PM_PARTITION_SIZE_MCUBOOT_STORAGE=0x2000 diff --git a/applications/nrf_desktop/configuration/nrf52_pca20044/app_ZDebug.conf b/applications/nrf_desktop/configuration/nrf52_pca20044/app_ZDebug.conf index 6acde3b11fc6..c15717a7c045 100644 --- a/applications/nrf_desktop/configuration/nrf52_pca20044/app_ZDebug.conf +++ b/applications/nrf_desktop/configuration/nrf52_pca20044/app_ZDebug.conf @@ -136,6 +136,8 @@ CONFIG_BT_PERIPHERAL_PREF_MAX_INT=6 CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY=99 CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400 +CONFIG_BT_WHITELIST=y + CONFIG_BT_GATT_DIS=y CONFIG_BT_GATT_DIS_MANUF="Nordic Semiconductor ASA" CONFIG_BT_GATT_DIS_MODEL="Mouse nRF52 Desktop" @@ -154,6 +156,7 @@ CONFIG_BT_GATT_HIDS_ATTR_MAX=19 CONFIG_BT_GATT_HIDS_DEFAULT_PERM_RW_ENCRYPT=y CONFIG_BT_CONN_CTX=y +CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT=1000 CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/configuration/nrf52_pca20044/app_ZRelease.conf b/applications/nrf_desktop/configuration/nrf52_pca20044/app_ZRelease.conf index b2b092b9f84b..3ff95decc208 100644 --- a/applications/nrf_desktop/configuration/nrf52_pca20044/app_ZRelease.conf +++ b/applications/nrf_desktop/configuration/nrf52_pca20044/app_ZRelease.conf @@ -130,6 +130,8 @@ CONFIG_BT_PERIPHERAL_PREF_MAX_INT=6 CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY=99 CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400 +CONFIG_BT_WHITELIST=y + CONFIG_BT_GATT_DIS=y CONFIG_BT_GATT_DIS_MANUF="Nordic Semiconductor ASA" CONFIG_BT_GATT_DIS_MODEL="Mouse nRF52 Desktop" @@ -148,6 +150,7 @@ CONFIG_BT_GATT_HIDS_ATTR_MAX=19 CONFIG_BT_GATT_HIDS_DEFAULT_PERM_RW_ENCRYPT=y CONFIG_BT_CONN_CTX=y +CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT=1000 CONFIG_EVENT_MANAGER=y CONFIG_LINKER_ORPHAN_SECTION_PLACE=y diff --git a/applications/nrf_desktop/sample.yaml b/applications/nrf_desktop/sample.yaml index 3a300ba5a742..5f83507cd874 100644 --- a/applications/nrf_desktop/sample.yaml +++ b/applications/nrf_desktop/sample.yaml @@ -10,7 +10,7 @@ tests: test_debug_mcuboot: build_only: true build_on_all: true - platform_whitelist: nrf52840_pca20041 nrf52840_pca10059 nrf52_pca20037 + platform_whitelist: nrf52840_pca20041 nrf52840_pca10059 tags: bluetooth ci_build extra_args: "CMAKE_BUILD_TYPE=ZDebugMCUBoot" test_zrelease: diff --git a/applications/nrf_desktop/src/events/Kconfig b/applications/nrf_desktop/src/events/Kconfig index 82dbe3996cba..88fff1b30a63 100644 --- a/applications/nrf_desktop/src/events/Kconfig +++ b/applications/nrf_desktop/src/events/Kconfig @@ -22,6 +22,10 @@ config DESKTOP_INIT_LOG_BLE_PEER_EVENT bool "BLE peer event" default y +config DESKTOP_INIT_LOG_BLE_PEER_SEARCH_EVENT + bool "BLE peer search event" + default y + config DESKTOP_INIT_LOG_BLE_PEER_OPERATION_EVENT bool "BLE peer operation event" default y diff --git a/applications/nrf_desktop/src/events/ble_event.c b/applications/nrf_desktop/src/events/ble_event.c index 49d393b61d08..71582d4a4c0f 100644 --- a/applications/nrf_desktop/src/events/ble_event.c +++ b/applications/nrf_desktop/src/events/ble_event.c @@ -51,6 +51,34 @@ EVENT_TYPE_DEFINE(ble_peer_event, log_ble_peer_event, &ble_peer_event_info); +static int log_ble_peer_search_event(const struct event_header *eh, char *buf, + size_t buf_len) +{ + const struct ble_peer_search_event *event = cast_ble_peer_search_event(eh); + + return snprintf(buf, buf_len, "%sactive", (event->active)?(""):("in")); +} + +static void profile_ble_peer_search_event(struct log_event_buf *buf, + const struct event_header *eh) +{ + const struct ble_peer_search_event *event = cast_ble_peer_search_event(eh); + + ARG_UNUSED(event); + profiler_log_encode_u32(buf, (u32_t)event->active); +} + +EVENT_INFO_DEFINE(ble_peer_search_event, + ENCODE(PROFILER_ARG_U32), + ENCODE("active"), + profile_ble_peer_search_event); + +EVENT_TYPE_DEFINE(ble_peer_search_event, + IS_ENABLED(CONFIG_DESKTOP_INIT_LOG_BLE_PEER_SEARCH_EVENT), + log_ble_peer_search_event, + &ble_peer_search_event_info); + + static const char * const op_name[] = { #define X(name) STRINGIFY(name), PEER_OPERATION_LIST diff --git a/applications/nrf_desktop/src/events/ble_event.h b/applications/nrf_desktop/src/events/ble_event.h index 3ee43e60fb73..fd9f3a3e6148 100644 --- a/applications/nrf_desktop/src/events/ble_event.h +++ b/applications/nrf_desktop/src/events/ble_event.h @@ -44,6 +44,7 @@ enum peer_state { #define PEER_OPERATION_LIST \ X(SELECT) \ X(SELECTED) \ + X(SCAN_REQUEST) \ X(ERASE) \ X(ERASE_ADV) \ X(ERASE_ADV_CANCEL) \ @@ -92,6 +93,14 @@ struct ble_peer_operation_event { }; EVENT_TYPE_DECLARE(ble_peer_operation_event); +/** @brief BLE peer search event. */ +struct ble_peer_search_event { + struct event_header header; + + bool active; +}; +EVENT_TYPE_DECLARE(ble_peer_search_event); + /** @brief BLE discovery complete event. */ struct ble_discovery_complete_event { struct event_header header; diff --git a/applications/nrf_desktop/src/events/config_event.h b/applications/nrf_desktop/src/events/config_event.h index 955ba1cad452..b8c399fe7b5c 100644 --- a/applications/nrf_desktop/src/events/config_event.h +++ b/applications/nrf_desktop/src/events/config_event.h @@ -60,6 +60,7 @@ extern "C" { MOD_FIELD_SET(module) | OPT_FIELD_SET(option)) #define SETUP_MODULE_SENSOR 0x1 +#define SETUP_MODULE_QOS 0x2 /* Config event, setup group, sensor module macros */ @@ -69,6 +70,13 @@ extern "C" { #define SENSOR_OPT_DOWNSHIFT_REST2 0x3 #define SENSOR_OPT_COUNT 4 +/* Config event, setup group, qos module macros */ +#define QOS_OPT_BLACKLIST 0x0 +#define QOS_OPT_CHMAP 0x1 +#define QOS_OPT_PARAM_BLE 0x2 +#define QOS_OPT_PARAM_WIFI 0x3 +#define QOS_OPT_COUNT 4 + /* Config event, DFU group macros */ #define DFU_START 0x0 #define DFU_DATA 0x1 diff --git a/applications/nrf_desktop/src/hw_interface/Kconfig.buttons b/applications/nrf_desktop/src/hw_interface/Kconfig.buttons index fe156e5f75b8..c2d4590bedeb 100644 --- a/applications/nrf_desktop/src/hw_interface/Kconfig.buttons +++ b/applications/nrf_desktop/src/hw_interface/Kconfig.buttons @@ -21,7 +21,7 @@ endchoice config DESKTOP_BUTTONS_SCAN_INTERVAL int "Buttons scan interval in ms" depends on DESKTOP_BUTTONS_ENABLE - default 15 + default 6 help Interval at which key matrix is scanned. diff --git a/applications/nrf_desktop/src/hw_interface/buttons.c b/applications/nrf_desktop/src/hw_interface/buttons.c index 1aedac847aaa..3f20994b6b55 100644 --- a/applications/nrf_desktop/src/hw_interface/buttons.c +++ b/applications/nrf_desktop/src/hw_interface/buttons.c @@ -113,6 +113,7 @@ static int set_trig_mode(int trig_mode) for (size_t i = 0; (i < ARRAY_SIZE(row)) && !err; i++) { __ASSERT_NO_MSG(row[i].port < ARRAY_SIZE(port_map)); + __ASSERT_NO_MSG(port_map[row[i].port] != NULL); err = gpio_pin_configure(gpio_devs[row[i].port], row[i].pin, flags); @@ -237,6 +238,17 @@ static void scan_fn(struct k_work *work) goto error; } + static u32_t settled_state[COLUMNS]; + + /* Prevent bouncing */ + static u32_t prev_state[COLUMNS]; + for (size_t i = 0; i < COLUMNS; i++) { + u32_t bounce_mask = prev_state[i] ^ raw_state[i]; + prev_state[i] = raw_state[i]; + raw_state[i] &= ~bounce_mask; + raw_state[i] |= settled_state[i] & bounce_mask; + } + /* Prevent ghosting */ u32_t cur_state[COLUMNS]; for (size_t i = 0; i < COLUMNS; i++) { @@ -256,7 +268,6 @@ static void scan_fn(struct k_work *work) } /* Emit event for any key state change */ - static u32_t old_state[COLUMNS]; bool any_pressed = false; size_t evt_limit = 0; @@ -264,7 +275,7 @@ static void scan_fn(struct k_work *work) for (size_t j = 0; j < ARRAY_SIZE(row); j++) { bool is_raw_pressed = raw_state[i] & BIT(j); bool is_pressed = cur_state[i] & BIT(j); - bool was_pressed = old_state[i] & BIT(j); + bool was_pressed = settled_state[i] & BIT(j); if ((is_pressed != was_pressed) && (is_pressed == is_raw_pressed) && @@ -277,12 +288,12 @@ static void scan_fn(struct k_work *work) evt_limit++; - WRITE_BIT(old_state[i], j, is_pressed); + WRITE_BIT(settled_state[i], j, is_pressed); } } any_pressed = any_pressed || - (old_state[i] != 0) || + (settled_state[i] != 0) || (cur_state[i] != 0); } diff --git a/applications/nrf_desktop/src/hw_interface/selector_hw.c b/applications/nrf_desktop/src/hw_interface/selector_hw.c index 555f9fa73a9f..92d060b8ca04 100644 --- a/applications/nrf_desktop/src/hw_interface/selector_hw.c +++ b/applications/nrf_desktop/src/hw_interface/selector_hw.c @@ -187,10 +187,13 @@ static int configure_callbacks(struct selector *selector) } for (size_t i = 0; (i < ARRAY_SIZE(gpio_dev)) && !err; i++) { + if (!gpio_dev[i]) { + __ASSERT_NO_MSG(bitmask[i] == 0); + continue; + } gpio_init_callback(&selector->gpio_cb[i], selector_isr, bitmask[i]); err = gpio_add_callback(gpio_dev[i], &selector->gpio_cb[i]); - } if (err) { @@ -266,6 +269,10 @@ static int init(void) "There is no active selector"); for (size_t i = 0; i < ARRAY_SIZE(port_map); i++) { + if (!port_map[i]) { + continue; + } + gpio_dev[i] = device_get_binding(port_map[i]); if (!gpio_dev[i]) { diff --git a/applications/nrf_desktop/src/modules/CMakeLists.txt b/applications/nrf_desktop/src/modules/CMakeLists.txt index 4bbd82da2f83..42b152062d37 100644 --- a/applications/nrf_desktop/src/modules/CMakeLists.txt +++ b/applications/nrf_desktop/src/modules/CMakeLists.txt @@ -8,6 +8,9 @@ target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ble_state.c) target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ble_bond.c) +target_sources_ifdef(CONFIG_BT_PERIPHERAL + app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ble_latency.c) + target_sources_ifdef(CONFIG_DESKTOP_BLE_ADVERTISING_ENABLE app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ble_adv.c) @@ -17,6 +20,9 @@ target_sources_ifdef(CONFIG_DESKTOP_BLE_DISCOVERY_ENABLE target_sources_ifdef(CONFIG_DESKTOP_BLE_SCANNING_ENABLE app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ble_scan.c) +target_sources_ifdef(CONFIG_DESKTOP_BLE_QOS_ENABLE + app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ble_qos.c) + target_sources_ifdef(CONFIG_DESKTOP_CONFIG_ENABLE app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/config.c) diff --git a/applications/nrf_desktop/src/modules/Kconfig.ble b/applications/nrf_desktop/src/modules/Kconfig.ble index ab1c86c52105..0239c7fba03d 100644 --- a/applications/nrf_desktop/src/modules/Kconfig.ble +++ b/applications/nrf_desktop/src/modules/Kconfig.ble @@ -39,9 +39,28 @@ config DESKTOP_BLE_PEER_CONTROL_BUTTON config DESKTOP_BLE_PEER_SELECT bool "Enable switching between peers" + depends on BT_PERIPHERAL help Short click to switch peer. Double click to accept choice. +config DESKTOP_BLE_NEW_PEER_SCAN_REQUEST + bool "Enable scanning on request" + depends on BT_CENTRAL + help + Short click to start new peer scanning. When enabled the device will + look for non-bonded devices only when requested. + When disabled the device always search for non-bonded devices while + scanning. + Regardless of the choice made the device scans periodically for + already bonded peers. + +config DESKTOP_BLE_NEW_PEER_SCAN_ON_BOOT + bool "Look for peers after boot" + depends on DESKTOP_BLE_NEW_PEER_SCAN_REQUEST + help + When enabled the device will look for new peers also after it was + booted. + config DESKTOP_BLE_PEER_ERASE bool "Enable peer erase" help @@ -157,14 +176,6 @@ config DESKTOP_BLE_SCANNING_ENABLE help Enable device to scan for peripheral devices. -config DESKTOP_BLE_SECURITY_FAIL_TIMEOUT_S - int "Security fail timeout [s]" - default 10 - depends on BT_PERIPHERAL - help - After this time peripheral device disconnects if security is not - established. - config DESKTOP_BLE_SCAN_START_TIMEOUT_S int "Scan start timeout [s]" default 5 @@ -206,4 +217,73 @@ source "subsys/logging/Kconfig.template.log_config" endif +comment "BLE Latency" + +if BT_PERIPHERAL + +config DESKTOP_BLE_SECURITY_FAIL_TIMEOUT_S + int "Security fail timeout [s]" + default 10 + help + After this time peripheral device disconnects if security is not + established. + +module = DESKTOP_BLE_LATENCY +module-str = BLE latency +source "subsys/logging/Kconfig.template.log_config" + +endif + +comment "BLE QoS management" + +config DESKTOP_BLE_QOS_ENABLE + bool "Enable BLE channel map management" + depends on BT_CENTRAL + depends on BT_LL_NRFXLIB + select BT_HCI_VS_EVT_USER + help + Enable device to avoid congested RF channels. + +config DESKTOP_BLE_QOS_INTERVAL + int "Processing interval for QoS thread [ms]" + default 1000 + depends on DESKTOP_BLE_QOS_ENABLE + help + Configure processing interval for QoS algorithm. + Longer intervals means more time to accumulate CRC stats, + and vice versa. + +config DESKTOP_BLE_QOS_STACK_SIZE + int "Base stack size for QoS thread" + default 240 + depends on DESKTOP_BLE_QOS_ENABLE + help + Configure base stack size for QoS processing thread. + +config DESKTOP_BLE_QOS_STATS_PRINTOUT_ENABLE + bool "Enable BLE QoS statistics printout" + depends on DESKTOP_BLE_QOS_ENABLE + select USB_COMPOSITE_DEVICE + select USB_CDC_ACM + select SERIAL + select UART_LINE_CTRL + help + Enable to get real-time QoS information printouts via CDC ACM. + +config DESKTOP_BLE_QOS_STATS_PRINT_STACK_SIZE + int "Stack size addition for QoS printout" + default 784 + depends on DESKTOP_BLE_QOS_STATS_PRINTOUT_ENABLE + help + This size increase is added to the QoS base stack size + to facilitate statistics printout. + +if DESKTOP_BLE_QOS_ENABLE + +module = DESKTOP_BLE_QOS +module-str = BLE channel map management +source "subsys/logging/Kconfig.template.log_config" + +endif + endmenu diff --git a/applications/nrf_desktop/src/modules/Kconfig.hids b/applications/nrf_desktop/src/modules/Kconfig.hids index 18ff01ba4ab3..d8f5aa3080d1 100644 --- a/applications/nrf_desktop/src/modules/Kconfig.hids +++ b/applications/nrf_desktop/src/modules/Kconfig.hids @@ -33,7 +33,6 @@ config DESKTOP_HID_PERIPHERAL depends on BT_PERIPHERAL depends on (DESKTOP_HID_MOUSE || DESKTOP_HID_KEYBOARD || DESKTOP_HID_CONSUMER_CTRL) select BT_GATT_HIDS - select NRF_BT_DIS select DESKTOP_BAS_ENABLE select DESKTOP_HIDS_ENABLE help diff --git a/applications/nrf_desktop/src/modules/Kconfig.led_stream b/applications/nrf_desktop/src/modules/Kconfig.led_stream index 361c1d53c274..28b0b47cff51 100644 --- a/applications/nrf_desktop/src/modules/Kconfig.led_stream +++ b/applications/nrf_desktop/src/modules/Kconfig.led_stream @@ -15,8 +15,8 @@ config DESKTOP_LED_STREAM_ENABLE config DESKTOP_LED_STREAM_QUEUE_SIZE int "Stream led event queue size" depends on DESKTOP_LED_ENABLE && DESKTOP_CONFIG_CHANNEL_ENABLE - default 5 - range 2 255 + default 15 + range 2 254 if DESKTOP_LED_STREAM_ENABLE diff --git a/applications/nrf_desktop/src/modules/Kconfig.power_manager b/applications/nrf_desktop/src/modules/Kconfig.power_manager index 31cfa0485a2b..579e1d428196 100644 --- a/applications/nrf_desktop/src/modules/Kconfig.power_manager +++ b/applications/nrf_desktop/src/modules/Kconfig.power_manager @@ -17,6 +17,13 @@ config DESKTOP_POWER_MANAGER_ENABLE if DESKTOP_POWER_MANAGER_ENABLE +config DESKTOP_POWER_MANAGER_CONSTLAT + bool "Constant latency interrupts" + help + When enabled SoC will use configuration for constant latency + interrupts. This reduces interrupt propagation time but increases + power consumption. + config DESKTOP_POWER_MANAGER_TIMEOUT int "Power down timeout [s]" default 120 diff --git a/applications/nrf_desktop/src/modules/ble_adv.c b/applications/nrf_desktop/src/modules/ble_adv.c index c128a9361b8c..6da4500442ca 100644 --- a/applications/nrf_desktop/src/modules/ble_adv.c +++ b/applications/nrf_desktop/src/modules/ble_adv.c @@ -36,19 +36,6 @@ LOG_MODULE_REGISTER(MODULE, CONFIG_DESKTOP_BLE_ADV_LOG_LEVEL); #define MAX_KEY_LEN 30 #define PEER_IS_RPA_STORAGE_NAME "peer_is_rpa_" -static const struct bt_le_adv_param adv_param_fast = { - .options = BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_ONE_TIME | - BT_LE_ADV_OPT_USE_NAME, - .interval_min = BT_GAP_ADV_FAST_INT_MIN_1, - .interval_max = BT_GAP_ADV_FAST_INT_MAX_1, -}; - -static const struct bt_le_adv_param adv_param_normal = { - .options = BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_ONE_TIME | - BT_LE_ADV_OPT_USE_NAME, - .interval_min = BT_GAP_ADV_FAST_INT_MIN_2, - .interval_max = BT_GAP_ADV_FAST_INT_MAX_2, -}; static const struct bt_data ad[] = { BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), @@ -109,6 +96,16 @@ enum peer_rpa { static enum peer_rpa peer_is_rpa[CONFIG_BT_ID_MAX]; + +static void broadcast_adv_state(bool active) +{ + struct ble_peer_search_event *event = new_ble_peer_search_event(); + event->active = active; + EVENT_SUBMIT(event); + + LOG_INF("Advertising %s", (active)?("started"):("stopped")); +} + static int ble_adv_stop(void) { int err = bt_le_adv_stop(); @@ -125,7 +122,7 @@ static int ble_adv_stop(void) state = STATE_IDLE; - LOG_INF("Advertising stopped"); + broadcast_adv_state(false); } return err; @@ -172,15 +169,40 @@ static int ble_adv_start_directed(const bt_addr_le_t *addr, bool fast_adv) return 0; } -static int ble_adv_start_undirected(bool fast_adv, bool swift_pair) +static int ble_adv_start_undirected(const bt_addr_le_t *bond_addr, + bool fast_adv, bool swift_pair) { - struct bt_le_adv_param adv_param; + struct bt_le_adv_param adv_param = { + .options = BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_ONE_TIME | + BT_LE_ADV_OPT_USE_NAME, + }; LOG_INF("Use %s advertising", (fast_adv)?("fast"):("slow")); if (fast_adv) { - adv_param = adv_param_fast; + adv_param.interval_min = BT_GAP_ADV_FAST_INT_MIN_1; + adv_param.interval_max = BT_GAP_ADV_FAST_INT_MAX_1; } else { - adv_param = adv_param_normal; + adv_param.interval_min = BT_GAP_ADV_FAST_INT_MIN_2; + adv_param.interval_max = BT_GAP_ADV_FAST_INT_MAX_2; + } + + if (IS_ENABLED(CONFIG_BT_WHITELIST)) { + int err = bt_le_whitelist_clear(); + + if (err) { + LOG_ERR("Cannot clear whitelist (err: %d)", err); + return err; + } + + if (bt_addr_le_cmp(bond_addr, BT_ADDR_LE_ANY)) { + adv_param.options |= BT_LE_ADV_OPT_FILTER_CONN; + err = bt_le_whitelist_add(bond_addr); + } + + if (err) { + LOG_ERR("Cannot add peer to whitelist (err: %d)", err); + return err; + } } adv_param.id = cur_identity; @@ -204,6 +226,7 @@ static int ble_adv_start(bool can_fast_adv) .peer_id = 0, .peer_count = 0, }; + bt_addr_le_copy(&bond_find_data.peer_address, BT_ADDR_LE_ANY); bt_foreach_bond(cur_identity, bond_find, &bond_find_data); int err = ble_adv_stop(); @@ -228,7 +251,8 @@ static int ble_adv_start(bool can_fast_adv) err = ble_adv_start_directed(&bond_find_data.peer_address, fast_adv); } else { - err = ble_adv_start_undirected(fast_adv, swift_pair); + err = ble_adv_start_undirected(&bond_find_data.peer_address, + fast_adv, swift_pair); } if (err == -ECONNREFUSED) { @@ -256,8 +280,7 @@ static int ble_adv_start(bool can_fast_adv) } } - LOG_INF("Advertising started"); - + broadcast_adv_state(true); error: return err; } @@ -364,7 +387,6 @@ static int init_settings(void) static void init(void) { - if (init_settings()) { module_set_state(MODULE_STATE_ERROR); return; @@ -464,7 +486,7 @@ static bool event_handler(const struct event_header *eh) case PEER_STATE_DISCONNECTED: can_fast_adv = true; - /* fall through */ + /* Fall-through */ case PEER_STATE_CONN_FAILED: if (state != STATE_OFF) { @@ -544,6 +566,7 @@ static bool event_handler(const struct event_header *eh) /* Ignore */ break; + case PEER_OPERATION_SCAN_REQUEST: default: __ASSERT_NO_MSG(false); break; @@ -612,7 +635,7 @@ static bool event_handler(const struct event_header *eh) case STATE_OFF: was_off = true; state = STATE_IDLE; - /* fall through */ + /* Fall-through */ case STATE_GRACE_PERIOD: err = ble_adv_start(true); diff --git a/applications/nrf_desktop/src/modules/ble_bond.c b/applications/nrf_desktop/src/modules/ble_bond.c index f6617326106b..01f7b285ca56 100644 --- a/applications/nrf_desktop/src/modules/ble_bond.c +++ b/applications/nrf_desktop/src/modules/ble_bond.c @@ -49,6 +49,8 @@ static void select_start(void); static void select_next(void); static void select_confirm(void); +static void scan_request(void); + static void erase_start(void); static void erase_adv_confirm(void); static void erase_confirm(void); @@ -61,6 +63,10 @@ static const struct state_switch state_switch[] = { {STATE_SELECT_PEER, CLICK_DOUBLE, STATE_IDLE, select_confirm}, #endif +#if CONFIG_DESKTOP_BLE_NEW_PEER_SCAN_REQUEST + {STATE_IDLE, CLICK_SHORT, STATE_IDLE, scan_request}, +#endif + #if CONFIG_DESKTOP_BLE_PEER_ERASE {STATE_IDLE, CLICK_LONG, STATE_ERASE_PEER, erase_start}, #if CONFIG_BT_PERIPHERAL @@ -318,6 +324,19 @@ static void select_confirm(void) EVENT_SUBMIT(event); } +static void scan_request(void) +{ + LOG_INF("Peer scan request"); + + struct ble_peer_operation_event *event = new_ble_peer_operation_event(); + + event->op = PEER_OPERATION_SCAN_REQUEST; + event->bt_app_id = cur_peer_id; + event->bt_stack_id = get_bt_stack_peer_id(cur_peer_id); + + EVENT_SUBMIT(event); +} + static void erase_start(void) { LOG_INF("Start peer erase"); @@ -497,6 +516,10 @@ static void silence_unused(void) ARG_UNUSED(select_confirm); } + if (!IS_ENABLED(CONFIG_DESKTOP_BLE_NEW_PEER_SCAN_REQUEST)) { + ARG_UNUSED(scan_request); + } + if (!IS_ENABLED(CONFIG_DESKTOP_BLE_PEER_ERASE)) { ARG_UNUSED(erase_start); ARG_UNUSED(erase_adv_confirm); @@ -696,7 +719,7 @@ static void selector_event_handler(const struct selector_event *event) case STATE_ERASE_ADV: case STATE_SELECT_PEER: cancel_operation(); - /* Fall-though */ + /* Fall-through */ case STATE_IDLE: select_dongle_peer(); break; @@ -716,7 +739,7 @@ static void selector_event_handler(const struct selector_event *event) case STATE_ERASE_ADV: case STATE_SELECT_PEER: cancel_operation(); - /* Fall-though */ + /* Fall-through */ case STATE_DONGLE_CONN: select_ble_peers(); break; diff --git a/applications/nrf_desktop/src/modules/ble_latency.c b/applications/nrf_desktop/src/modules/ble_latency.c new file mode 100644 index 000000000000..666eb125c0ad --- /dev/null +++ b/applications/nrf_desktop/src/modules/ble_latency.c @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +#include +#include + +#include "ble_event.h" +#include "config_event.h" + +#define MODULE ble_latency +#include "module_state_event.h" + +#include +LOG_MODULE_REGISTER(MODULE, CONFIG_DESKTOP_BLE_LATENCY_LOG_LEVEL); + +#define SECURITY_FAIL_TIMEOUT_MS \ + K_SECONDS(CONFIG_DESKTOP_BLE_SECURITY_FAIL_TIMEOUT_S) +#define LOW_LATENCY_CHECK_PERIOD_MS 5000 +#define DEFAULT_LATENCY CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY + +static struct bt_conn *active_conn; +static struct k_delayed_work security_timeout; +static struct k_delayed_work low_latency_check; + +enum { + LOW_LATENCY_ENABLED = BIT(0), + LOW_LATENCY_REQUIRED = BIT(1), +}; + +static u8_t latency_state; + + +static void security_timeout_fn(struct k_work *w) +{ + /* Assert one local identity holds exactly one bond. + * One local identity is unused. + */ + BUILD_ASSERT(CONFIG_BT_MAX_PAIRED == (CONFIG_BT_ID_MAX - 1)); + BUILD_ASSERT(CONFIG_BT_MAX_CONN == 1); + BUILD_ASSERT(SECURITY_FAIL_TIMEOUT_MS > 0); + __ASSERT_NO_MSG(active_conn); + + int err = bt_conn_disconnect(active_conn, + BT_HCI_ERR_REMOTE_USER_TERM_CONN); + + if (err == -ENOTCONN) { + err = 0; + } + LOG_WRN("Security establishment failed - device %s", + err ? "failed to disconnect" : "disconnected"); +} + +static void set_ble_latency(bool low_latency) +{ + struct bt_conn_info info; + + int err = bt_conn_get_info(active_conn, &info); + + if (err) { + LOG_WRN("Cannot get conn info (%d)", err); + return; + } + + __ASSERT_NO_MSG(info.role == BT_CONN_ROLE_SLAVE); + + const struct bt_le_conn_param param = { + .interval_min = info.le.interval, + .interval_max = info.le.interval, + .latency = (low_latency) ? (0) : (DEFAULT_LATENCY), + .timeout = info.le.timeout + }; + + err = bt_conn_le_param_update(active_conn, ¶m); + + if (!err || (err == -EALREADY)) { + LOG_INF("BLE latency %screased", low_latency ? "de" : "in"); + + if (low_latency) { + latency_state |= LOW_LATENCY_ENABLED; + } else { + latency_state &= ~LOW_LATENCY_ENABLED; + } + + if (err == -EALREADY) { + LOG_INF("Conn parameters were already updated"); + } + } else { + LOG_WRN("Failed to update conn parameters (err %d)", err); + } +} + +static void low_latency_check_fn(struct k_work *w) +{ + if (latency_state & LOW_LATENCY_REQUIRED) { + latency_state &= ~LOW_LATENCY_REQUIRED; + k_delayed_work_submit(&low_latency_check, + LOW_LATENCY_CHECK_PERIOD_MS); + } else { + LOG_INF("Low latency timed out"); + set_ble_latency(false); + } +} + +static void le_param_updated(struct bt_conn *conn, u16_t interval, + u16_t latency, u16_t timeout) +{ + if (latency == 0) { + latency_state |= LOW_LATENCY_ENABLED; + k_delayed_work_submit(&low_latency_check, + LOW_LATENCY_CHECK_PERIOD_MS); + } else { + latency_state &= ~LOW_LATENCY_ENABLED; + k_delayed_work_cancel(&low_latency_check); + } +} + +static void init(void) +{ + static struct bt_conn_cb conn_callbacks = { + .le_param_updated = le_param_updated, + }; + + bt_conn_cb_register(&conn_callbacks); + + k_delayed_work_init(&security_timeout, security_timeout_fn); + k_delayed_work_init(&low_latency_check, low_latency_check_fn); +} + +static bool event_handler(const struct event_header *eh) +{ + if (is_module_state_event(eh)) { + const struct module_state_event *event = + cast_module_state_event(eh); + + if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) { + static bool initialized; + + init(); + __ASSERT_NO_MSG(!initialized); + initialized = true; + } + + return false; + } + + if (is_ble_peer_event(eh)) { + const struct ble_peer_event *event = cast_ble_peer_event(eh); + + switch (event->state) { + case PEER_STATE_CONNECTED: + active_conn = event->id; + k_delayed_work_submit(&security_timeout, + SECURITY_FAIL_TIMEOUT_MS); + break; + + case PEER_STATE_DISCONNECTED: + __ASSERT_NO_MSG(active_conn == event->id); + active_conn = NULL; + + /* Clear BLE latency state. */ + latency_state = 0; + k_delayed_work_cancel(&low_latency_check); + + /* Fall-through */ + + case PEER_STATE_SECURED: + k_delayed_work_cancel(&security_timeout); + break; + + default: + /* Ignore. */ + break; + } + + return false; + } + + if (IS_ENABLED(CONFIG_DESKTOP_CONFIG_CHANNEL_ENABLE) && + (is_config_event(eh) || is_config_fetch_request_event(eh))) { + if (!active_conn) { + return false; + } + + if (latency_state & LOW_LATENCY_ENABLED) { + latency_state |= LOW_LATENCY_REQUIRED; + } else { + set_ble_latency(true); + } + + return false; + } + + /* If event is unhandled, unsubscribe. */ + __ASSERT_NO_MSG(false); + + return false; +} +EVENT_LISTENER(MODULE, event_handler); +EVENT_SUBSCRIBE(MODULE, module_state_event); +EVENT_SUBSCRIBE(MODULE, ble_peer_event); +#if CONFIG_DESKTOP_CONFIG_CHANNEL_ENABLE +EVENT_SUBSCRIBE(MODULE, config_event); +EVENT_SUBSCRIBE(MODULE, config_fetch_request_event); +#endif diff --git a/applications/nrf_desktop/src/modules/ble_qos.c b/applications/nrf_desktop/src/modules/ble_qos.c new file mode 100644 index 000000000000..862409e1235e --- /dev/null +++ b/applications/nrf_desktop/src/modules/ble_qos.c @@ -0,0 +1,743 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ble_controller_hci_vs.h" + +#include "chmap_filter.h" + +#define MODULE ble_qos +#include "module_state_event.h" +#include "ble_event.h" +#include "config_event.h" +#include "hid_event.h" + +#include +LOG_MODULE_REGISTER(MODULE, CONFIG_DESKTOP_BLE_QOS_LOG_LEVEL); + +#define INVALID_BLACKLIST 0xFFFF + +#if CONFIG_DESKTOP_BLE_QOS_STATS_PRINTOUT_ENABLE +# if DT_NORDIC_NRF_USBD_USBD_0_NUM_IN_ENDPOINTS < 4 +# error Too few USB IN Endpoints enabled. \ + Modify appropriate dts.overlay to increase num-in-endpoints to 4 or more +# endif +#endif + +#ifdef CONFIG_USB_CDC_ACM_DEVICE_NAME +#define USB_SERIAL_DEVICE_NAME CONFIG_USB_CDC_ACM_DEVICE_NAME +#else +#define USB_SERIAL_DEVICE_NAME "" +#endif + +#ifndef ROUNDED_DIV +#define ROUNDED_DIV(a, b) (((a) + ((b) / 2)) / (b)) +#endif /* ROUNDED_DIV */ + +#if CONFIG_DESKTOP_BLE_QOS_STATS_PRINTOUT_ENABLE +#define THREAD_STACK_SIZE \ + (CONFIG_DESKTOP_BLE_QOS_STACK_SIZE + \ + CONFIG_DESKTOP_BLE_QOS_STATS_PRINT_STACK_SIZE) +#else +#define THREAD_STACK_SIZE CONFIG_DESKTOP_BLE_QOS_STACK_SIZE +#endif + +#define THREAD_PRIORITY K_PRIO_PREEMPT(K_LOWEST_APPLICATION_THREAD_PRIO) + +static K_THREAD_STACK_DEFINE(thread_stack, THREAD_STACK_SIZE); +static struct k_thread thread; + +struct params_ble { + u16_t sample_count_min; + u8_t min_channel_count; + s16_t weight_crc_ok; + s16_t weight_crc_error; + u16_t ble_block_threshold; + u8_t eval_max_count; + u16_t eval_duration; + u16_t eval_keepout_duration; + u16_t eval_success_threshold; +} __packed; + +struct params_wifi { + s16_t wifi_rating_inc; + s16_t wifi_present_threshold; + s16_t wifi_active_threshold; +} __packed; + +struct params_chmap { + u8_t chmap[CHMAP_BLE_BITMASK_SIZE]; +} __packed; + +struct params_blacklist { + u16_t wifi_chn_bitmask; +} __packed; + +static u8_t chmap_instance_buf[CHMAP_FILTER_INST_SIZE]; +static struct chmap_instance *chmap_inst; +static u8_t current_chmap[CHMAP_BLE_BITMASK_SIZE] = CHMAP_BLE_BITMASK_DEFAULT; +static atomic_t processing; +static atomic_t new_blacklist; +static atomic_t params_updated; +static struct chmap_filter_params filter_params; +static struct k_mutex data_access_mutex; + +BUILD_ASSERT(sizeof(struct bt_hci_cp_le_set_host_chan_classif) == + sizeof(struct params_chmap)); +BUILD_ASSERT(sizeof(current_chmap) == sizeof(struct params_chmap)); +BUILD_ASSERT(THREAD_PRIORITY >= CONFIG_BT_HCI_TX_PRIO); + +static void ble_qos_thread_fn(void); + +static struct device *cdc_dev; +static u32_t cdc_dtr; + +static void ble_chn_stats_print(bool update_channel_map) +{ + char str[64]; + int str_len, part_len; + s16_t chn_rating; + u8_t chn_freq, chn_state; + + if (!IS_ENABLED(CONFIG_DESKTOP_BLE_QOS_STATS_PRINTOUT_ENABLE)) { + return; + } + + if (IS_ENABLED(CONFIG_UART_LINE_CTRL)) { + int err; + u32_t cdc_val; + + /* Repeated to monitor CDC state */ + err = uart_line_ctrl_get(cdc_dev, LINE_CTRL_DTR, &cdc_val); + if (!err) { + cdc_dtr = cdc_val; + } + + if (!cdc_dtr) { + return; + } + } + + /* Note: if UART FIFO overflows, expect a warning printout: */ + /* " usb_cdc_acm: Ring buffer full (...)" */ + /* USB_CDC_ACM_RINGBUF_SIZE can be increased to prevent an overflow */ + + if (update_channel_map) { + str_len = snprintf( + str, + sizeof(str), + "[%08" PRIu32 "]Channel map update\n", + (u32_t)k_cycle_get_32()); + if (str_len <= 0 || str_len > sizeof(str)) { + LOG_ERR("Encoding error"); + return; + } + uart_fifo_fill(cdc_dev, str, str_len); + } + + /* Channel state information print format: */ + /* BT_INFO={idx0_freq:idx0_state:idx0_rating, ... } */ + + str_len = snprintf(str, sizeof(str), "BT_INFO={"); + if (str_len <= 0 || str_len > sizeof(str)) { + LOG_ERR("Encoding error"); + return; + } + uart_fifo_fill(cdc_dev, str, str_len); + + str_len = 0; + for (u8_t i = 0; i < CHMAP_BLE_CHANNEL_COUNT; i++) { + chmap_filter_chn_info_get( + chmap_inst, + i, + &chn_state, + &chn_rating, + &chn_freq); + part_len = snprintf( + &str[str_len], + sizeof(str) - str_len, + "%" PRIu8 ":%" PRIu8 ":%" PRId16 ",", + chn_freq, + chn_state, + chn_rating); + str_len += part_len; + if (part_len <= 0 || str_len > sizeof(str)) { + LOG_ERR("Encoding error"); + return; + } + + if (str_len >= ((sizeof(str) * 2) / 3)) { + uart_fifo_fill(cdc_dev, str, str_len); + str_len = 0; + } + } + part_len = snprintf(&str[str_len], sizeof(str) - str_len, "}\n"); + str_len += part_len; + if (part_len <= 0 || str_len > sizeof(str)) { + LOG_ERR("Encoding error"); + return; + } + uart_fifo_fill(cdc_dev, str, str_len); +} + +static void hid_pkt_stats_print(u32_t ble_recv) +{ + static u32_t prev_ble; + static u32_t prev_timestamp; + u32_t timestamp; + u32_t time_diff, rate_diff; + u32_t ble_rate; + char str[64]; + int str_len; + + if (IS_ENABLED(CONFIG_UART_LINE_CTRL)) { + if (!cdc_dtr) { + return; + } + } + + timestamp = k_cycle_get_32(); + if (timestamp == prev_timestamp) { + LOG_WRN("Too high printout rate"); + return; + } + + time_diff = prev_timestamp < timestamp ? + (timestamp - prev_timestamp) : + (UINT32_MAX - prev_timestamp + timestamp); + prev_timestamp = timestamp; + rate_diff = prev_ble <= ble_recv ? + (ble_recv - prev_ble) : + (UINT32_MAX - prev_ble + ble_recv); + prev_ble = ble_recv; + ble_rate = ROUNDED_DIV( + (rate_diff * CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC), + time_diff); + + str_len = snprintf( + str, + sizeof(str), + "[%08" PRIu32 "]Rate:%04" PRIu32 "\n", + timestamp, + ble_rate); + if (str_len <= 0 || str_len > sizeof(str)) { + LOG_ERR("Encoding error"); + return; + } + uart_fifo_fill(cdc_dev, str, str_len); +} + +static bool is_my_config_id(u8_t config_id) +{ + return (GROUP_FIELD_GET(config_id) == EVENT_GROUP_SETUP) && + (MOD_FIELD_GET(config_id) == SETUP_MODULE_QOS); +} + +static struct config_fetch_event *fetch_event_qos_ble_params(void) +{ + struct chmap_filter_params chmap_params; + struct params_ble qos_ble_params; + struct config_fetch_event *fetch_event; + size_t pos = 0; + + fetch_event = new_config_fetch_event(sizeof(qos_ble_params)); + + chmap_filter_params_get(chmap_inst, &chmap_params); + + sys_put_le16(chmap_params.maintenance_sample_count, + &fetch_event->dyndata.data[pos]); + pos += sizeof(qos_ble_params.sample_count_min); + + fetch_event->dyndata.data[pos] = chmap_params.min_channel_count; + pos += sizeof(qos_ble_params.min_channel_count); + + sys_put_le16(chmap_params.ble_weight_crc_ok, + &fetch_event->dyndata.data[pos]); + pos += sizeof(qos_ble_params.weight_crc_ok); + + sys_put_le16(chmap_params.ble_weight_crc_error, + &fetch_event->dyndata.data[pos]); + pos += sizeof(qos_ble_params.weight_crc_error); + + sys_put_le16(chmap_params.ble_block_threshold, + &fetch_event->dyndata.data[pos]); + pos += sizeof(qos_ble_params.ble_block_threshold); + + fetch_event->dyndata.data[pos] = chmap_params.eval_max_count; + pos += sizeof(qos_ble_params.eval_max_count); + + sys_put_le16(chmap_params.eval_duration, + &fetch_event->dyndata.data[pos]); + pos += sizeof(qos_ble_params.eval_duration); + + sys_put_le16(chmap_params.eval_keepout_duration, + &fetch_event->dyndata.data[pos]); + pos += sizeof(qos_ble_params.eval_keepout_duration); + + sys_put_le16(chmap_params.eval_success_threshold, + &fetch_event->dyndata.data[pos]); + pos += sizeof(qos_ble_params.eval_success_threshold); + + return fetch_event; +} + +static struct config_fetch_event *fetch_event_qos_wifi_params(void) +{ + struct chmap_filter_params chmap_params; + struct params_wifi qos_wifi_params; + struct config_fetch_event *fetch_event; + size_t pos = 0; + + fetch_event = new_config_fetch_event(sizeof(qos_wifi_params)); + + chmap_filter_params_get(chmap_inst, &chmap_params); + + sys_put_le16(chmap_params.wifi_rating_inc, + &fetch_event->dyndata.data[pos]); + pos += sizeof(qos_wifi_params.wifi_rating_inc); + + sys_put_le16(chmap_params.wifi_present_threshold, + &fetch_event->dyndata.data[pos]); + pos += sizeof(qos_wifi_params.wifi_present_threshold); + + sys_put_le16(chmap_params.wifi_active_threshold, + &fetch_event->dyndata.data[pos]); + pos += sizeof(qos_wifi_params.wifi_active_threshold); + + return fetch_event; +} + +static struct config_fetch_event *fetch_event_qos_channel_map(void) +{ + struct config_fetch_event *fetch_event; + struct params_chmap qos_chmap; + + fetch_event = new_config_fetch_event(sizeof(qos_chmap)); + + k_mutex_lock(&data_access_mutex, K_FOREVER); + memcpy(fetch_event->dyndata.data, current_chmap, sizeof(qos_chmap)); + k_mutex_unlock(&data_access_mutex); + + return fetch_event; +} + +static struct config_fetch_event *fetch_event_qos_blacklist(void) +{ + struct config_fetch_event *fetch_event; + + fetch_event = new_config_fetch_event(sizeof(struct params_blacklist)); + + sys_put_le16(chmap_filter_wifi_blacklist_get(), + fetch_event->dyndata.data); + + return fetch_event; +} + +static void update_parameters( + const u8_t *qos_ble_params, + const u8_t *qos_wifi_params) +{ + struct params_ble ble_params; + struct params_wifi wifi_params; + size_t pos; + + k_mutex_lock(&data_access_mutex, K_FOREVER); + + if (qos_ble_params != NULL) { + pos = 0; + + filter_params.maintenance_sample_count = + sys_get_le16(&qos_ble_params[pos]); + pos += sizeof(ble_params.sample_count_min); + + filter_params.min_channel_count = qos_ble_params[pos]; + pos += sizeof(ble_params.min_channel_count); + + filter_params.ble_weight_crc_ok = + sys_get_le16(&qos_ble_params[pos]); + pos += sizeof(ble_params.weight_crc_ok); + + filter_params.ble_weight_crc_error = + sys_get_le16(&qos_ble_params[pos]); + pos += sizeof(ble_params.weight_crc_error); + + filter_params.ble_block_threshold = + sys_get_le16(&qos_ble_params[pos]); + pos += sizeof(ble_params.ble_block_threshold); + + filter_params.eval_max_count = qos_ble_params[pos]; + pos += sizeof(ble_params.eval_max_count); + + filter_params.eval_duration = + sys_get_le16(&qos_ble_params[pos]); + pos += sizeof(ble_params.eval_duration); + + filter_params.eval_keepout_duration = + sys_get_le16(&qos_ble_params[pos]); + pos += sizeof(ble_params.eval_keepout_duration); + + filter_params.eval_success_threshold = + sys_get_le16(&qos_ble_params[pos]); + pos += sizeof(ble_params.eval_success_threshold); + } + if (qos_wifi_params != NULL) { + pos = 0; + + filter_params.wifi_rating_inc = + sys_get_le16(&qos_ble_params[pos]); + pos += sizeof(wifi_params.wifi_rating_inc); + + filter_params.wifi_present_threshold = + sys_get_le16(&qos_ble_params[pos]); + pos += sizeof(wifi_params.wifi_present_threshold); + + filter_params.wifi_active_threshold = + sys_get_le16(&qos_ble_params[pos]); + pos += sizeof(wifi_params.wifi_active_threshold); + } + + atomic_set(¶ms_updated, true); + k_mutex_unlock(&data_access_mutex); +} + +static void update_blacklist(const u8_t *blacklist) +{ + atomic_set(&new_blacklist, sys_get_le16(blacklist)); +} + +static bool on_vs_evt(struct net_buf_simple *buf) +{ + u8_t *subevent_code; + hci_vs_evt_qos_conn_event_report_t *evt; + + subevent_code = net_buf_simple_pull_mem( + buf, + sizeof(*subevent_code)); + + switch (*subevent_code) { + case HCI_VS_SUBEVENT_CODE_QOS_CONN_EVENT_REPORT: + if (atomic_get(&processing)) { + /* Cheaper to skip this update */ + /* instead of using locks */ + return true; + } + + evt = (void *)buf->data; + + chmap_filter_crc_update( + chmap_inst, + evt->channel_index, + evt->crc_ok_count, + evt->crc_error_count); + return true; + default: + return false; + } +} + +static void enable_qos_reporting(void) +{ + int err; + struct net_buf *buf; + + err = bt_hci_register_vnd_evt_cb(on_vs_evt); + if (err) { + LOG_ERR("Failed to register HCI VS callback"); + return; + } + + hci_vs_cmd_qos_conn_event_report_enable_t *cmd_enable; + + buf = bt_hci_cmd_create(HCI_VS_OPCODE_CMD_QOS_CONN_EVENT_REPORT_ENABLE, + sizeof(*cmd_enable)); + if (!buf) { + LOG_ERR("Failed to enable HCI VS QoS"); + return; + } + + cmd_enable = net_buf_add(buf, sizeof(*cmd_enable)); + cmd_enable->enable = 1; + + err = bt_hci_cmd_send( + HCI_VS_OPCODE_CMD_QOS_CONN_EVENT_REPORT_ENABLE, + buf); + if (err) { + LOG_ERR("Failed to enable HCI VS QoS"); + return; + } +} + +static void handle_config_event(const struct config_event *event) +{ + switch (OPT_FIELD_GET(event->id)) { + case QOS_OPT_BLACKLIST: + if (event->dyndata.size != sizeof(struct params_blacklist)) { + LOG_WRN("Invalid size"); + } else { + update_blacklist(event->dyndata.data); + } + break; + case QOS_OPT_CHMAP: + /* Avoid updating channel map directly to */ + /* reduce complexity in interaction with */ + /* chmap filter lib */ + LOG_WRN("Not supported"); + break; + case QOS_OPT_PARAM_BLE: + if (event->dyndata.size != sizeof(struct params_ble)) { + LOG_WRN("Invalid size"); + } else { + update_parameters(event->dyndata.data, NULL); + } + break; + case QOS_OPT_PARAM_WIFI: + if (event->dyndata.size != sizeof(struct params_wifi)) { + LOG_WRN("Invalid size"); + } else { + update_parameters(NULL, event->dyndata.data); + } + break; + default: + LOG_WRN("Unknown opt"); + return; + } +} + +static bool event_handler(const struct event_header *eh) +{ + if (IS_ENABLED(CONFIG_DESKTOP_BLE_QOS_STATS_PRINTOUT_ENABLE) && + IS_ENABLED(CONFIG_DESKTOP_HID_MOUSE)) { + static s32_t hid_pkt_recv_count; + static u32_t cdc_notify_count; + + /* Count number of HID packets received via BLE. */ + /* Send stats printout via CDC every 100 packets. */ + + if (is_hid_mouse_event(eh)) { + hid_pkt_recv_count++; + cdc_notify_count++; + + if (cdc_notify_count == 100) { + hid_pkt_stats_print( + hid_pkt_recv_count); + cdc_notify_count = 0; + } + + return false; + } + } + + if (is_module_state_event(eh)) { + const struct module_state_event *event = + cast_module_state_event(eh); + + if (check_state( + event, + MODULE_ID(main), + MODULE_STATE_READY)) { + static bool initialized; + int err; + + __ASSERT_NO_MSG(!initialized); + + initialized = true; + + chmap_filter_init(); + + chmap_inst = + (struct chmap_instance *) chmap_instance_buf; + err = chmap_filter_instance_init( + chmap_inst, + sizeof(chmap_instance_buf)); + if (err) { + LOG_ERR("Failed to initialize filter"); + module_set_state(MODULE_STATE_ERROR); + return false; + } + + LOG_DBG("Chmap lib version: %s", + chmap_filter_version()); + + chmap_filter_params_get(chmap_inst, &filter_params); + + k_mutex_init(&data_access_mutex); + new_blacklist = INVALID_BLACKLIST; + atomic_set(¶ms_updated, false); + + if (IS_ENABLED(CONFIG_DESKTOP_BLE_QOS_STATS_PRINTOUT_ENABLE)) { + cdc_dev = device_get_binding( + USB_SERIAL_DEVICE_NAME "_0"); + __ASSERT_NO_MSG(cdc_dev != NULL); + /* CONFIG_UART_LINE_CTRL == 1: dynamic dtr */ + cdc_dtr = !IS_ENABLED(CONFIG_UART_LINE_CTRL); + } + + k_thread_create(&thread, thread_stack, + THREAD_STACK_SIZE, + (k_thread_entry_t)ble_qos_thread_fn, + NULL, NULL, NULL, + THREAD_PRIORITY, 0, K_NO_WAIT); + k_thread_name_set(&thread, MODULE_NAME "_thread"); + } + + if (check_state( + event, + MODULE_ID(ble_state), + MODULE_STATE_READY)) { + enable_qos_reporting(); + } + + return false; + } + + if (IS_ENABLED(CONFIG_DESKTOP_CONFIG_CHANNEL_ENABLE)) { + if (is_config_event(eh)) { + const struct config_event *event = + cast_config_event(eh); + + if (is_my_config_id(event->id)) { + handle_config_event(event); + } + + return false; + } + + if (is_config_fetch_request_event(eh)) { + const struct config_fetch_request_event *event = + cast_config_fetch_request_event(eh); + + if (is_my_config_id(event->id)) { + struct config_fetch_event *fetch_event; + + switch (OPT_FIELD_GET(event->id)) { + case QOS_OPT_BLACKLIST: + fetch_event = + fetch_event_qos_blacklist(); + break; + case QOS_OPT_CHMAP: + fetch_event = + fetch_event_qos_channel_map(); + break; + case QOS_OPT_PARAM_BLE: + fetch_event = + fetch_event_qos_ble_params(); + break; + case QOS_OPT_PARAM_WIFI: + fetch_event = + fetch_event_qos_wifi_params(); + break; + default: + LOG_WRN("Unknown opt"); + return false; + } + + fetch_event->id = event->id; + fetch_event->recipient = event->recipient; + fetch_event->channel_id = event->channel_id; + + EVENT_SUBMIT(fetch_event); + } + + return false; + } + } + + /* If event is unhandled, unsubscribe. */ + __ASSERT_NO_MSG(false); + + return false; +} + +static void apply_new_params(void) +{ + int err; + + k_mutex_lock(&data_access_mutex, K_FOREVER); + /* chmap_filter_params_set returns immediately */ + err = chmap_filter_params_set(chmap_inst, &filter_params); + atomic_set(¶ms_updated, false); + k_mutex_unlock(&data_access_mutex); + + if (err) { + LOG_WRN("Param update failed"); + } +} + +static void ble_qos_thread_fn(void) +{ + while (true) { + bool update_channel_map; + int err; + + k_sleep(CONFIG_DESKTOP_BLE_QOS_INTERVAL); + + /* Check and apply new parameters received via config channel */ + if (atomic_get(¶ms_updated)) { + apply_new_params(); + } + + /* Check and apply new blacklist received via config channel */ + u16_t blacklist_update = + (u16_t) atomic_set(&new_blacklist, INVALID_BLACKLIST); + + if (blacklist_update != INVALID_BLACKLIST) { + err = chmap_filter_blacklist_set( + chmap_inst, + blacklist_update); + if (err) { + LOG_WRN("Blacklist update failed"); + } + } + + /* Run processing function. */ + /* Atomic variable is used as data busy flag */ + /* (this thread runs at the lowest priority) */ + atomic_set(&processing, true); + update_channel_map = chmap_filter_process(chmap_inst); + atomic_set(&processing, false); + + ble_chn_stats_print(update_channel_map); + + if (!update_channel_map) { + continue; + } + + u8_t *chmap; + + chmap = chmap_filter_suggested_map_get(chmap_inst); + err = bt_le_set_chan_map(chmap); + if (err) { + LOG_WRN("bt_le_set_chan_map: %d", err); + } else { + LOG_DBG("Channel map update"); + chmap_filter_suggested_map_confirm(chmap_inst); + + k_mutex_lock(&data_access_mutex, K_FOREVER); + memcpy(current_chmap, chmap, sizeof(current_chmap)); + k_mutex_unlock(&data_access_mutex); + } + } +} + +EVENT_LISTENER(MODULE, event_handler); +EVENT_SUBSCRIBE(MODULE, module_state_event); +#if CONFIG_DESKTOP_BLE_QOS_STATS_PRINTOUT_ENABLE +EVENT_SUBSCRIBE(MODULE, hid_mouse_event); +#endif +#if CONFIG_DESKTOP_CONFIG_CHANNEL_ENABLE +EVENT_SUBSCRIBE(MODULE, config_event); +EVENT_SUBSCRIBE(MODULE, config_fetch_request_event); +#endif diff --git a/applications/nrf_desktop/src/modules/ble_scan.c b/applications/nrf_desktop/src/modules/ble_scan.c index 940c4d8a5325..671378867c85 100644 --- a/applications/nrf_desktop/src/modules/ble_scan.c +++ b/applications/nrf_desktop/src/modules/ble_scan.c @@ -9,6 +9,8 @@ #include #include #include +#include + #include #define MODULE ble_scan @@ -43,6 +45,7 @@ static struct bt_conn *discovering_peer_conn; static unsigned int scan_counter; static struct k_delayed_work scan_start_trigger; static struct k_delayed_work scan_stop_trigger; +static bool peers_only = !IS_ENABLED(CONFIG_DESKTOP_BLE_NEW_PEER_SCAN_ON_BOOT); static bool scanning; @@ -66,6 +69,26 @@ static size_t count_conn(void) return conn_count; } +static size_t count_bond(void) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(subscribed_peers); i++) { + if (!bt_addr_le_cmp(&subscribed_peers[i].addr, BT_ADDR_LE_NONE)) { + break; + } + } + + return i; +} + +static void broadcast_scan_state(bool active) +{ + struct ble_peer_search_event *event = new_ble_peer_search_event(); + event->active = active; + EVENT_SUBMIT(event); +} + static void scan_stop(void) { int err = bt_scan_stop(); @@ -81,6 +104,8 @@ static void scan_stop(void) } scanning = false; + broadcast_scan_state(scanning); + k_delayed_work_cancel(&scan_stop_trigger); if (count_conn() < CONFIG_BT_MAX_CONN) { @@ -89,32 +114,29 @@ static void scan_stop(void) } } -static int configure_filters(void) +static int configure_address_filters(u8_t *filter_mode) { - bt_scan_filter_remove_all(); - BUILD_ASSERT_MSG(CONFIG_BT_MAX_PAIRED == CONFIG_BT_MAX_CONN, ""); - BUILD_ASSERT_MSG(CONFIG_BT_MAX_PAIRED <= CONFIG_BT_SCAN_ADDRESS_CNT, - "Insufficient number of address filters"); - BUILD_ASSERT_MSG(ARRAY_SIZE(peer_type_short_name) <= - CONFIG_BT_SCAN_SHORT_NAME_CNT, - "Insufficient number of short name filers"); - BUILD_ASSERT_MSG(ARRAY_SIZE(peer_type_short_name) == PEER_TYPE_COUNT, - ""); - - u8_t filter_mode = 0; - int err; size_t i; + int err = 0; for (i = 0; i < ARRAY_SIZE(subscribed_peers); i++) { if (!bt_addr_le_cmp(&subscribed_peers[i].addr, BT_ADDR_LE_NONE)) { break; } + struct bt_conn *conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, + &subscribed_peers[i].addr); + + if (conn) { + bt_conn_unref(conn); + continue; + } + err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_ADDR, &subscribed_peers[i].addr); if (err) { LOG_ERR("Address filter cannot be added (err %d)", err); - return err; + break; } char addr_str[BT_ADDR_LE_STR_LEN]; @@ -122,14 +144,16 @@ static int configure_filters(void) bt_addr_le_to_str(&subscribed_peers[i].addr, addr_str, sizeof(addr_str)); LOG_INF("Address filter added %s", log_strdup(addr_str)); - filter_mode |= BT_SCAN_ADDR_FILTER; + *filter_mode |= BT_SCAN_ADDR_FILTER; } - if (i == CONFIG_BT_MAX_PAIRED) { - goto filter_enable; - } + return err; +} +static int configure_short_name_filters(u8_t *filter_mode) +{ u8_t peers_mask = 0; + int err = 0; for (size_t i = 0; i < ARRAY_SIZE(subscribed_peers); i++) { peers_mask |= BIT(subscribed_peers[i].peer_type); @@ -150,31 +174,69 @@ static int configure_filters(void) &filter); if (err) { LOG_ERR("Name filter cannot be added (err %d)", err); - return err; + break; } - filter_mode |= BT_SCAN_SHORT_NAME_FILTER; + *filter_mode |= BT_SCAN_SHORT_NAME_FILTER; } - LOG_INF("Device type filters added"); -filter_enable: - err = bt_scan_filter_enable(filter_mode, false); - if (err) { + if (!err) { + LOG_INF("Device type filters added"); + } + + return err; +} + +static int configure_filters(void) +{ + BUILD_ASSERT_MSG(CONFIG_BT_MAX_PAIRED == CONFIG_BT_MAX_CONN, ""); + BUILD_ASSERT_MSG(CONFIG_BT_MAX_PAIRED <= CONFIG_BT_SCAN_ADDRESS_CNT, + "Insufficient number of address filters"); + BUILD_ASSERT_MSG(ARRAY_SIZE(peer_type_short_name) <= + CONFIG_BT_SCAN_SHORT_NAME_CNT, + "Insufficient number of short name filers"); + BUILD_ASSERT_MSG(ARRAY_SIZE(peer_type_short_name) == PEER_TYPE_COUNT, + ""); + bt_scan_filter_remove_all(); + + u8_t filter_mode = 0; + int err = configure_address_filters(&filter_mode); + + bool use_name_filters = true; + + if (IS_ENABLED(CONFIG_DESKTOP_BLE_NEW_PEER_SCAN_REQUEST) && + peers_only) { + use_name_filters = false; + } + + if (!err && use_name_filters && + (count_bond() < CONFIG_BT_MAX_PAIRED)) { + err = configure_short_name_filters(&filter_mode); + } + + if (!err) { + err = bt_scan_filter_enable(filter_mode, false); + } else { LOG_ERR("Filters cannot be turned on (err %d)", err); - return err; } - return 0; + return err; } static void scan_start(void) { + size_t conn_count = count_conn(); + size_t bond_count = count_bond(); int err; - if (count_conn() == CONFIG_BT_MAX_CONN) { + if (IS_ENABLED(CONFIG_DESKTOP_BLE_NEW_PEER_SCAN_REQUEST) && + (conn_count == bond_count) && peers_only) { + LOG_INF("All known peers connected - scanning disabled"); + return; + } else if (conn_count == CONFIG_BT_MAX_CONN) { LOG_INF("Max number of peers connected - scanning disabled"); return; } else if (discovering_peer_conn) { - LOG_INF("Discovery in progress - scanning disabled"); + LOG_INF("Discovery in progress"); return; } @@ -195,6 +257,7 @@ static void scan_start(void) } scanning = true; + broadcast_scan_state(scanning); k_delayed_work_submit(&scan_stop_trigger, SCAN_DURATION_MS); k_delayed_work_cancel(&scan_start_trigger); @@ -224,6 +287,7 @@ static void scan_stop_trigger_fn(struct k_work *w) BUILD_ASSERT_MSG(SCAN_DURATION_MS > 0, ""); if (count_conn() != 0) { + peers_only = true; scan_stop(); } } @@ -245,7 +309,6 @@ static void scan_filter_match(struct bt_scan_device_info *device_info, static void scan_connecting_error(struct bt_scan_device_info *device_info) { LOG_WRN("Connecting failed"); - /* Restarting scan. */ scan_start(); } @@ -264,17 +327,14 @@ static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param) { LOG_INF("Connection parameter update request"); - param->interval_min = 1; - param->interval_max = 1; - - if (bt_le_conn_params_valid(param)) { - LOG_INF("Low latency operation"); - } else { - LOG_INF("Normal operation"); - param->interval_min = 6; - param->interval_max = 6; + if (IS_ENABLED(CONFIG_BT_LL_NRFXLIB)) { + LOG_INF("Keep LLPM params"); + return false; } + param->interval_min = 6; + param->interval_max = 6; + return true; } @@ -407,6 +467,26 @@ static void scan_init(void) k_delayed_work_init(&scan_stop_trigger, scan_stop_trigger_fn); } +static void enable_llpm(struct bt_conn *conn) +{ + if (IS_ENABLED(CONFIG_BT_LL_NRFXLIB)) { + struct bt_le_conn_param param = { + .interval_min = 0x0D01, + .interval_max = 0x0D01, + .latency = 99, + .timeout = 400 + }; + + int err = bt_conn_le_param_update(conn, ¶m); + + if (err) { + LOG_ERR("Cannot set LLPM params (err:%d)", err); + } else { + LOG_INF("LLPM params set"); + } + } +} + static bool event_handler(const struct event_header *eh) { if ((IS_ENABLED(CONFIG_DESKTOP_HID_MOUSE) && is_hid_mouse_event(eh)) || @@ -440,10 +520,10 @@ static bool event_handler(const struct event_header *eh) static bool started; __ASSERT_NO_MSG(!started); + started = true; /* Settings need to be loaded before scan start */ scan_start(); - started = true; } return false; @@ -465,7 +545,11 @@ static bool event_handler(const struct event_header *eh) discovering_peer_conn = NULL; } scan_stop(); - scan_start(); + /* ble_state keeps reference to connection object. + * Cannot create new connection now. + */ + k_delayed_work_submit(&scan_start_trigger, 0); + scan_counter = SCAN_TRIG_TIMEOUT_MS; break; default: __ASSERT_NO_MSG(false); @@ -479,11 +563,14 @@ static bool event_handler(const struct event_header *eh) cast_ble_peer_operation_event(eh); switch (event->op) { - case PEER_OPERATION_ERASED: - scan_stop(); reset_subscribers(); store_subscribed_peers(); + /* Fall-through */ + + case PEER_OPERATION_SCAN_REQUEST: + peers_only = false; + scan_stop(); scan_start(); break; @@ -537,6 +624,8 @@ static bool event_handler(const struct event_header *eh) SCAN_TRIG_TIMEOUT_MS); scan_counter = SCAN_TRIG_TIMEOUT_MS; + enable_llpm(bt_gatt_dm_conn_get(event->dm)); + return false; } diff --git a/applications/nrf_desktop/src/modules/ble_state.c b/applications/nrf_desktop/src/modules/ble_state.c index 7640e4a7fd84..2c727d1f2dff 100644 --- a/applications/nrf_desktop/src/modules/ble_state.c +++ b/applications/nrf_desktop/src/modules/ble_state.c @@ -10,21 +10,20 @@ #include #include #include +#include #include "ble_event.h" +#ifdef CONFIG_BT_LL_NRFXLIB +#include "ble_controller_hci_vs.h" +#endif + #define MODULE ble_state #include "module_state_event.h" #include LOG_MODULE_REGISTER(MODULE, CONFIG_DESKTOP_BLE_STATE_LOG_LEVEL); -#ifdef CONFIG_BT_PERIPHERAL -#define SECURITY_FAIL_TIMEOUT_MS \ - K_SECONDS(CONFIG_DESKTOP_BLE_SECURITY_FAIL_TIMEOUT_S) -#else -#define SECURITY_FAIL_TIMEOUT_MS 0 -#endif struct bond_find_data { bt_addr_le_t peer_address; @@ -32,24 +31,9 @@ struct bond_find_data { u8_t peer_count; }; -static struct k_delayed_work security_timeout; static struct bt_conn *active_conn[CONFIG_BT_MAX_CONN]; -static void security_timeout_fn(struct k_work *w) -{ - BUILD_ASSERT_MSG(!IS_ENABLED(CONFIG_BT_PERIPHERAL) || - (ARRAY_SIZE(active_conn) == 1), ""); - BUILD_ASSERT_MSG(!IS_ENABLED(CONFIG_BT_PERIPHERAL) || - (SECURITY_FAIL_TIMEOUT_MS > 0), ""); - __ASSERT_NO_MSG(active_conn[0]); - - bt_conn_disconnect(active_conn[0], - BT_HCI_ERR_REMOTE_USER_TERM_CONN); - - LOG_WRN("Security establishment failed - device disconnected"); -} - static void bond_find(const struct bt_bond_info *info, void *user_data) { struct bond_find_data *bond_find_data = user_data; @@ -62,14 +46,32 @@ static void bond_find(const struct bt_bond_info *info, void *user_data) bond_find_data->peer_count++; } +static void disconnect_peer(struct bt_conn *conn) +{ + int err = bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + + if (err == -ENOTCONN) { + err = 0; + } + LOG_WRN("Device %s", err ? "failed to disconnect" : "disconnected"); + + if (err) { + module_set_state(MODULE_STATE_ERROR); + } +} + static void connected(struct bt_conn *conn, u8_t error) { + /* Make sure that connection will remain valid. */ + bt_conn_ref(conn); + char addr_str[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(bt_conn_get_dst(conn), addr_str, sizeof(addr_str)); if (error) { struct ble_peer_event *event = new_ble_peer_event(); + event->id = conn; event->state = PEER_STATE_CONN_FAILED; EVENT_SUBMIT(event); @@ -81,9 +83,28 @@ static void connected(struct bt_conn *conn, u8_t error) LOG_INF("Connected to %s", log_strdup(addr_str)); + size_t i; + + for (i = 0; i < ARRAY_SIZE(active_conn); i++) { + if (!active_conn[i]) { + break; + } + } + if (i >= ARRAY_SIZE(active_conn)) { + k_panic(); + } + active_conn[i] = conn; + + struct ble_peer_event *event = new_ble_peer_event(); + + event->id = conn; + event->state = PEER_STATE_CONNECTED; + EVENT_SUBMIT(event); + struct bt_conn_info info; int err = bt_conn_get_info(conn, &info); + if (err) { LOG_WRN("Cannot get conn info"); goto disconnect; @@ -91,11 +112,6 @@ static void connected(struct bt_conn *conn, u8_t error) if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && (info.role == BT_CONN_ROLE_SLAVE)) { - /* Assert one local identity holds exactly one bond. - * One local identity is unused. - */ - __ASSERT_NO_MSG(CONFIG_BT_MAX_PAIRED == CONFIG_BT_ID_MAX - 1); - struct bond_find_data bond_find_data = { .peer_id = 0, .peer_count = 0, @@ -112,27 +128,6 @@ static void connected(struct bt_conn *conn, u8_t error) LOG_INF("Already bonded to %s", log_strdup(addr_str)); goto disconnect; } - } - - size_t i; - - for (i = 0; i < ARRAY_SIZE(active_conn); i++) { - if (!active_conn[i]) { - break; - } - } - if (i >= ARRAY_SIZE(active_conn)) { - k_panic(); - } - active_conn[i] = conn; - - struct ble_peer_event *event = new_ble_peer_event(); - event->id = conn; - event->state = PEER_STATE_CONNECTED; - EVENT_SUBMIT(event); - - if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && - (info.role == BT_CONN_ROLE_SLAVE)) { /* Security must be enabled after peer event is sent. * This is to make sure notification events are propagated * in the right order. @@ -144,15 +139,12 @@ static void connected(struct bt_conn *conn, u8_t error) LOG_ERR("Failed to set security"); goto disconnect; } - k_delayed_work_submit(&security_timeout, - SECURITY_FAIL_TIMEOUT_MS); } return; disconnect: - LOG_WRN("Disconnect"); - bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + disconnect_peer(conn); } static void disconnected(struct bt_conn *conn, u8_t reason) @@ -172,6 +164,7 @@ static void disconnected(struct bt_conn *conn, u8_t reason) } if (i == ARRAY_SIZE(active_conn)) { + __ASSERT_NO_MSG(false); return; } @@ -182,10 +175,6 @@ static void disconnected(struct bt_conn *conn, u8_t reason) event->id = conn; event->state = PEER_STATE_DISCONNECTED; EVENT_SUBMIT(event); - - if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) { - k_delayed_work_cancel(&security_timeout); - } } static struct bt_gatt_exchange_params exchange_params; @@ -201,9 +190,9 @@ static void security_changed(struct bt_conn *conn, bt_security_t level, { if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) { __ASSERT_NO_MSG(active_conn[0] == conn); - k_delayed_work_cancel(&security_timeout); } + int err; char addr[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); @@ -214,8 +203,7 @@ static void security_changed(struct bt_conn *conn, bt_security_t level, LOG_WRN("Security with %s failed, level %u err %d", log_strdup(addr), level, bt_err); if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) { - bt_conn_disconnect(active_conn[0], - BT_HCI_ERR_REMOTE_USER_TERM_CONN); + disconnect_peer(conn); } return; @@ -228,7 +216,7 @@ static void security_changed(struct bt_conn *conn, bt_security_t level, struct bt_conn_info info; - int err = bt_conn_get_info(conn, &info); + err = bt_conn_get_info(conn, &info); if (err) { LOG_WRN("Cannot get conn info"); } else { @@ -271,11 +259,32 @@ static void bt_ready(int err) LOG_INF("Bluetooth initialized"); +#ifdef CONFIG_BT_LL_NRFXLIB + hci_vs_cmd_llpm_mode_set_t *p_cmd_enable; + + struct net_buf *buf = bt_hci_cmd_create(HCI_VS_OPCODE_CMD_LLPM_MODE_SET, + sizeof(*p_cmd_enable)); + + p_cmd_enable = net_buf_add(buf, sizeof(*p_cmd_enable)); + p_cmd_enable->enable = 1; + + int hci_err = bt_hci_cmd_send(HCI_VS_OPCODE_CMD_LLPM_MODE_SET, buf); + + if (hci_err) { + LOG_ERR("Error enabling LLPM (err:%" PRIu8 ")", hci_err); + } else { + LOG_INF("LLPM enabled"); + } +#endif + module_set_state(MODULE_STATE_READY); } static int ble_state_init(void) { + BUILD_ASSERT(!IS_ENABLED(CONFIG_BT_PERIPHERAL) || + (ARRAY_SIZE(active_conn) == 1)); + static struct bt_conn_cb conn_callbacks = { .connected = connected, .disconnected = disconnected, @@ -291,7 +300,8 @@ static int ble_state_init(void) static bool event_handler(const struct event_header *eh) { if (is_module_state_event(eh)) { - struct module_state_event *event = cast_module_state_event(eh); + const struct module_state_event *event = + cast_module_state_event(eh); if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) { static bool initialized; @@ -299,10 +309,6 @@ static bool event_handler(const struct event_header *eh) __ASSERT_NO_MSG(!initialized); initialized = true; - if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) { - k_delayed_work_init(&security_timeout, - security_timeout_fn); - } if (ble_state_init()) { LOG_ERR("Cannot initialize"); } @@ -311,6 +317,23 @@ static bool event_handler(const struct event_header *eh) return false; } + if (is_ble_peer_event(eh)) { + const struct ble_peer_event *event = cast_ble_peer_event(eh); + + switch (event->state) { + case PEER_STATE_CONN_FAILED: + case PEER_STATE_DISCONNECTED: + /* Connection object is no longer in use. */ + bt_conn_unref(event->id); + break; + default: + /* Ignore. */ + break; + } + + return false; + } + /* If event is unhandled, unsubscribe. */ __ASSERT_NO_MSG(false); @@ -318,3 +341,4 @@ static bool event_handler(const struct event_header *eh) } EVENT_LISTENER(MODULE, event_handler); EVENT_SUBSCRIBE(MODULE, module_state_event); +EVENT_SUBSCRIBE_FINAL(MODULE, ble_peer_event); diff --git a/applications/nrf_desktop/src/modules/config.c b/applications/nrf_desktop/src/modules/config.c index a88540cc532a..bfcb389965c8 100644 --- a/applications/nrf_desktop/src/modules/config.c +++ b/applications/nrf_desktop/src/modules/config.c @@ -21,7 +21,7 @@ LOG_MODULE_REGISTER(MODULE, CONFIG_DESKTOP_CONFIG_LOG_LEVEL); #define KEY_LEN 11 /* Array of pointers to data loaded from settings */ -#define LOADED_DATA_MAX SENSOR_OPT_COUNT +#define LOADED_DATA_MAX MAX(SENSOR_OPT_COUNT, QOS_OPT_COUNT) static struct config_event *loaded_data[LOADED_DATA_MAX]; @@ -37,6 +37,14 @@ static bool config_id_is_supported(u8_t event_id) } } + if (IS_ENABLED(CONFIG_DESKTOP_BLE_QOS_ENABLE)) { + if ((MOD_FIELD_GET(event_id) == SETUP_MODULE_QOS) && + ((OPT_FIELD_GET(event_id) == QOS_OPT_PARAM_BLE) || + (OPT_FIELD_GET(event_id) == QOS_OPT_PARAM_WIFI))) { + return true; + } + } + return false; } diff --git a/applications/nrf_desktop/src/modules/dfu.c b/applications/nrf_desktop/src/modules/dfu.c index c19fb7009e03..28486363f396 100644 --- a/applications/nrf_desktop/src/modules/dfu.c +++ b/applications/nrf_desktop/src/modules/dfu.c @@ -11,11 +11,9 @@ #include #include #include -#include #include "event_manager.h" #include "config_event.h" -#include "ble_event.h" #define MODULE dfu #include "module_state_event.h" @@ -24,21 +22,13 @@ LOG_MODULE_REGISTER(MODULE, CONFIG_DESKTOP_CONFIG_CHANNEL_DFU_LOG_LEVEL); -#if defined(CONFIG_BT_PERIPHERAL) -#define DEFAULT_LATENCY CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY -#else -#define DEFAULT_LATENCY 0 -#endif - - #define FLASH_PAGE_SIZE_LOG2 12 #define FLASH_PAGE_SIZE BIT(FLASH_PAGE_SIZE_LOG2) #define FLASH_PAGE_ID(off) ((off) >> FLASH_PAGE_SIZE_LOG2) -#define DFU_TIMEOUT K_SECONDS(2) -#define REBOOT_REQUEST_TIMEOUT K_MSEC(250) +#define DFU_TIMEOUT K_SECONDS(2) +#define REBOOT_REQUEST_TIMEOUT K_MSEC(250) -static struct bt_conn *active_conn; static struct k_delayed_work dfu_timeout; static struct k_delayed_work reboot_request; @@ -48,46 +38,10 @@ static u32_t img_csum; static u32_t img_length; -static void set_ble_latency(bool low_latency) -{ - if (!active_conn) { - LOG_INF("No active_connection"); - return; - } - - struct bt_conn_info info; - - int err = bt_conn_get_info(active_conn, &info); - if (err) { - LOG_WRN("Cannot get conn info (%d)", err); - return; - } - - if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && - (info.role == BT_CONN_ROLE_SLAVE)) { - const struct bt_le_conn_param param = { - .interval_min = info.le.interval, - .interval_max = info.le.interval, - .latency = (low_latency) ? (0) : (DEFAULT_LATENCY), - .timeout = info.le.timeout - }; - - err = bt_conn_le_param_update(active_conn, ¶m); - if (err) { - LOG_WRN("Cannot update parameters (%d)", err); - return; - } - } - - LOG_INF("BLE latency %screased", low_latency ? "de" : "in"); -} - static void dfu_timeout_handler(struct k_work *work) { LOG_WRN("DFU timed out"); - set_ble_latency(false); - if (flash_area) { flash_area_close(flash_area); flash_area = NULL; @@ -164,7 +118,6 @@ static void handle_dfu_data(const struct config_event *event) dfu_finish: flash_area_close(flash_area); flash_area = NULL; - set_ble_latency(false); k_delayed_work_cancel(&dfu_timeout); } @@ -243,8 +196,6 @@ static void handle_dfu_start(const struct config_event *event) flash_area = NULL; } else { LOG_INF("DFU started"); - - set_ble_latency(true); } k_delayed_work_submit(&dfu_timeout, DFU_TIMEOUT); @@ -286,9 +237,6 @@ static void handle_reboot_request(const struct config_fetch_request_event *event { LOG_INF("System reboot requested"); - /* Decrease latency to ensure device will send response in time. */ - set_ble_latency(true); - struct config_fetch_event *fetch_event = new_config_fetch_event(0); @@ -423,31 +371,6 @@ static bool event_handler(const struct event_header *eh) return false; } - if (is_ble_peer_event(eh)) { - struct ble_peer_event *event = cast_ble_peer_event(eh); - - switch (event->state) { - case PEER_STATE_CONNECTED: - active_conn = event->id; - break; - - case PEER_STATE_DISCONNECTED: - active_conn = NULL; - break; - - case PEER_STATE_SECURED: - case PEER_STATE_CONN_FAILED: - /* No action */ - break; - - default: - __ASSERT_NO_MSG(false); - break; - } - - return false; - } - if (is_module_state_event(eh)) { const struct module_state_event *event = cast_module_state_event(eh); @@ -474,4 +397,3 @@ EVENT_LISTENER(MODULE, event_handler); EVENT_SUBSCRIBE_EARLY(MODULE, config_event); EVENT_SUBSCRIBE(MODULE, config_fetch_request_event); EVENT_SUBSCRIBE(MODULE, module_state_event); -EVENT_SUBSCRIBE(MODULE, ble_peer_event); diff --git a/applications/nrf_desktop/src/modules/hid_forward.c b/applications/nrf_desktop/src/modules/hid_forward.c index c9c90e923375..f56b7276f4ea 100644 --- a/applications/nrf_desktop/src/modules/hid_forward.c +++ b/applications/nrf_desktop/src/modules/hid_forward.c @@ -8,6 +8,7 @@ #include #include +#include #include #define MODULE hid_forward @@ -24,6 +25,12 @@ #include LOG_MODULE_REGISTER(MODULE, CONFIG_DESKTOP_BLE_SCANNING_LOG_LEVEL); +#define LATENCY_LOW 0 +#define LATENCY_DEFAULT 99 + +#define FORWARD_TIMEOUT K_SECONDS(5) +#define FORWARD_WORK_DELAY K_SECONDS(1) + struct keyboard_event_item { sys_snode_t node; struct hid_keyboard_event *evt; @@ -37,8 +44,11 @@ struct consumer_ctrl_event_item { struct hids_subscriber { struct bt_gatt_hids_c hidc; u16_t pid; + u32_t timestamp; }; +static struct k_delayed_work config_fwd_timeout; + static struct hids_subscriber subscribers[CONFIG_BT_MAX_CONN]; static const void *usb_id; static atomic_t usb_ready; @@ -52,6 +62,80 @@ static sys_slist_t consumer_ctrl_event_list; static struct k_spinlock lock; +static int change_connection_latency(struct hids_subscriber *subscriber, u16_t latency) +{ + __ASSERT_NO_MSG(subscriber != NULL); + + struct bt_conn *conn = bt_gatt_hids_c_conn(&subscriber->hidc); + + if (!conn) { + LOG_WRN("There is no connection for a HIDS subscriber: %" + PRIx16, subscriber->pid); + return -ENXIO; + } + + struct bt_conn_info info; + int err = bt_conn_get_info(conn, &info); + + if (err) { + LOG_WRN("Cannot get conn info (%d)", err); + return err; + } + + __ASSERT_NO_MSG(info.role == BT_CONN_ROLE_MASTER); + + const struct bt_le_conn_param param = { + .interval_min = info.le.interval, + .interval_max = info.le.interval, + .latency = latency, + .timeout = info.le.timeout + }; + + err = bt_conn_le_param_update(conn, ¶m); + if (err == -EALREADY) { + LOG_WRN("Slave latency already changed"); + err = 0; + } else if (err) { + LOG_WRN("Cannot update parameters (%d)", err); + } else { + LOG_INF("BLE latency changed to: %"PRIu16, latency); + } + + return err; +} + +static void forward_config_timeout(struct k_work *work_desc) +{ + __ASSERT_NO_MSG(work_desc != NULL); + + u32_t cur_time = k_uptime_get_32(); + bool forward_is_pending = false; + int err = 0; + + for (size_t idx = 0; idx < ARRAY_SIZE(subscribers); idx++) { + if ((subscribers[idx].timestamp != 0) && + (bt_gatt_hids_c_assign_check(&subscribers[idx].hidc))) { + if ((cur_time - subscribers[idx].timestamp) > FORWARD_TIMEOUT) { + LOG_WRN("Configuration forward timed out"); + + err = change_connection_latency(&subscribers[idx], LATENCY_DEFAULT); + if (err) { + LOG_WRN("Can't set latency to default"); + continue; + } + + subscribers[idx].timestamp = 0; + } else { + forward_is_pending = true; + } + } + } + + if (forward_is_pending) { + k_delayed_work_submit(&config_fwd_timeout, FORWARD_WORK_DELAY); + } +} + static void process_mouse_report(const u8_t *data) { u8_t button_bm = data[0]; @@ -234,6 +318,8 @@ static void init(void) bt_gatt_hids_c_init(&subscribers[i].hidc, ¶ms); } sys_slist_init(&keyboard_event_list); + + k_delayed_work_init(&config_fwd_timeout, forward_config_timeout); } static int register_subscriber(struct bt_gatt_dm *dm, u16_t pid) @@ -318,16 +404,175 @@ static u8_t hidc_read_cfg(struct bt_gatt_hids_c *hidc, return 0; } -static struct bt_gatt_hids_c *find_subscriber_hidc(u16_t pid) +static struct hids_subscriber *find_subscriber_hidc(u16_t pid) { for (size_t i = 0; i < ARRAY_SIZE(subscribers); i++) { if (subscribers[i].pid == pid) { - return &subscribers[i].hidc; + return &subscribers[i]; } } return NULL; } +static int switch_to_low_latency(struct hids_subscriber *subscriber) +{ + int err; + + if (IS_ENABLED(CONFIG_BT_LL_NRFXLIB)) { + if (subscriber->timestamp == 0) { + LOG_INF("DFU next forward"); + err = change_connection_latency(subscriber, LATENCY_LOW); + if (err) { + LOG_WRN("Error while change of connection parameters"); + return err; + } + } + subscriber->timestamp = k_uptime_get_32(); + + if (!k_delayed_work_remaining_get(&config_fwd_timeout)) { + k_delayed_work_submit(&config_fwd_timeout, + FORWARD_WORK_DELAY); + } + } + + return err; +} + +static void handle_config_forward(const struct config_forward_event *event) +{ + struct hids_subscriber *subscriber = find_subscriber_hidc(event->recipient); + + if (!subscriber) { + LOG_INF("Recipent %02" PRIx16 "not found", event->recipient); + return; + } + + struct bt_gatt_hids_c *recipient_hidc = &subscriber->hidc; + + __ASSERT_NO_MSG(recipient_hidc != NULL); + + if (!bt_gatt_hids_c_ready_check(recipient_hidc)) { + LOG_WRN("Cannot forward, peer disconnected"); + + notify_config_forwarded(CONFIG_STATUS_DISCONNECTED_ERROR); + return; + } + + struct bt_gatt_hids_c_rep_info *config_rep = + bt_gatt_hids_c_rep_find(recipient_hidc, + BT_GATT_HIDS_REPORT_TYPE_FEATURE, + REPORT_ID_USER_CONFIG); + + if (!config_rep) { + LOG_ERR("Feature report not found"); + notify_config_forwarded(CONFIG_STATUS_WRITE_ERROR); + return; + } + + if (event->dyndata.size > UCHAR_MAX) { + LOG_WRN("Event data too big"); + return; + } + + struct config_channel_frame frame; + u8_t report[REPORT_SIZE_USER_CONFIG]; + + if (event->status == CONFIG_STATUS_FETCH) { + LOG_INF("Forwarding fetch request"); + frame.status = CONFIG_STATUS_FETCH; + } + + frame.recipient = event->recipient; + frame.event_id = event->id; + frame.event_data_len = event->dyndata.size; + frame.event_data = (u8_t *)event->dyndata.data; + + int err = switch_to_low_latency(subscriber); + + if (err) { + notify_config_forwarded(CONFIG_STATUS_WRITE_ERROR); + return; + } + + int pos = config_channel_report_fill(report, sizeof(report), &frame, + false); + + if (pos < 0) { + LOG_WRN("Could not set report"); + return; + } + + err = bt_gatt_hids_c_rep_write(recipient_hidc, + config_rep, + hidc_write_cb, + report, + sizeof(report)); + if (err) { + LOG_ERR("Writing report failed, err:%d", err); + notify_config_forwarded(CONFIG_STATUS_WRITE_ERROR); + } + + return; +} + +static void handle_config_forward_get(const struct config_forward_get_event *event) +{ + struct hids_subscriber *subscriber = + find_subscriber_hidc(event->recipient); + + if (!subscriber) { + LOG_INF("Recipent %02" PRIx16 "not found", event->recipient); + return; + } + + struct bt_gatt_hids_c *recipient_hidc = &subscriber->hidc; + + __ASSERT_NO_MSG(recipient_hidc != NULL); + + int err = switch_to_low_latency(subscriber); + + if (err) { + notify_config_forwarded(CONFIG_STATUS_WRITE_ERROR); + return; + } + + if (forward_pending) { + LOG_DBG("GATT read already pending"); + return; + } + + if (!bt_gatt_hids_c_ready_check(recipient_hidc)) { + LOG_WRN("Cannot forward, peer disconnected"); + + notify_config_forwarded(CONFIG_STATUS_DISCONNECTED_ERROR); + return; + } + + struct bt_gatt_hids_c_rep_info *config_rep = + bt_gatt_hids_c_rep_find(recipient_hidc, + BT_GATT_HIDS_REPORT_TYPE_FEATURE, + REPORT_ID_USER_CONFIG); + + if (!config_rep) { + LOG_ERR("Feature report not found"); + notify_config_forwarded(CONFIG_STATUS_WRITE_ERROR); + return; + } + + notify_config_forwarded(CONFIG_STATUS_PENDING); + + channel_id = event->channel_id; + + err = bt_gatt_hids_c_rep_read(recipient_hidc, + config_rep, + hidc_read_cfg); + + if (err) { + LOG_ERR("Reading report failed, err:%d", err); + notify_config_forwarded(CONFIG_STATUS_WRITE_ERROR); + } +} + static bool event_handler(const struct event_header *eh) { if (is_hid_report_sent_event(eh)) { @@ -415,6 +660,8 @@ static bool event_handler(const struct event_header *eh) LOG_INF("HID device disconnected"); bt_gatt_hids_c_release( &subscribers[i].hidc); + subscribers[i].pid = 0; + subscribers[i].timestamp = 0; } } } @@ -443,121 +690,15 @@ static bool event_handler(const struct event_header *eh) if (IS_ENABLED(CONFIG_DESKTOP_CONFIG_CHANNEL_ENABLE)) { if (is_config_forward_event(eh)) { - const struct config_forward_event *event = - cast_config_forward_event(eh); - - struct bt_gatt_hids_c *recipient_hidc = - find_subscriber_hidc(event->recipient); - - if (!recipient_hidc) { - LOG_INF("Recipent %02x not found", - event->recipient); - return false; - } - - if (!bt_gatt_hids_c_ready_check(recipient_hidc)) { - LOG_WRN("Cannot forward, peer disconnected"); - - notify_config_forwarded(CONFIG_STATUS_DISCONNECTED_ERROR); - return false; - } - - struct bt_gatt_hids_c_rep_info *config_rep = - bt_gatt_hids_c_rep_find(recipient_hidc, - BT_GATT_HIDS_REPORT_TYPE_FEATURE, - REPORT_ID_USER_CONFIG); - if (!config_rep) { - LOG_ERR("Feature report not found"); - notify_config_forwarded(CONFIG_STATUS_WRITE_ERROR); - return false; - } - - if (event->dyndata.size > UCHAR_MAX) { - LOG_ERR("Event data too big"); - __ASSERT_NO_MSG(false); - return false; - } - - struct config_channel_frame frame; - u8_t report[REPORT_SIZE_USER_CONFIG]; - - if (event->status == CONFIG_STATUS_FETCH) { - LOG_INF("Forwarding fetch request"); - frame.status = CONFIG_STATUS_FETCH; - } - - frame.recipient = event->recipient; - frame.event_id = event->id; - frame.event_data_len = event->dyndata.size; - frame.event_data = (u8_t *) event->dyndata.data; - - int pos = config_channel_report_fill(report, sizeof(report), &frame, false); - if (pos < 0) { - LOG_WRN("Could not set report"); - return pos; - } - - int err = bt_gatt_hids_c_rep_write(recipient_hidc, - config_rep, - hidc_write_cb, - report, - sizeof(report)); - if (err) { - LOG_ERR("Writing report failed, err:%d", err); - notify_config_forwarded(CONFIG_STATUS_WRITE_ERROR); - } + handle_config_forward(cast_config_forward_event(eh)); return false; } if (is_config_forward_get_event(eh)) { - const struct config_forward_get_event *event = - cast_config_forward_get_event(eh); - - struct bt_gatt_hids_c *recipient_hidc = - find_subscriber_hidc(event->recipient); - - if (!recipient_hidc) { - LOG_INF("Recipent %02x not found", - event->recipient); - return false; - } + handle_config_forward_get(cast_config_forward_get_event(eh)); - if (forward_pending) { - LOG_DBG("GATT read already pending"); - return false; - } else { - if (!bt_gatt_hids_c_ready_check(recipient_hidc)) { - LOG_WRN("Cannot forward, peer disconnected"); - - notify_config_forwarded(CONFIG_STATUS_DISCONNECTED_ERROR); - return false; - } - - struct bt_gatt_hids_c_rep_info *config_rep = - bt_gatt_hids_c_rep_find(recipient_hidc, - BT_GATT_HIDS_REPORT_TYPE_FEATURE, - REPORT_ID_USER_CONFIG); - if (!config_rep) { - LOG_ERR("Feature report not found"); - notify_config_forwarded(CONFIG_STATUS_WRITE_ERROR); - return false; - } - - notify_config_forwarded(CONFIG_STATUS_PENDING); - - channel_id = event->channel_id; - - int err = bt_gatt_hids_c_rep_read(recipient_hidc, - config_rep, - hidc_read_cfg); - if (err) { - LOG_ERR("Reading report failed, err:%d", err); - notify_config_forwarded(CONFIG_STATUS_WRITE_ERROR); - } - - return false; - } + return false; } } @@ -566,6 +707,7 @@ static bool event_handler(const struct event_header *eh) return false; } + EVENT_LISTENER(MODULE, event_handler); EVENT_SUBSCRIBE(MODULE, module_state_event); EVENT_SUBSCRIBE_EARLY(MODULE, ble_discovery_complete_event); diff --git a/applications/nrf_desktop/src/modules/led_state.c b/applications/nrf_desktop/src/modules/led_state.c index ba536939f899..194950d477be 100644 --- a/applications/nrf_desktop/src/modules/led_state.c +++ b/applications/nrf_desktop/src/modules/led_state.c @@ -22,7 +22,8 @@ LOG_MODULE_REGISTER(MODULE, CONFIG_DESKTOP_LED_STATE_LOG_LEVEL); static enum led_system_state system_state = LED_SYSTEM_STATE_IDLE; -static bool connected; +static u8_t connected; +static bool peer_search; static enum peer_operation peer_op = PEER_OPERATION_CANCEL; static u8_t cur_peer_id; @@ -46,7 +47,10 @@ static void load_peer_state_led(void) case PEER_OPERATION_ERASE_ADV_CANCEL: case PEER_OPERATION_ERASED: case PEER_OPERATION_CANCEL: - if (connected) { + case PEER_OPERATION_SCAN_REQUEST: + if (peer_search) { + state = LED_PEER_STATE_PEER_SEARCH; + } else if (connected > 0) { state = LED_PEER_STATE_CONNECTED; } break; @@ -97,10 +101,12 @@ static bool event_handler(const struct event_header *eh) switch (event->state) { case PEER_STATE_SECURED: case PEER_STATE_CONNECTED: - connected = true; + __ASSERT_NO_MSG(connected < UINT8_MAX); + connected++; break; case PEER_STATE_DISCONNECTED: - connected = false; + __ASSERT_NO_MSG(connected > 0); + connected--; break; case PEER_STATE_CONN_FAILED: /* Ignore */ @@ -114,6 +120,16 @@ static bool event_handler(const struct event_header *eh) return false; } + if (is_ble_peer_search_event(eh)) { + struct ble_peer_search_event *event = + cast_ble_peer_search_event(eh); + + peer_search = event->active; + load_peer_state_led(); + + return false; + } + if (is_ble_peer_operation_event(eh)) { struct ble_peer_operation_event *event = cast_ble_peer_operation_event(eh); @@ -166,5 +182,6 @@ static bool event_handler(const struct event_header *eh) EVENT_LISTENER(MODULE, event_handler); EVENT_SUBSCRIBE(MODULE, module_state_event); EVENT_SUBSCRIBE(MODULE, ble_peer_event); +EVENT_SUBSCRIBE(MODULE, ble_peer_search_event); EVENT_SUBSCRIBE(MODULE, ble_peer_operation_event); EVENT_SUBSCRIBE(MODULE, battery_state_event); diff --git a/applications/nrf_desktop/src/modules/led_stream.c b/applications/nrf_desktop/src/modules/led_stream.c index 19150de05f8d..2da5c3f26a56 100644 --- a/applications/nrf_desktop/src/modules/led_stream.c +++ b/applications/nrf_desktop/src/modules/led_stream.c @@ -23,10 +23,12 @@ LOG_MODULE_REGISTER(MODULE, CONFIG_DESKTOP_LED_STREAM_LOG_LEVEL); #define LED_ID(led) ((led) - &leds[0]) +#define STEPS_QUEUE_ARRAY_SIZE (CONFIG_DESKTOP_LED_STREAM_QUEUE_SIZE + 1) + struct led { const struct led_effect *state_effect; struct led_effect led_stream_effect; - struct led_effect_step steps_queue[CONFIG_DESKTOP_LED_STREAM_QUEUE_SIZE]; + struct led_effect_step steps_queue[STEPS_QUEUE_ARRAY_SIZE]; u8_t rx_idx; u8_t tx_idx; bool streaming; @@ -38,7 +40,7 @@ static bool initialized; static size_t next_index(size_t index) { - return (index + 1) % CONFIG_DESKTOP_LED_STREAM_QUEUE_SIZE; + return (index + 1) % STEPS_QUEUE_ARRAY_SIZE; } static bool queue_data(const struct event_dyndata *dyndata, struct led *led) @@ -95,9 +97,9 @@ static void send_effect(const struct led_effect *effect, struct led *led) static size_t count_free_places(struct led *led) { - size_t len = (CONFIG_DESKTOP_LED_STREAM_QUEUE_SIZE + led->rx_idx - led->tx_idx) - % CONFIG_DESKTOP_LED_STREAM_QUEUE_SIZE; - return CONFIG_DESKTOP_LED_STREAM_QUEUE_SIZE - len - 1; + size_t len = (STEPS_QUEUE_ARRAY_SIZE + led->rx_idx - led->tx_idx) + % STEPS_QUEUE_ARRAY_SIZE; + return STEPS_QUEUE_ARRAY_SIZE - len - 1; } static bool is_queue_empty(struct led *led) diff --git a/applications/nrf_desktop/src/modules/power_manager.c b/applications/nrf_desktop/src/modules/power_manager.c index 726a2c8d8932..86ebbe7da592 100644 --- a/applications/nrf_desktop/src/modules/power_manager.c +++ b/applications/nrf_desktop/src/modules/power_manager.c @@ -9,7 +9,6 @@ #include #include -#include #include #include @@ -289,6 +288,11 @@ static bool event_handler(const struct event_header *eh) k_delayed_work_init(&power_down_trigger, power_down); k_delayed_work_submit(&power_down_trigger, POWER_DOWN_CHECK_MS); + + if (IS_ENABLED(CONFIG_DESKTOP_POWER_MANAGER_CONSTLAT)) { + nrf_power_task_trigger(NRF_POWER_TASK_CONSTLAT); + LOG_WRN("Constant latency enabled"); + } } else if (event->state == MODULE_STATE_ERROR) { power_state = POWER_STATE_ERROR; k_delayed_work_cancel(&power_down_trigger); diff --git a/applications/nrf_desktop/src/modules/usb_state.c b/applications/nrf_desktop/src/modules/usb_state.c index f38c84ae5212..ae3ca17c6190 100644 --- a/applications/nrf_desktop/src/modules/usb_state.c +++ b/applications/nrf_desktop/src/modules/usb_state.c @@ -328,7 +328,15 @@ static void device_status(enum usb_dc_status_code cb_status, const u8_t *param) break; case USB_DC_SUSPEND: - __ASSERT_NO_MSG(state != USB_STATE_DISCONNECTED); + if (state == USB_STATE_DISCONNECTED) { + /* Due to the way USB driver and stack are written + * some events may be issued before application + * connect its callback. + * We assume that device was powered. + */ + state = USB_STATE_POWERED; + LOG_WRN("USB suspended while disconnected"); + } before_suspend = state; new_state = USB_STATE_SUSPENDED; LOG_WRN("USB suspend"); diff --git a/applications/nrf_desktop/src/util/CMakeLists.txt b/applications/nrf_desktop/src/util/CMakeLists.txt index 8fbf90b7b6c3..7b7af0f6a6ab 100644 --- a/applications/nrf_desktop/src/util/CMakeLists.txt +++ b/applications/nrf_desktop/src/util/CMakeLists.txt @@ -5,3 +5,19 @@ # target_sources_ifdef(CONFIG_DESKTOP_CONFIG_CHANNEL_ENABLE app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/config_channel.c) + +if(CONFIG_DESKTOP_BLE_QOS_ENABLE) + if(CONFIG_FLOAT) + if(CONFIG_FP_HARDABI) + set(float_dir hard-float) + elseif(CONFIG_FP_SOFTABI) + set(float_dir softfp-float) + else() + assert(0 "Unreachable code") + endif() + else() + set(float_dir soft-float) + endif() + zephyr_link_libraries(${CMAKE_CURRENT_SOURCE_DIR}/chmap_filter/lib/${GCC_M_CPU}/${float_dir}/libchmapfilt.a) + target_include_directories(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/chmap_filter/include) +endif() diff --git a/applications/nrf_desktop/src/util/chmap_filter/include/chmap_filter.h b/applications/nrf_desktop/src/util/chmap_filter/include/chmap_filter.h new file mode 100644 index 000000000000..46f6b53df93f --- /dev/null +++ b/applications/nrf_desktop/src/util/chmap_filter/include/chmap_filter.h @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ +#ifndef _CHMAP_FILTER_H_ +#define _CHMAP_FILTER_H_ + +/** + * @brief Utility used to filter QoS information (per-channel CRC status) + * with the purpose of generating a suitable BLE channel map. + * + * @details Input to the library is CRC information. + * This information is used to assign ratings to each BLE channel. + * Single channels with poor ratings, or blocks of channels + * affected by wifi are removed from the channel map recommendation. + * + * @note The library is not thread-safe. Apart from the "get" functions, + * functions must not be called while @ref chmap_filter_process is running + * + * @defgroup chmap_filter Channel map filtering + * @{ + */ + +#include +#include +#include + +#include "zephyr/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* WLAN characteristics */ +/* Note: Some channels may be omitted to reduce state memory usage */ +#define CHMAP_WLAN_802_11GN_CENTER_FREQS \ + {12, 17, 22, 27, 32, 37, 42, 47, 52, 57, 62, 67} +#define CHMAP_WLAN_802_11GN_CENTER_FREQ_COUNT 12 +#define CHMAP_WLAN_802_11GN_CHANNEL_WIDTH_MHz 20 + +/* BLE characteristics */ +#define CHMAP_BLE_CHANNEL_COUNT 37 +#define CHMAP_BLE_BITMASK_SIZE 5 +#define CHMAP_BLE_BITMASK_DEFAULT {0xFF, 0xFF, 0xFF, 0xFF, 0x1F} + +/* Filter instance size [bytes] */ +#define CHMAP_FILTER_INST_SIZE 580 + +/* Static parameters */ +/* Target evaluation keepout for minimum size channel map */ +#define CHMAP_PARAM_DYN_EVAL_TARGET 10 +/* Channel block duration factor */ +/* Duration will increase based on previous channel history */ +#define CHMAP_PARAM_DYN_BLOCK_INCREASE 2 + +/* Default parameter values */ +#define DEFAULT_PARAM_MAINTENANCE_SAMPLE_COUNT 2000 +#define DEFAULT_PARAM_INITIAL_RATING 0 +#define DEFAULT_PARAM_BLE_MIN_CHANNEL_COUNT 4 +#define DEFAULT_PARAM_BLE_WIFI_KEEPOUT_DURATION 2 +#define DEFAULT_PARAM_EVAL_MAX_COUNT 1 +#define DEFAULT_PARAM_EVAL_DURATION 5 +#define DEFAULT_PARAM_EVAL_KEEPOUT_DURATION 300 +#define DEFAULT_PARAM_DYN_DURATIONS true +/* The below parameters are fixed point values, with scaling factor of 1/100 */ +#define DEFAULT_PARAM_BLE_WEIGHT_CRC_OK 100 /* = 1.0 */ +#define DEFAULT_PARAM_BLE_WEIGHT_CRC_ERROR -100 /* = -1.0 */ +#define DEFAULT_PARAM_BLE_RATING_TRIM 49 /* = 0.49 */ +#define DEFAULT_PARAM_BLE_BLOCK_THRESHOLD 25 /* = 0.25 */ +#define DEFAULT_PARAM_WIFI_RATING_INC 400 /* = 4.0 */ +#define DEFAULT_PARAM_WIFI_PRESENT_THRESHOLD 90 /* = 0.9 */ +#define DEFAULT_PARAM_WIFI_ACTIVE_THRESHOLD 75 /* = 0.75 */ +#define DEFAULT_PARAM_WIFI_RATING_TRIM 50 /* = 0.5 */ +#define DEFAULT_PARAM_EVAL_SUCCESS_THRESHOLD 85 /* = 0.85 */ + +/**@brief Channel map filter parameters that can be changed at runtime + * + * @note Type 'fix' is used to indicate a fixed point value with a scaling + * factor of 1/100. E.g. value 20 = 20/100 = 0.2 + * @note Threshold values are ratios of average channel rating. + * E.g. PARAM_BLE_BLOCK_THRESHOLD = 20 will block a BLE channel if its rating + * falls below 20% of the average rating. + * @note The time unit is based on how often @ref chmap_filter_process is run. + * Recommend running this function at ~1 second interval + */ +struct chmap_filter_params { +/** Maintenance algorithm needs these many samples to run [int] */ + u16_t maintenance_sample_count; +/** Initial channel rating [int] */ + s32_t initial_rating; +/** Min num of channels in channel map [int] */ + u8_t min_channel_count; +/** Channel rating weight of CRC OK [fix] */ + s16_t ble_weight_crc_ok; +/** Channel rating weight of CRC ERR [fix] */ + s16_t ble_weight_crc_error; +/** Factor for BLE channel rating trim. Must be < 100. + * Note: "high" value can lead to rating overflow [fix] + */ + s16_t ble_rating_trim; +/** Threshold of average rating required to block of single channel [fix] */ + s16_t ble_block_threshold; +/** BLE channel suspected of being affected by wifi will not be blocked as + * single channel until this time has passed [int] + */ + u16_t ble_wifi_keepout_duration; +/** Wifi strength increase factor. Higher value blocks wifi faster [fix] */ + s16_t wifi_rating_inc; +/** Threshold of average rating that indicates Wifi presence [fix] */ + s16_t wifi_present_threshold; +/** Threshold of average rating required to block wifi channel [fix] */ + s16_t wifi_active_threshold; +/** Factor for Wifi channel rating trim. Must be < 100. [fix] */ + s16_t wifi_rating_trim; +/** Maximum number of channels that can be evaluated at a time [int] */ + u8_t eval_max_count; +/** Evaluated channels will be evaluated for these many time units [int] */ + u16_t eval_duration; +/** Blocked channels will not be evaluated for these many time units [int] */ + u16_t eval_keepout_duration; +/** Rating of evaluated channel must be above this threshold [fix] */ + s16_t eval_success_threshold; +/** Dynamic block and evaluation durations. [bool] + * Fewer channels in channel map = shorter time until evaluation. + * Channel blocked in the past = longer time until evaluation + */ + bool dynamic_durations; +}; + +struct chmap_instance; + +/**@brief Get library version number + * + * @return Pointer to null-terminated version string + */ +const char *chmap_filter_version(void); + +/**@brief Initialize channel map filter library + */ +void chmap_filter_init(void); + +/**@brief Initialize channel map filter instance. + * + * @details Sufficient memory should be supplied to hold filter state. + * Allocate @ref CHMAP_FILTER_INST_SIZE bytes for this purpose. + * + * @param[in] p_inst Pointer to instance buffer + * @param[in] size Size of instance buffer + * + * @retval 0 when successful + * @retval -ENOMEM if buffer is too small + */ +int chmap_filter_instance_init(struct chmap_instance *p_inst, size_t size); + +/**@brief Update channel CRC information + * + * @note Should not preempt @ref chmap_filter_process + * + * @param[in] p_inst Channel map instance pointer + * @param[in] ch_idx BLE channel index + * @param[in] crc_ok CRC ok count + * @param[in] crc_error CRC error count + */ +void chmap_filter_crc_update( + struct chmap_instance *p_inst, + u8_t ch_idx, + u16_t crc_ok, + u8_t crc_error); + +/**@brief Process channel map filter + * + * @param[in] p_inst Channel map instance pointer + * + * @return true when channel map update is recommended + */ +bool chmap_filter_process(struct chmap_instance *p_inst); + +/**@brief Get suggested channel map + * + * @param[in] p_inst Channel map instance pointer + * + * @return pointer to @ref CHMAP_BLE_BITMAP_SIZE sized channel map array + */ +u8_t *chmap_filter_suggested_map_get(struct chmap_instance *p_inst); + +/**@brief Confirm that suggested channel map has been applied + * + * @details This function is used to keep internal state consistent with actual + * channel map used. There will always be a brief period of + * inconsistency when a new channel map is applied, + * which is to be expected. + * + * @param[in] p_inst Channel map instance pointer + */ +void chmap_filter_suggested_map_confirm(struct chmap_instance *p_inst); + +/**@brief Get dynamic parameters + * + * @param[in] p_inst Channel map instance pointer + * @param[out] p_params Buffer to hold parameter values + */ +void chmap_filter_params_get( + struct chmap_instance *p_inst, + struct chmap_filter_params *p_params); + +/**@brief Set dynamic parameters + * + * @details Parameter update follow the read-modify-write pattern. + * Use @ref chmap_filter_params_get to get the full set of parameters, + * make desired adjustments, + * then @ref chmap_filter_params_set to set updated parameters. + * + * @note Parameters will be applied when @ref chmap_filter_process is called. + * + * @param[in] p_inst Channel map instance pointer + * @param[in] p_params Buffer that holds parameter values + * + * @retval 0 when successful. + * @retval -EINVAL if one or more parameters are invalid. + */ +int chmap_filter_params_set( + struct chmap_instance *p_inst, + struct chmap_filter_params *p_params); + +/**@brief Get bitmask of currently blacklisted wifi channels + * + * @note As blacklist updates happen asynchronously, + * @ref chmap_filter_process needs to run after + * @ref chmap_filter_blacklist_set + * for the information returned from this function to be valid. + * + * @return Bitmask of wifi channels. + * E.g. channel 6 and channel 8 = (1 << 6) | (1 << 8) = 320 + */ +u16_t chmap_filter_wifi_blacklist_get(void); + +/**@brief Set wifi channels blacklist + * + * @details One or more channels can be blacklisted. + * It does not matter if channels overlap. + * The blacklist will not be accepted if the total number of + * blacklisted BLE channels exceeds the minimum channel count param. + * + * @note Blacklist can be partially added if more than one channel is + * blacklisted and function return is non-zero. + * Use @ref chmap_filter_wifi_blacklist_get to confirm. + * + * @param[in] p_inst Channel map instance pointer + * @param[in] blacklist Bitfield of blacklisted wifi channels. + * E.g. wifi channel 6 = (1 << 6) + * + * @retval 0 when successful, + * @retval -EINVAL if blacklist exceeds minimum channel count. + */ +int chmap_filter_blacklist_set(struct chmap_instance *p_inst, u16_t blacklist); + +/**@brief Get BLE channel information + * + * @param[in] p_inst Channel map instance pointer + * @param[in] chn_idx Bluetooth channel index (0-36) + * @param[out] p_state Pointer to hold state value + * @param[out] p_rating Pointer to hold rating value + * @param[out] p_freq Pointer to hold frequency value [2400 + x MHz] + * + * @retval 0 when successful + * @retval -EINVAL if p_inst or chn_idx is invalid + */ +int chmap_filter_chn_info_get( + struct chmap_instance *p_inst, + u8_t chn_idx, + u8_t *p_state, + s16_t *p_rating, + u8_t *p_freq); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* _CHMAP_FILTER_H_ */ diff --git a/applications/nrf_desktop/src/util/chmap_filter/lib/cortex-m4/hard-float/libchmapfilt.a b/applications/nrf_desktop/src/util/chmap_filter/lib/cortex-m4/hard-float/libchmapfilt.a new file mode 100644 index 000000000000..1b4097611ef5 Binary files /dev/null and b/applications/nrf_desktop/src/util/chmap_filter/lib/cortex-m4/hard-float/libchmapfilt.a differ diff --git a/applications/nrf_desktop/src/util/chmap_filter/lib/cortex-m4/soft-float/libchmapfilt.a b/applications/nrf_desktop/src/util/chmap_filter/lib/cortex-m4/soft-float/libchmapfilt.a new file mode 100644 index 000000000000..53a2e9a80f0b Binary files /dev/null and b/applications/nrf_desktop/src/util/chmap_filter/lib/cortex-m4/soft-float/libchmapfilt.a differ diff --git a/boards/arm/nrf52810_pca20045/nrf52810_pca20045.dts b/boards/arm/nrf52810_pca20045/nrf52810_pca20045.dts index cbfdbbf2e3c3..0c1e7275df02 100644 --- a/boards/arm/nrf52810_pca20045/nrf52810_pca20045.dts +++ b/boards/arm/nrf52810_pca20045/nrf52810_pca20045.dts @@ -53,6 +53,7 @@ }; &spi0 { + compatible = "nordic,nrf-spim"; status = "okay"; sck-pin = <10>; mosi-pin = <12>; diff --git a/boards/arm/nrf52840_pca20041/nrf52840_pca20041.dts b/boards/arm/nrf52840_pca20041/nrf52840_pca20041.dts index 489f5c65513e..ee2c848bcde7 100644 --- a/boards/arm/nrf52840_pca20041/nrf52840_pca20041.dts +++ b/boards/arm/nrf52840_pca20041/nrf52840_pca20041.dts @@ -57,6 +57,7 @@ }; &spi1 { + compatible = "nordic,nrf-spim"; status = "okay"; sck-pin = <16>; mosi-pin = <17>; diff --git a/boards/arm/nrf52_pca20044/nrf52_pca20044.dts b/boards/arm/nrf52_pca20044/nrf52_pca20044.dts index f3f449fb8d41..4e87177f7c02 100644 --- a/boards/arm/nrf52_pca20044/nrf52_pca20044.dts +++ b/boards/arm/nrf52_pca20044/nrf52_pca20044.dts @@ -53,6 +53,7 @@ }; &spi0 { + compatible = "nordic,nrf-spi"; status = "okay"; sck-pin = <5>; mosi-pin = <6>; diff --git a/boards/arm/nrf9160_pca20035/board_secure.c b/boards/arm/nrf9160_pca20035/board_secure.c index d7092c47c1e1..470c3feb3ba9 100644 --- a/boards/arm/nrf9160_pca20035/board_secure.c +++ b/boards/arm/nrf9160_pca20035/board_secure.c @@ -10,7 +10,7 @@ #include LOG_MODULE_REGISTER(board_secure, CONFIG_BOARD_LOG_LEVEL); -#define ADP536X_I2C_DEV_NAME DT_NORDIC_NRF_I2C_I2C_2_LABEL +#define ADP536X_I2C_DEV_NAME DT_NORDIC_NRF_TWIM_I2C_2_LABEL #define LC_MAX_READ_LENGTH 128 static int power_mgmt_init(void) diff --git a/boards/arm/nrf9160_pca20035/nrf9160_pca20035_common.dts b/boards/arm/nrf9160_pca20035/nrf9160_pca20035_common.dts index 17b2a8289cdd..18b98fe59387 100644 --- a/boards/arm/nrf9160_pca20035/nrf9160_pca20035_common.dts +++ b/boards/arm/nrf9160_pca20035/nrf9160_pca20035_common.dts @@ -106,6 +106,7 @@ }; &i2c2 { + compatible = "nordic,nrf-twim"; status = "okay"; sda-pin = <11>; scl-pin = <12>; @@ -126,6 +127,7 @@ }; &spi3 { + compatible = "nordic,nrf-spim"; status = "okay"; sck-pin = <3>; mosi-pin = <4>; diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 7db3dfd43438..73350e206461 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -1,21 +1,42 @@ -# # Copyright (c) 2019 Nordic Semiconductor ASA -# # SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + +# Builds combined documentation for all documentation sets: nRF (including +# Doxygen documentation), Zephyr, MCUboot, etc. +# +# We use our own Sphinx configuration files when building the documentation set +# for each repository, instead of reusing configuration files. See e.g. +# doc/nrf/conf.py and doc/zephyr/conf.py. # +# Intersphinx (http://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html) +# is used to link documentation sets together. It is configured in the Sphinx +# configuration files. +# +# The *-content targets copy/create .rst files that later get built by Sphinx. + cmake_minimum_required(VERSION 3.13.1) project(nrf-connect-sdk-doc LANGUAGES) +# +# Set various *_BASE variables pointing to the nrf/, zephyr/, etc., +# directories. Derive them automatically if they're not set in the environment, +# by assuming that e.g. nrfxlib can be found at ../../nrfxlib/. Also add them +# to the environment if they're not there. +# + set(ZEPHYR_BASE $ENV{ZEPHYR_BASE}) + get_filename_component(NRF_BASE ${CMAKE_CURRENT_LIST_DIR}../ DIRECTORY) set(ENV{NRF_BASE} ${NRF_BASE}) + if(NOT DEFINED ENV{MCUBOOT_BASE}) - get_filename_component(MCUBOOT_BASE ${CMAKE_CURRENT_LIST_DIR}/../../mcuboot/ REALPATH) - set(ENV{MCUBOOT_BASE} ${MCUBOOT_BASE}) + get_filename_component(MCUBOOT_BASE ${CMAKE_CURRENT_LIST_DIR}/../../mcuboot/ REALPATH) + set(ENV{MCUBOOT_BASE} ${MCUBOOT_BASE}) endif() + if(NOT DEFINED ENV{NRFXLIB_BASE}) - get_filename_component(NRFXLIB_BASE ${CMAKE_CURRENT_LIST_DIR}/../../nrfxlib/ REALPATH) - set(ENV{NRFXLIB_BASE} ${NRFXLIB_BASE}) + get_filename_component(NRFXLIB_BASE ${CMAKE_CURRENT_LIST_DIR}/../../nrfxlib/ REALPATH) + set(ENV{NRFXLIB_BASE} ${NRFXLIB_BASE}) endif() message(STATUS "ZEPHYR_BASE: ${ZEPHYR_BASE}") @@ -23,54 +44,57 @@ message(STATUS "NRF_BASE: $ENV{NRF_BASE}") message(STATUS "MCUBOOT_BASE: $ENV{MCUBOOT_BASE}") message(STATUS "NRFXLIB_BASE: $ENV{NRFXLIB_BASE}") +# +# Find programs we need (Python, Sphinx, and Doxygen) +# + find_package(PythonInterp 3.4) -set(DOXYGEN_SKIP_DOT True) +set(DOXYGEN_SKIP_DOT True) # Skip the otional Dot component find_package(Doxygen REQUIRED) -find_program( - SPHINXBUILD - sphinx-build - ) +find_program(SPHINXBUILD sphinx-build) if(${SPHINXBUILD} STREQUAL SPHINXBUILD-NOTFOUND) message(FATAL_ERROR "The 'sphinx-build' command was not found. Make sure you have Sphinx installed.") endif() +# +# CMake build directories (build/zephyr, build/nrf, etc.) and the shared HTML +# output directory +# + set(ZEPHYR_BINARY_DIR ${CMAKE_BINARY_DIR}/zephyr) set(NRF_BINARY_DIR ${CMAKE_BINARY_DIR}/nrf) set(MCUBOOT_BINARY_DIR ${CMAKE_BINARY_DIR}/mcuboot) set(NRFXLIB_BINARY_DIR ${CMAKE_BINARY_DIR}/nrfxlib) -set(NRF_DOC_DIR ${NRF_BASE}/doc/nrf) -set(MCUBOOT_DOC_DIR ${MCUBOOT_BASE}/docs) + +# HTML output directory set(HTML_DIR ${CMAKE_BINARY_DIR}/html) -# Create the combined HTML output folder file(MAKE_DIRECTORY ${HTML_DIR}) -# Create a mbedtls config file with all settings on -file(STRINGS ${NRFXLIB_BASE}/nrf_security/configs/nrf-config.h.template MBEDTLS_TEMPLATE_CONFIG) -string(REGEX REPLACE - "#cmakedefine ([-_A-Z0-9]*)" - "#define \\1\n#define CONFIG_GLUE_\\1\n#define CONFIG_CC310_\\1\n#define CONFIG_VANILLA_\\1" - MBEDTLS_CONFIG "${MBEDTLS_TEMPLATE_CONFIG}" -) -string(REGEX REPLACE ";" "\n" MBEDTLS_CONFIG "${MBEDTLS_CONFIG}") -file(WRITE ${NRFXLIB_BINARY_DIR}/mbedtls_doxygen_config.h ${MBEDTLS_CONFIG}) +# +# Add the 'zephyr' target for building the Zephyr documentation. We reuse +# doc/CMakeLists.txt from the Zephyr repository, but use our own Sphinx +# configuration from doc/zephyr/conf.py. The generated HTML is placed in the +# common Sphinx HTML output folder. +# -# Build Zephyr documentation +# Parameters to doc/CMakeLists.txt in Zephyr set(SPHINXOPTS -c ${NRF_BASE}/doc/zephyr) -# Place the generated HTML in the common Sphinx HTML output folder set(SPHINX_OUTPUT_DIR ${HTML_DIR}/zephyr) + +# Get access to the 'html' target from doc/CMakeLists.txt in Zephyr add_subdirectory(${ZEPHYR_BASE}/doc ${ZEPHYR_BINARY_DIR}) -add_custom_target( - zephyr -) +add_custom_target(zephyr) +# The 'html' target is from Zephyr add_dependencies(zephyr html) -# Common code -set(TARGET_LIST zephyr nrf mcuboot nrfxlib) +# +# Add 'clean-zephyr', 'clean-nrf', etc., targets +# -foreach(target ${TARGET_LIST}) +foreach(target zephyr nrf mcuboot nrfxlib) set(TARGET_BINARY_DIR ${CMAKE_BINARY_DIR}/${target}) # Cleanup build output add_custom_target( @@ -85,31 +109,29 @@ foreach(target ${TARGET_LIST}) ) endforeach() -################################################### -# Everything below this line is nrf-specific -################################################### +# +# Add nRF-related targets +# set(NRF_SPHINXOPTS -d ${NRF_BINARY_DIR}/doctrees ${NRF_SPHINXOPTS}) +set(NRF_DOC_DIR ${NRF_BASE}/doc/nrf) + set(NRF_DOXYFILE_IN ${NRF_DOC_DIR}/nrf.doxyfile.in) set(NRF_DOXYFILE_OUT ${NRF_BINARY_DIR}/nrf.doxyfile) set(NRF_RST_OUT ${NRF_BINARY_DIR}/rst) -set(NRF_CONF_PY ${NRF_DOC_DIR}/conf.py) set(NRF_DOC_LOG ${NRF_BINARY_DIR}/doc.log) set(NRF_DOXY_LOG ${NRF_BINARY_DIR}/doxy.log) set(NRF_SPHINX_LOG ${NRF_BINARY_DIR}/sphinx.log) -set(NRF_DOC_WARN ${NRF_BINARY_DIR}/doc.warnings) set(NRF_CONTENT_OUTPUTS ${NRF_BINARY_DIR}/extracted-content.txt) configure_file(${NRF_DOXYFILE_IN} ${NRF_DOXYFILE_OUT} @ONLY) -set(ARGS ${NRF_DOXYFILE_OUT}) - add_custom_target( nrf-doxy COMMAND ${CMAKE_COMMAND} -DCOMMAND=${DOXYGEN_EXECUTABLE} - -DARGS="${ARGS}" + -DARGS="${NRF_DOXYFILE_OUT}" -DOUTPUT_FILE=${NRF_DOXY_LOG} -DERROR_FILE=${NRF_DOXY_LOG} -DWORKING_DIRECTORY=${CMAKE_CURRENT_LIST_DIR} @@ -159,15 +181,9 @@ add_custom_target( COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH="${ZEPHYR_BASE}/scripts/kconfig${SEP}$ENV{PYTHONPATH}" srctree=${NRF_BASE} - ENV_VAR_BOARD_DIR=boards/*/*/ - ENV_VAR_ARCH=* - KERNELVERSION=${PROJECT_VERSION} - SRCARCH=x86 - GENERATED_DTS_BOARD_CONF=not_applicable - CMAKE_BINARY_DIR=${CMAKE_BINARY_DIR} KCONFIG_TURBO_MODE=${KCONFIG_TURBO_MODE} KCONFIG_DOC_MODE=1 - ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/doc/scripts/genrest.py ${NRF_BASE}/Kconfig.nrf ${NRF_RST_OUT}/doc/nrf/reference/kconfig/ + ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/doc/scripts/genrest.py --kconfig ${NRF_BASE}/Kconfig.nrf ${NRF_RST_OUT}/doc/nrf/reference/kconfig/ WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} ) @@ -198,20 +214,16 @@ add_dependencies(nrf-html nrf-doxy nrf-content nrf-kconfig) add_custom_target(nrf) add_dependencies(nrf nrf-html) -################################################### -# Everything below this line is mcuboot-specific -################################################### +# +# Add mcuboot-related targets +# set(MCUBOOT_SPHINXOPTS -d ${MCUBOOT_BINARY_DIR}/doctrees -c ${NRF_BASE}/doc/mcuboot ${MCUBOOT_SPHINXOPTS}) set(MCUBOOT_RST_OUT ${MCUBOOT_BINARY_DIR}/rst) -set(MCUBOOT_CONF_PY ${NRF_BASE}/doc/mcuboot/conf.py) -set(MCUBOOT_DOC_LOG ${MCUBOOT_BINARY_DIR}/doc.log) set(MCUBOOT_SPHINX_LOG ${MCUBOOT_BINARY_DIR}/sphinx.log) -set(MCUBOOT_DOC_WARN ${MCUBOOT_BINARY_DIR}/doc.warnings) -set(EXTRACT_CONTENT ${ZEPHYR_BASE}/doc/scripts/extract_content.py) -set(FIX_MARKDOWN ${NRF_BASE}/doc/scripts/fix_markdown.py) +set(MCUBOOT_DOC_DIR ${MCUBOOT_BASE}/docs) file(GLOB MDFILES ${MCUBOOT_DOC_DIR}/*.md) @@ -235,7 +247,8 @@ add_custom_target( # Fix problems in markdown files COMMAND ${CMAKE_COMMAND} -E env - ${PYTHON_EXECUTABLE} ${FIX_MARKDOWN} ${MCUBOOT_RST_OUT}/doc/mcuboot + ${PYTHON_EXECUTABLE} ${NRF_BASE}/doc/scripts/fix_markdown.py + ${MCUBOOT_RST_OUT}/doc/mcuboot WORKING_DIRECTORY ${MCUBOOT_DOC_DIR} ) @@ -253,11 +266,20 @@ add_dependencies(mcuboot-html mcuboot-content) add_custom_target(mcuboot) add_dependencies(mcuboot mcuboot-html) +# +# Add nrfxlib-related targets +# +# Create an mbedtls configuration file with all settings on +file(STRINGS ${NRFXLIB_BASE}/nrf_security/configs/nrf-config.h.template MBEDTLS_TEMPLATE_CONFIG) +string(REGEX REPLACE + "#cmakedefine ([-_A-Z0-9]*)" + "#define \\1\n#define CONFIG_GLUE_\\1\n#define CONFIG_CC310_\\1\n#define CONFIG_VANILLA_\\1" + MBEDTLS_CONFIG "${MBEDTLS_TEMPLATE_CONFIG}" +) +string(REGEX REPLACE ";" "\n" MBEDTLS_CONFIG "${MBEDTLS_CONFIG}") +file(WRITE ${NRFXLIB_BINARY_DIR}/mbedtls_doxygen_config.h ${MBEDTLS_CONFIG}) -################################################### -# Everything below this line is nrfxlib-specific -################################################### set(NRFXLIB_SPHINXOPTS -d ${NRFXLIB_BINARY_DIR}/doctrees -c ${NRF_BASE}/doc/nrfxlib ${NRFXLIB_SPHINXOPTS}) @@ -265,22 +287,17 @@ set(NRFXLIB_SPHINXOPTS -d ${NRFXLIB_BINARY_DIR}/doctrees -c ${NRF_BASE}/doc/nrfx set(NRFXLIB_DOXYFILE_IN ${NRF_BASE}/doc/nrfxlib/nrfxlib.doxyfile.in) set(NRFXLIB_DOXYFILE_OUT ${NRFXLIB_BINARY_DIR}/nrfxlib.doxyfile) set(NRFXLIB_RST_OUT ${NRFXLIB_BINARY_DIR}/rst) -set(NRFXLIB_CONF_PY ${NRF_BASE}/doc/nrfxlib/conf.py) -set(NRFXLIB_DOC_LOG ${NRFXLIB_BINARY_DIR}/doc.log) set(NRFXLIB_DOXY_LOG ${NRFXLIB_BINARY_DIR}/doxy.log) set(NRFXLIB_SPHINX_LOG ${NRFXLIB_BINARY_DIR}/sphinx.log) -set(NRFXLIB_DOC_WARN ${NRFXLIB_BINARY_DIR}/doc.warnings) configure_file(${NRFXLIB_DOXYFILE_IN} ${NRFXLIB_DOXYFILE_OUT} @ONLY) -set(ARGS ${NRFXLIB_DOXYFILE_OUT}) - add_custom_target( nrfxlib-doxy COMMAND ${CMAKE_COMMAND} -DCOMMAND=${DOXYGEN_EXECUTABLE} - -DARGS="${ARGS}" + -DARGS="${NRFXLIB_DOXYFILE_OUT}" -DOUTPUT_FILE=${NRFXLIB_DOXY_LOG} -DERROR_FILE=${NRFXLIB_DOXY_LOG} -DWORKING_DIRECTORY=${NRFXLIB_BASE} @@ -294,20 +311,12 @@ add_custom_target( COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH="${ZEPHYR_BASE}/scripts/kconfig${SEP}$ENV{PYTHONPATH}" srctree=${NRFXLIB_BASE} - ENV_VAR_BOARD_DIR=boards/*/*/ - ENV_VAR_ARCH=* - KERNELVERSION=${PROJECT_VERSION} - SRCARCH=x86 - GENERATED_DTS_BOARD_CONF=not_applicable - CMAKE_BINARY_DIR=${CMAKE_BINARY_DIR} KCONFIG_TURBO_MODE=${KCONFIG_TURBO_MODE} KCONFIG_DOC_MODE=1 - ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/doc/scripts/genrest.py ${NRFXLIB_BASE}/Kconfig.nrfxlib ${NRFXLIB_RST_OUT}/kconfig + ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/doc/scripts/genrest.py --kconfig ${NRFXLIB_BASE}/Kconfig.nrfxlib ${NRFXLIB_RST_OUT}/kconfig WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} ) -set(EXTRACT_CONTENT ${ZEPHYR_BASE}/doc/scripts/extract_content.py) - set(NRFXLIB_EXTRACT_CONTENT_COMMAND ${CMAKE_COMMAND} -E env ZEPHYR_BASE=${NRFXLIB_BASE} diff --git a/doc/mcuboot/conf.py b/doc/mcuboot/conf.py index 2f19694e0a24..27e631d2bf8e 100644 --- a/doc/mcuboot/conf.py +++ b/doc/mcuboot/conf.py @@ -66,9 +66,9 @@ # built documents. # # The short X.Y version. -version = '1.3.99' +version = '1.4.0' # The full version, including alpha/beta/rc tags. -release = '1.3.99' +release = '1.4.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/mcuboot/wrapper.rst b/doc/mcuboot/wrapper.rst index 984499772c14..40c98f4ca5c6 100644 --- a/doc/mcuboot/wrapper.rst +++ b/doc/mcuboot/wrapper.rst @@ -20,3 +20,4 @@ MCUboot documentation testplan-mynewt.md release.md PORTING.md + SECURITY.md diff --git a/doc/nrf/images/aws_s3_bucket_permissions.png b/doc/nrf/images/aws_s3_bucket_permissions.png new file mode 100644 index 000000000000..1464f1f86315 Binary files /dev/null and b/doc/nrf/images/aws_s3_bucket_permissions.png differ diff --git a/doc/nrf/images/ncs-west-repos.png b/doc/nrf/images/ncs-west-repos.png index 3ca9a0df68b2..32fcec111619 100644 Binary files a/doc/nrf/images/ncs-west-repos.png and b/doc/nrf/images/ncs-west-repos.png differ diff --git a/doc/nrf/images/ncs-west-repos.xml b/doc/nrf/images/ncs-west-repos.xml new file mode 100644 index 000000000000..208ec7d2d5c0 --- /dev/null +++ b/doc/nrf/images/ncs-west-repos.xml @@ -0,0 +1 @@ +7VzbcqM4EP0aP4YCSQh4zGUye8lOpWpqdjN52SIg29pgi8U4tvP1K4zASMgOeMAhs67KA2qJRqgP3a3Tckbwerb+nPjx9A8WkmgEzHA9gjcjACwEwCj7M8NNLnGwEEwSGopBO8FX+kqE0BTSJQ3JQhqYMhalNJaFAZvPSZBKMj9J2EoeNmaR/NTYn5Ca4GvgR3XpXzRMp7nUtc2d/BdCJ9PiyZYpemZ+MVgIFlM/ZKuKCH4aweuEsTS/mq2vSZQtXrEu+X23e3rLiSVknja5Ibp6pK9sc7f6bf2IY+/1+fE1uBBaXvxoKV54BHDE9V2NGVfLF8wP8g787zKb6dXdMqChz3uu2XzB+DKVPfxqsr1zdTFPxsIg2WWh8ikphhQSPtv8ObK4PrAq2S5muikslLDlPCTZS1q8ezWlKfka57NecUxy2TSdRaL7hSQp5da9jOhkzmUpywaMaRRds4glW40w9Ik7Drh8kSbsmVR6cOCSp3E5i+riFyvJH0DWFZEwxmfCZiRNNnyI6EUFxDcldPL2aoczyxOyaRVjrhD6AtuTUvfO/PxCIKAFGoAGDcpqk3l4mX1WvBVE/mJBA3mB+XokmwfeMA0PeIXg+1ZglgNusjUwy9am2ronCeWvQ5JCuKbpQ+U61wVs0dypyhqFpg4tumDLRHwD+lVDwi35yYSkB8bBfBwJJY9Tx0fF/rbG/IUsIZGf0hfZT+kgIZ5wz+j2iy7gZ9sS/KCjoCp/b3FX1a8oigCCsiITy4ryhakp2iK0fO3jQQt7d2GvJJ5ukhY+60c8lIJcYoU2cXTI9bADfbzXp00SP6Qcy5U7btAn69buxntZtuK9vKbeC/flvdBeIDypxjoeGSuySI0Nt1bbmMbFtWl0OC9Fww59xcCs42Kxza8u+QA3Xu9XEtIXrQ6OjfTCz2GWKYnIOHvY9k4zpIs48jd5D51HdH5gmk/75zj2ZzQSaniXP4u390GIsg+FRC8kQ3utp5yH8qIWOPSmA1nwOoqamiDgnzBJhrPQoq/tNAdihzPwT7TgccL+4aF1sR3ylt9s6k65bGvApgp+Vr0dhhXDMOo+qrmw1eSVrOmNPMlfxPl+f0zXWW61Jw3qYqOmpjrIrqc6wNakOoWw81TH7j3nnQXLp4yaGGrS23N6i5Fic9cxNFbXJbior/zW6cHo3OLriD69j5lPw7NgTzYlcDQ7FXBKQ7pd0iwjiWKxR4cZliHyIs6weBGswAViw3VdaGIIIHIBVnx6Y5rEcwwP27blAGRBx1NIEwsblgkRf4Bju1iddncUyq8eG6PrL+tJDOhVfPkI0d1dLyxwTpqIPO8iSdkim3aRvEaVJLAUtqWGTxtxOnBFwJSxBTWkSVn5qGIbdOCKtJbXMb5ny3dveZUuQ1BjeXhKy+to07ZBSDDzF6ZhmnhUpedt2xkd4uezRo3o35UOnDKOfZfC2CCD2iGXOpygZlSii+koyS0wdnGJh6EjQxxUQA5t96RhTEcAHwlpudjkHC42VWteJqgiN/sy8FFVryFiGgwL07Zn7IU04PkUrCZUChIbQ1rJBiHuDdJa0+DuIG3JDvogot8BfQNBlW3KZdFSb1vkqAwCMHtDjvV75Lw6l7ffAJzgb6n/8kCvDuT052rYuSgwyKLAfhSdq2Fn4P/EwD9Xw3rUe66GdVoNK08fFnkN0NHpfVXDtKmOjsQ6dt9nIFBNlIvr5ixGh/TSm6nzsE4UQmAZ7t4NGeSdObuQD1EOCTam0eEeCvVEaXWHtNmgsDYQDFmeZdh7d/XQNo0dS4WQdxyGLG8PGds9hv6+9b0/kUund/EMT+7jezp/Pl25ZeAHV7uIRtAz5A27fcKSita6nUYju7F/aE/ZVGjLVnR7o3h1CPgf7KQ89zgKxixwpOcBSFHUX/TSGqAevW5Z8lzDZ7bBkxEpo2jO+KZOhpwQiV3hjdjh1vPQGQ3D7DFab7LzNwdx1qbgClTTaX5lg07pHvafUj/e+b91WOvDuXUM5AiN39up6w7ctXTqA3DQA3GoDkCSddXfszX1pqqeU3vTenHm5/am2K0Fwvf2pn2ciTyfX9Hk2crJJaQ5RNnb+RWt5bs4RPmOG/GKbwfdOPdqVn3oWxlIELDwwZ0+sjvZ6Svle6RykD1HCK87kA6gfP8BUIVMTzK4bR6ZWyCFZ0RqktI3R2T+z5IL9aQN0pyNPWlqYemInB/NLb6wJKTBfeRvJtsV/OCZRc+/wql9hLp9oKkBhUrodgeKLvj/Xei3kBT6DRM7b4T/vg8LvplFFJ/FUBx+ccZU/XVP65N+joI19eDX0Q6fN3f/5ScfvvtfSfDTfw== \ No newline at end of file diff --git a/doc/nrf/libraries.rst b/doc/nrf/libraries.rst index 29968950161e..e6737c5e3920 100644 --- a/doc/nrf/libraries.rst +++ b/doc/nrf/libraries.rst @@ -25,13 +25,21 @@ Here you can find documentation for these libraries, including API documentation .. note:: |noBLE| +.. toctree:: + :maxdepth: 1 + :caption: Libraries for FOTA updates: + :glob: + + ../../include/net/* + + .. toctree:: :maxdepth: 1 :caption: Other libraries: :glob: ../../include/* - ../../include/net/* + ../../include/dfu/* ../../include/nfc/ndef/* ../../include/nfc/t2t/* ../../include/nfc/t4t/* diff --git a/doc/nrf/links.txt b/doc/nrf/links.txt index 91f830a6c4aa..021460787af5 100644 --- a/doc/nrf/links.txt +++ b/doc/nrf/links.txt @@ -50,8 +50,12 @@ .. _`Device programming section in the nRF9160 DK User Guide`: https://infocenter.nordicsemi.com/topic/ug_nrf91_dk/UG/nrf91_DK/mcu_device_programming.html +.. _`AT Commands Reference Guide`: https://infocenter.nordicsemi.com/topic/ref_at_commands/REF/at_commands/intro.html + .. _`system mode section in the AT Commands reference document`: https://infocenter.nordicsemi.com/topic/ref_at_commands/REF/at_commands/mob_termination_ctrl_status/xsystemmode.html +.. _`AT Commands reference`: https://infocenter.nordicsemi.com/topic/ref_at_commands/REF/at_commands/intro.html + .. ### Links to the Nordic website @@ -92,11 +96,13 @@ .. ### Links to other websites and software -.. _Git: https://git-scm.com/ +.. _`Git`: https://git-scm.com/ -.. _GitHub: https://github.com/ +.. _`GitHub`: https://github.com/ -.. _GitHub fork: https://help.github.com/en/articles/fork-a-repo +.. _`GitHub fork`: https://help.github.com/en/articles/fork-a-repo + +.. _`GitHub duplicate`: https://help.github.com/en/articles/duplicating-a-repository .. _`Semantic Versioning 2.0.0`: https://semver.org/ @@ -136,6 +142,12 @@ .. _`HIDAPI releases`: https://github.com/libusb/hidapi/releases .. _`pyhidapi Python wrapper`: https://github.com/apmorton/pyhidapi +.. _`AWS account`: https://aws.amazon.com/iot/ +.. _`AWS IoT console`: https://console.aws.amazon.com/iot/home +.. _`AWS S3 console`: https://console.aws.amazon.com/s3/home + +.. _`TLS`: https://www.ietf.org/rfc/rfc5246.txt + .. ### Links to Bluetooth specs @@ -165,6 +177,18 @@ .. _`generator expressions`: https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html +.. _`AWS IoT Developer Guide`: https://docs.aws.amazon.com/iot/latest/developerguide/what-is-aws-iot.html +.. _`AWS IoT MQTT`: https://docs.aws.amazon.com/iot/latest/developerguide/mqtt.html +.. _`AWS IoT Developer Guide: Basic Policy Variables`: https://docs.aws.amazon.com/iot/latest/developerguide/basic-policy-variables.html +.. _`AWS IoT Developer Guide: Security Best Practices`: https://docs.aws.amazon.com/iot/latest/developerguide/security-best-practices.html +.. _`AWS IoT jobs`: +.. _`AWS IoT Developer Guide: Jobs`: https://docs.aws.amazon.com/iot/latest/developerguide/iot-jobs.html + +.. _`AWS S3 Developer Guide: Using Bucket Policies and User Policies`: https://docs.aws.amazon.com/AmazonS3/latest/dev/using-iam-policies.html +.. _`AWS S3 Developer Guide: Bucket Policy Examples`: https://docs.aws.amazon.com/AmazonS3/latest/dev/example-bucket-policies.html + +.. _`AWS Simple Storage Service (S3)`: https://docs.aws.amazon.com/s3/index.html + .. ### Links to specific files on GitHub .. _`nrf9160_pca10090_partition_conf.dts`: https://github.com/NordicPlayground/fw-nrfconnect-zephyr/blob/master/boards/arm/nrf9160_pca10090/nrf9160_pca10090_partition_conf.dts diff --git a/doc/nrf/ug_dev_model.rst b/doc/nrf/ug_dev_model.rst index f2c4d02de447..ca422aca4d18 100644 --- a/doc/nrf/ug_dev_model.rst +++ b/doc/nrf/ug_dev_model.rst @@ -27,6 +27,25 @@ Basic familiarity with Git is required to understand the architecture of the rep All |NCS| repositories are publicly hosted on `GitHub`_, and accessible to both individual users and companies. +Forks and upstream/downstream repositories +========================================== + +Git is a distributed version control system that allows repositories to be easily duplicated. +Every time you take an existing Git repository and create a copy of it, you are creating a *fork* of that repository. +This means that you create an identical copy that might diverge from the original over time, since commits to the original will not be automatically reflected in the copy, and commits to your copy will not be automatically reflected in the original. + +.. note:: + When we talk about forks or copying Git repositories, we refer to the creation of a new repository hosted on a server and accessible to other users. + If you clone a repository to your local machine using ``git clone``, that is referred to as a *clone* and not a fork. + +When you create a fork by copying an existing repository, the original repository is called the *upstream* repository and the newly created copy the *downstream* repository. + +A fork can be hosted on any server, including a public Git hosting site like `GitHub`_. +It is, however, important to differentiate between the generic concept of a fork and GitHub's concept of a `GitHub fork`_. +When you create a GitHub fork, GitHub copies the original repository and tags the downstream repository (the fork) with a flag that allows users to send pull requests from the fork to its upstream repository. +GitHub also supports creating forks without linking them to the upstream respository. +See the `GitHub documentation `_ for information about how to do this. + .. _dm-repo-types: Repository types @@ -369,7 +388,7 @@ Upmerge The act of updating a downstream repository with a new revision of its upstream counterpart. Clone - A local copy of a remote Git repository. + A local copy of a remote Git repository obtained with ``git clone``. Fork A server-hosted copy of a repository (upstream) that intends to follow the changes made in the original repository as time goes by, while at the same time keeping some other changes unique to it. @@ -404,4 +423,3 @@ Commit tag Pull Request A GitHub Pull Request, a set of commits that are sent for code review using GitHub. - diff --git a/doc/nrfxlib/nrfxlib.doxyfile.in b/doc/nrfxlib/nrfxlib.doxyfile.in index 5e6426b8124d..12caea351ea9 100644 --- a/doc/nrfxlib/nrfxlib.doxyfile.in +++ b/doc/nrfxlib/nrfxlib.doxyfile.in @@ -1965,6 +1965,7 @@ PREDEFINED = "MBEDTLS_CONFIG_FILE=\"@NRFXLIB_BINARY_DIR@/mbedtls_dox "BUILD_ASSERT_MSG()=" \ "BUILD_ASSERT()=" \ "__deprecated=" \ + "__PACKED_STRUCT=struct" \ "__packed=" \ "__aligned(x)=" \ "__printf_like(x, y)=" \ diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index da84cd799e38..a63594934b76 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -13,3 +13,7 @@ add_subdirectory_ifdef(CONFIG_ADP536X adp536x) add_subdirectory_ifdef(CONFIG_ST25R3911B_LIB st25r3911b) add_subdirectory_ifdef(CONFIG_BT_LL_NRFXLIB bt_ll_nrfxlib) add_subdirectory_ifdef(CONFIG_NRF9160_GPS nrf9160_gps) +add_subdirectory_ifdef(CONFIG_FPROTECT fprotect) +add_subdirectory(flash_patch) +add_subdirectory_ifdef(CONFIG_HW_CC310 hw_cc310) +add_subdirectory(entropy) diff --git a/drivers/Kconfig b/drivers/Kconfig index 407b0447b0ce..e5cb020ac910 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -10,11 +10,15 @@ rsource "adp536x/Kconfig" rsource "at_cmd/Kconfig" rsource "gps_sim/Kconfig" +rsource "entropy/Kconfig" +rsource "hw_cc310/Kconfig" rsource "nrf9160_gps/Kconfig" rsource "lte_link_control/Kconfig" rsource "net/Kconfig" rsource "sensor/Kconfig" rsource "st25r3911b/Kconfig" +rsource "fprotect/Kconfig" +rsource "flash_patch/Kconfig" if BT_LL_NRFXLIB rsource "bt_ll_nrfxlib/Kconfig" diff --git a/drivers/at_cmd/at_cmd.c b/drivers/at_cmd/at_cmd.c index 341cd083d57e..b7427a168523 100644 --- a/drivers/at_cmd/at_cmd.c +++ b/drivers/at_cmd/at_cmd.c @@ -171,6 +171,8 @@ static void socket_thread_fn(void *arg1, void *arg2, void *arg3) goto next; } + LOG_DBG("at_cmd_rx: %s", log_strdup(item->data)); + payload_len = get_return_code(item->data, &ret); if (ret.state != AT_CMD_NOTIFICATION) { @@ -289,6 +291,11 @@ int at_cmd_write(const char *const cmd, void at_cmd_set_notification_handler(at_cmd_handler_t handler) { + LOG_DBG("Setting notification handler to %p", handler); + if ((notification_handler != NULL) && (handler != NULL)) { + LOG_WRN("Forgetting prior notification handler %p", + notification_handler); + } k_sem_take(&cmd_pending, K_FOREVER); diff --git a/drivers/entropy/CMakeLists.txt b/drivers/entropy/CMakeLists.txt new file mode 100644 index 000000000000..85e8599ad828 --- /dev/null +++ b/drivers/entropy/CMakeLists.txt @@ -0,0 +1,16 @@ +# +# Copyright (c) 2019 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic +# +zephyr_library_amend() +zephyr_library_sources_if_kconfig(entropy_cc310.c) + +# Link with the nrf_cc310 platform library if the following is met: +# -nRF52840 device +# -nRF9160 device that is not using SPM +# -nRF9150 device that is using SPM and in a secure image +# (CONFIG_SPM is not defined in a secure image) +if (CONFIG_SOC_NRF52840 OR (CONFIG_SOC_NRF9160 AND (NOT CONFIG_SPM))) + zephyr_link_libraries_ifdef(CONFIG_ENTROPY_CC310 ${IMAGE}platform_cc310) +endif () diff --git a/drivers/entropy/Kconfig b/drivers/entropy/Kconfig new file mode 100644 index 000000000000..418cad38f1e9 --- /dev/null +++ b/drivers/entropy/Kconfig @@ -0,0 +1,19 @@ +# Kconfig - Arm CC310 entropy driver for nRF52840 and nRF9160 +# +# Copyright (c) 2019 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic +# + +config ENTROPY_CC310 + bool "Arm CC310 RNG driver for Nordic devices" + depends on HW_CC310 || (SOC_NRF9160 && SPM) + depends on ENTROPY_GENERATOR + depends on !BT_LL_SW_LEGACY + depends on !BT_LLL_VENDOR_NORDIC + select ENTROPY_HAS_DRIVER + select ENTROPY_NRF_FORCE_ALT + default y + help + This option enables the Arm CC310 RNG devices in nRF52840 and nRF9160 + devices. This is dependent on CC310 being enabled in nrf_security. diff --git a/drivers/entropy/entropy_cc310.c b/drivers/entropy/entropy_cc310.c new file mode 100644 index 000000000000..6888b218b51e --- /dev/null +++ b/drivers/entropy/entropy_cc310.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +#include +#include +#include +#include +#include + +#include +#include + +#if CONFIG_ENTROPY_CC310 + +#if defined(CONFIG_SPM) +#include "secure_services.h" +#else +#include "nrf_cc310_platform_entropy.h" +#endif + +static int entropy_cc310_rng_get_entropy(struct device *dev, u8_t *buffer, + u16_t length) +{ + int res = -EINVAL; + size_t olen; + + __ASSERT_NO_MSG(dev != NULL); + __ASSERT_NO_MSG(buffer != NULL); + +#if defined(CONFIG_SPM) + /** This is a call from a non-secure app that enables secure services, + * in which case entropy is gathered by calling through SPM + */ + res = spm_request_random_number(buffer, length, &olen); + if (olen != length) { + return -EINVAL; + } + +#else + /** This is a call from a secure app, in which case entropy is gathered + * using CC310 HW using the nrf_cc310_platform library. + */ + res = nrf_cc310_platform_entropy_get(buffer, length, &olen); + if (olen != length) { + return -EINVAL; + } +#endif + + return res; +} + +static int entropy_cc310_rng_init(struct device *dev) +{ + /* No initialization is required */ + (void)dev; + + return 0; +} + +static const struct entropy_driver_api entropy_cc310_rng_api = { + .get_entropy = entropy_cc310_rng_get_entropy +}; + +DEVICE_AND_API_INIT(entropy_cc310_rng, CONFIG_ENTROPY_NAME, + &entropy_cc310_rng_init, + NULL, + NULL, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &entropy_cc310_rng_api); + +#endif /* CONFIG_ENTROPY_CC310 */ diff --git a/drivers/flash_patch/CMakeLists.txt b/drivers/flash_patch/CMakeLists.txt new file mode 100644 index 000000000000..8a82d2f09be7 --- /dev/null +++ b/drivers/flash_patch/CMakeLists.txt @@ -0,0 +1,19 @@ +# +# Copyright (c) 2019 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic +# + +if((NOT CONFIG_DISABLE_FLASH_PATCH) AND CONFIG_SOC_NRF52840 + AND (CONFIG_IS_SECURE_BOOTLOADER OR CONFIG_MCUBOOT)) + message(WARNING " + ---------------------------------------------------------- + --- WARNING: To maintain the integrity of secure boot, --- + --- enable CONFIG_DISABLE_FLASH_PATCH in production. --- + ----------------------------------------------------------" + ) +endif() + +if (CONFIG_DISABLE_FLASH_PATCH) + zephyr_sources(flash_patch.c) +endif() diff --git a/drivers/flash_patch/Kconfig b/drivers/flash_patch/Kconfig new file mode 100644 index 000000000000..3dc60fa21917 --- /dev/null +++ b/drivers/flash_patch/Kconfig @@ -0,0 +1,14 @@ +# +# Copyright (c) 2019 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic +# + +config DISABLE_FLASH_PATCH + bool "Disable Cortex-M4 Flash Patch capabilities" + depends on SOC_NRF52840 + depends on REBOOT + help + The flash patch can be used by malicious code to circumvent secure + boot checks. Note that disabling flash patching also disables + breakpoints. diff --git a/drivers/flash_patch/flash_patch.c b/drivers/flash_patch/flash_patch.c new file mode 100644 index 000000000000..57255b508d46 --- /dev/null +++ b/drivers/flash_patch/flash_patch.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +#include +#include +#include + +#ifdef CONFIG_DISABLE_FLASH_PATCH +static int disable_flash_patch(struct device *dev) +{ + (void)dev; + + /* Check if register has been written. */ + if ((NRF_UICR->DEBUGCTRL & UICR_DEBUGCTRL_CPUFPBEN_Msk) != 0x0) { + /* Enable flash write. */ + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen; + __DSB(); + __ISB(); + + /* Write to register. */ + NRF_UICR->DEBUGCTRL = ~UICR_DEBUGCTRL_CPUFPBEN_Msk; + + /* Wait for flash ready */ + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) + ; + + /* Disable flash write. */ + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren; + __DSB(); + __ISB(); + + /* Reset for change to take effect. */ + sys_reboot(SYS_REBOOT_WARM); + } + return 0; +} + +SYS_INIT(disable_flash_patch, POST_KERNEL, CONFIG_APPLICATION_INIT_PRIORITY); +#endif diff --git a/subsys/bootloader/fprotect/CMakeLists.txt b/drivers/fprotect/CMakeLists.txt similarity index 100% rename from subsys/bootloader/fprotect/CMakeLists.txt rename to drivers/fprotect/CMakeLists.txt diff --git a/drivers/fprotect/Kconfig b/drivers/fprotect/Kconfig new file mode 100644 index 000000000000..a0f635bdb16c --- /dev/null +++ b/drivers/fprotect/Kconfig @@ -0,0 +1,12 @@ +# +# Copyright (c) 2019 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic +# + +config FPROTECT + bool "Write-protect flash regions" + depends on SOC_FAMILY_NRF + help + Use hardware peripherals (BPROT, ACL, or SPU) to write-protect + regions of flash until next reset. diff --git a/subsys/bootloader/fprotect/fprotect_acl.c b/drivers/fprotect/fprotect_acl.c similarity index 93% rename from subsys/bootloader/fprotect/fprotect_acl.c rename to drivers/fprotect/fprotect_acl.c index 7ece2ccf1294..f17974da9a48 100644 --- a/subsys/bootloader/fprotect/fprotect_acl.c +++ b/drivers/fprotect/fprotect_acl.c @@ -11,7 +11,7 @@ int fprotect_area(u32_t start, size_t length) { static u32_t region_idx; - if (region_idx > ACL_REGIONS_COUNT) { + if (region_idx >= ACL_REGIONS_COUNT) { return -ENOSPC; } diff --git a/subsys/bootloader/fprotect/fprotect_bprot.c b/drivers/fprotect/fprotect_bprot.c similarity index 100% rename from subsys/bootloader/fprotect/fprotect_bprot.c rename to drivers/fprotect/fprotect_bprot.c diff --git a/subsys/bootloader/fprotect/fprotect_spu.c b/drivers/fprotect/fprotect_spu.c similarity index 100% rename from subsys/bootloader/fprotect/fprotect_spu.c rename to drivers/fprotect/fprotect_spu.c diff --git a/drivers/hw_cc310/CMakeLists.txt b/drivers/hw_cc310/CMakeLists.txt new file mode 100644 index 000000000000..a6cd66eead94 --- /dev/null +++ b/drivers/hw_cc310/CMakeLists.txt @@ -0,0 +1,10 @@ +# +# Copyright (c) 2019 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic +# +zephyr_library() +zephyr_library_sources(hw_cc310.c) + +# Link with the nrf_cc310 platform library +zephyr_library_link_libraries(${IMAGE}platform_cc310) diff --git a/drivers/hw_cc310/Kconfig b/drivers/hw_cc310/Kconfig new file mode 100644 index 000000000000..d2e283809898 --- /dev/null +++ b/drivers/hw_cc310/Kconfig @@ -0,0 +1,42 @@ +# Kconfig - Arm CC310 hw driver for nRF52840 and nRF9160 +# +# Copyright (c) 2019 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic +# + +# Defining MCUBOOT symbol here to avoid an issue discovered when building +# multi image zephyr/mcuboot. +config MCUBOOT + bool + +config HW_CC310_FORCE_ALT + bool + depends on SOC_COMPATIBLE_NRF + help + This option can be enabled to force an alternative implementation of + the Arm CC310 hardware driver. + +if !HW_CC310_FORCE_ALT + +menuconfig HW_CC310 + bool "Arm CC310 hw driver for Nordic devices" + depends on SOC_NRF52840 || (SOC_NRF9160 && !SPM) || (SOC_NRF9160 && TRUSTED_EXECUTION_SECURE) + select NRF_CC310_PLATFORM + default n if MCUBOOT + default y if SOC_NRF52840 || (SOC_NRF9160 && !SPM) || (SOC_NRF9160 && TRUSTED_EXECUTION_SECURE) + help + This option enables the Arm CC310 hw devices in nRF52840 and nRF9160 devices. + +if HW_CC310 + +config HW_CC310_NAME + string "Entropy Device Name" + default "HW_CC310_0" + help + Specify the device name to be used for the HW_CC310 driver. + + +endif # HW_CC310 + +endif # !HW_CC310_FORCE_ALT diff --git a/drivers/hw_cc310/hw_cc310.c b/drivers/hw_cc310/hw_cc310.c new file mode 100644 index 000000000000..834ac4bded0b --- /dev/null +++ b/drivers/hw_cc310/hw_cc310.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +#include +#include +#include +#include + +#include + +#if CONFIG_HW_CC310 + +#include "nrf_cc310_platform.h" + +static int hw_cc310_init(struct device *dev) +{ + int res; + + __ASSERT_NO_MSG(dev != NULL); + + /* Set the RTOS abort APIs */ + nrf_cc310_platform_abort_init(); + + /* Set the RTOS mutex APIs */ + nrf_cc310_platform_mutex_init(); + + /* Initialize the cc310 HW with or without RNG support */ +#if CONFIG_ENTROPY_CC310 + res = nrf_cc310_platform_init(); +#else + res = nrf_cc310_platform_init_no_rng(); +#endif + return res; +} + +DEVICE_INIT(hw_cc310, CONFIG_HW_CC310_NAME, hw_cc310_init, + NULL, NULL, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); + +#endif /* CONFIG_HW_CC310 */ diff --git a/drivers/lte_link_control/Kconfig b/drivers/lte_link_control/Kconfig index 7bb0c3a9f534..7c811e054ae2 100644 --- a/drivers/lte_link_control/Kconfig +++ b/drivers/lte_link_control/Kconfig @@ -8,6 +8,7 @@ menuconfig LTE_LINK_CONTROL bool "nRF91 LTE Link control library" select AT_CMD select AT_CMD_PARSER + select AT_NOTIF default n if LTE_LINK_CONTROL @@ -38,7 +39,6 @@ endif # LTE_LOCK_BANDS config LTE_LOCK_PLMN bool "Enable LTE PLMN lock" - default n help Enable PLMN locks for network selection. @@ -51,6 +51,12 @@ config LTE_LOCK_PLMN_STRING Only numeric string formats supported. endif # LTE_LOCK_PLMN +config LTE_UNLOCK_PLMN + bool "Disable LTE PLMN lock" + depends on !LTE_LOCK_PLMN + help + Disable PLMN locks for network selection. + config LTE_PSM_REQ_RPTAU string "PSM setting requested periodic TAU" default "00000011" diff --git a/drivers/lte_link_control/lte_lc.c b/drivers/lte_link_control/lte_lc.c index 91033a32491a..ba0ac52ae18f 100644 --- a/drivers/lte_link_control/lte_lc.c +++ b/drivers/lte_link_control/lte_lc.c @@ -15,6 +15,7 @@ #include #include #include +#include #include LOG_MODULE_REGISTER(lte_lc, CONFIG_LTE_LINK_CONTROL_LOG_LEVEL); @@ -78,6 +79,9 @@ static const char lock_bands[] = "AT%XBANDLOCK=2,\""CONFIG_LTE_LOCK_BAND_MASK /* Lock PLMN */ static const char lock_plmn[] = "AT+COPS=1,2,\"" CONFIG_LTE_LOCK_PLMN_STRING"\""; +#elif defined(CONFIG_LTE_UNLOCK_PLMN) +/* Unlock PLMN */ +static const char unlock_plmn[] = "AT+COPS=0"; #endif /* Request eDRX settings to be used */ static const char edrx_req[] = "AT+CEDRXS=1,"CONFIG_LTE_EDRX_REQ_ACTT_TYPE @@ -131,8 +135,10 @@ static const char cgauth[] = "AT+CGAUTH="CONFIG_LTE_PDN_AUTH; static const char legacy_pco[] = "AT%XEPCO=0"; #endif -void at_handler(char *response) +void at_handler(void *context, char *response) { + ARG_UNUSED(context); + int err; enum lte_lc_nw_reg_status status; @@ -181,12 +187,18 @@ static int w_lte_lc_init(void) } #endif #if defined(CONFIG_LTE_LOCK_PLMN) - /* Set Operator (volatile setting). + /* Manually select Operator (volatile setting). * Has to be done every time before activating the modem. */ if (at_cmd_write(lock_plmn, NULL, 0, NULL) != 0) { return -EIO; } +#elif defined(CONFIG_LTE_UNLOCK_PLMN) + /* Automatically select Operator (volatile setting). + */ + if (at_cmd_write(unlock_plmn, NULL, 0, NULL) != 0) { + return -EIO; + } #endif #if defined(CONFIG_LTE_LEGACY_PCO_MODE) if (at_cmd_write(legacy_pco, NULL, 0, NULL) != 0) { @@ -212,12 +224,17 @@ static int w_lte_lc_init(void) static int w_lte_lc_connect(void) { - int err; + int err, rc; const char *current_network_mode = nw_mode_preferred; bool retry; k_sem_init(&link, 0, 1); - at_cmd_set_notification_handler(at_handler); + + rc = at_notif_register_handler(NULL, at_handler); + if (rc != 0) { + LOG_ERR("Can't register handler rc=%d", rc); + return rc; + } do { retry = false; @@ -256,7 +273,10 @@ static int w_lte_lc_connect(void) } while (retry); exit: - at_cmd_set_notification_handler(NULL); + rc = at_notif_deregister_handler(NULL, at_handler); + if (rc != 0) { + LOG_ERR("Can't de-register handler rc=%d", rc); + } return err; } diff --git a/drivers/nrf9160_gps/Kconfig b/drivers/nrf9160_gps/Kconfig index c3937db4194f..5be7f7979bf5 100644 --- a/drivers/nrf9160_gps/Kconfig +++ b/drivers/nrf9160_gps/Kconfig @@ -51,6 +51,18 @@ config NRF9160_GPS_MAGPIO_STRING endif # NRF9160_GPS_SET_MAGPIO +config NRF9160_GPS_SET_COEX0 + bool "Let the driver set COEX0 configuration" + default y if BOARD_NRF9160_PCA10090NS + +if NRF9160_GPS_SET_COEX0 + +config NRF9160_GPS_COEX0_STRING + string "COEX0 string" + default "AT\%XCOEX0=1,1,1570,1580" if BOARD_NRF9160_PCA10090NS + +endif # NRF9160_GPS_SET_COEX0 + endif # NRF9160_GPS_HANDLE_MODEM_CONFIGURATION menu "NMEA strings" diff --git a/drivers/nrf9160_gps/nrf9160_gps.c b/drivers/nrf9160_gps/nrf9160_gps.c index d6ea02ab432f..f437b895a93c 100644 --- a/drivers/nrf9160_gps/nrf9160_gps.c +++ b/drivers/nrf9160_gps/nrf9160_gps.c @@ -35,6 +35,10 @@ LOG_MODULE_REGISTER(nrf9160_gps, CONFIG_NRF9160_GPS_LOG_LEVEL); #define FUNCTIONAL_MODE_ENABLED 1 #endif +/* Aligned strings describing sattelite states based on flags */ +#define sv_used_str(x) ((x)?" used":"not used") +#define sv_unhealthy_str(x) ((x)?"not healthy":" healthy") + struct gps_drv_data { gps_trigger_handler_t trigger_handler; struct gps_trigger trigger; @@ -70,6 +74,10 @@ static void copy_pvt(struct gps_pvt *dest, nrf_gnss_pvt_data_frame_t *src) dest->datetime.minute = src->datetime.minute; dest->datetime.seconds = src->datetime.seconds; dest->datetime.ms = src->datetime.ms; + dest->pdop = src->pdop; + dest->hdop = src->hdop; + dest->vdop = src->vdop; + dest->tdop = src->tdop; for (size_t i = 0; i < MIN(NRF_GNSS_MAX_SATELLITES, GPS_MAX_SATELLITES); i++) { @@ -92,32 +100,35 @@ static u64_t fix_timestamp; static void print_satellite_stats(nrf_gnss_data_frame_t *pvt_data) { - u8_t tracked = 0; - u8_t in_fix = 0; - u8_t unhealthy = 0; + u8_t n_tracked = 0; + u8_t n_used = 0; + u8_t n_unhealthy = 0; for (int i = 0; i < NRF_GNSS_MAX_SATELLITES; ++i) { - - if ((pvt_data->pvt.sv[i].sv > 0) && - (pvt_data->pvt.sv[i].sv < 33)) { - - tracked++; - - if (pvt_data->pvt.sv[i].flags & - NRF_GNSS_PVT_FLAG_FIX_VALID_BIT) { - in_fix++; + u8_t sv = pvt_data->pvt.sv[i].sv; + bool used = (pvt_data->pvt.sv[i].flags & + NRF_GNSS_SV_FLAG_USED_IN_FIX) ? true : false; + bool unhealthy = (pvt_data->pvt.sv[i].flags & + NRF_GNSS_SV_FLAG_UNHEALTHY) ? true : false; + + if (sv) { /* SV number 0 indicates no satellite */ + n_tracked++; + if (used) { + n_used++; } - - if (pvt_data->pvt.sv[i].flags & - NRF_GNSS_SV_FLAG_UNHEALTHY) { - unhealthy++; + if (unhealthy) { + n_unhealthy++; } + + LOG_DBG("Tracking SV %2u: %s, %s", sv, + sv_used_str(used), + sv_unhealthy_str(unhealthy)); } } - LOG_DBG("Tracking: %d Using: %d Unhealthy: %d", tracked, - in_fix, - unhealthy); + LOG_DBG("Tracking: %d Using: %d Unhealthy: %d", n_tracked, + n_used, + n_unhealthy); LOG_DBG("Seconds since last fix %lld", (k_uptime_get() - fix_timestamp) / 1000); @@ -145,10 +156,9 @@ static void gps_thread(int dev_ptr) continue; } - print_satellite_stats(&raw_gps_data); - switch (raw_gps_data.data_id) { case NRF_GNSS_PVT_DATA_ID: + print_satellite_stats(&raw_gps_data); copy_pvt(&fresh_pvt.pvt, &raw_gps_data.pvt); if ((drv_data->trigger.chan == GPS_CHAN_PVT) @@ -436,12 +446,13 @@ static int init(struct device *dev) init_thread(dev); - #if CONFIG_NRF9160_GPS_SET_MAGPIO + #if CONFIG_NRF9160_GPS_SET_MAGPIO || CONFIG_NRF9160_GPS_SET_COEX0 int err; - char buf[50] = {0}; + #endif + #if CONFIG_NRF9160_GPS_SET_MAGPIO err = at_cmd_write(CONFIG_NRF9160_GPS_MAGPIO_STRING, - buf, sizeof(buf), NULL); + NULL, 0, NULL); if (err) { LOG_ERR("Could not confiugure MAGPIO, error: %d", err); return err; @@ -451,6 +462,18 @@ static int init(struct device *dev) log_strdup(CONFIG_NRF9160_GPS_MAGPIO_STRING)); #endif /* CONFIG_NRF9160_GPS_SET_MAGPIO */ + #if CONFIG_NRF9160_GPS_SET_COEX0 + err = at_cmd_write(CONFIG_NRF9160_GPS_COEX0_STRING, + NULL, 0, NULL); + if (err) { + LOG_ERR("Could not confiugure COEX0, error: %d", err); + return err; + } + + LOG_DBG("COEX0 set: %s", + log_strdup(CONFIG_NRF9160_GPS_COEX0_STRING)); + #endif /* CONFIG_NRF9160_GPS_SET_COEX0 */ + return 0; } diff --git a/dts/bindings/sensor/pixart,paw3212.yaml b/dts/bindings/sensor/pixart,paw3212.yaml index 69cbc6e03087..9d2b2329b60e 100644 --- a/dts/bindings/sensor/pixart,paw3212.yaml +++ b/dts/bindings/sensor/pixart,paw3212.yaml @@ -16,5 +16,5 @@ include: spi-device.yaml properties: irq-gpios: - type: compound + type: phandle-array required: true diff --git a/dts/bindings/sensor/pixart,pmw3360.yaml b/dts/bindings/sensor/pixart,pmw3360.yaml index 170fb96236c5..a4feefe2010d 100644 --- a/dts/bindings/sensor/pixart,pmw3360.yaml +++ b/dts/bindings/sensor/pixart,pmw3360.yaml @@ -16,5 +16,5 @@ include: spi-device.yaml properties: irq-gpios: - type: compound + type: phandle-array required: true diff --git a/dts/bindings/sensor/rohm,bh1749.yaml b/dts/bindings/sensor/rohm,bh1749.yaml index ce3079757cf2..511fc7504fd0 100644 --- a/dts/bindings/sensor/rohm,bh1749.yaml +++ b/dts/bindings/sensor/rohm,bh1749.yaml @@ -15,5 +15,5 @@ include: i2c-device.yaml properties: int-gpios: - type: compound + type: phandle-array required: true diff --git a/ext/cjson/cJSON.c b/ext/cjson/cJSON.c index ae80a5f86e8c..f04010fad93b 100644 --- a/ext/cjson/cJSON.c +++ b/ext/cjson/cJSON.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2009 Dave Gamble + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -22,1456 +22,2959 @@ /* cJSON */ /* JSON parser in C. */ -/* Suppress Lint warnings for third-party library. */ -/*lint -save -e534 -e547 -e616 -e687 -e689 */ -#include "cJSON.h" -#include "math.h" -#include -#include -#include + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + +#include #include +#include #include -#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" -static const char *ep; +/* define our own boolean type */ +#ifdef true +#undef true +#endif +#define true ((cJSON_bool)1) -const char *cJSON_GetErrorPtr(void) { return ep; } +#ifdef false +#undef false +#endif +#define false ((cJSON_bool)0) + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; -static int cJSON_strcasecmp(const char *s1, const char *s2) +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) { - if (!s1) { - return (s1 == s2) ? 0 : 1; - }; - if (!s2) { - return 1; - }; - for (; tolower(*(const unsigned char *)s1) == - tolower(*(const unsigned char *)s2); - ++s1, ++s2) - if (*s1 == 0) { - return 0; - }; - return tolower(*(const unsigned char *)s1) - - tolower(*(const unsigned char *)s2); + return (const char*) (global_error.json + global_error.position); } -static void *(*cJSON_malloc)(size_t sz) = malloc; -static void (*cJSON_free)(void *ptr) = free; +CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item) { + if (!cJSON_IsString(item)) { + return NULL; + } -static char *cJSON_strdup(const char *str) + return item->valuestring; +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 12) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) { - size_t len; - char *copy; + static char version[15]; + snprintf(version, sizeof(version), "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); - len = strlen(str) + 1; - copy = (char *)cJSON_malloc(len); - if (!copy) - return 0; - memcpy(copy, str, len); - return copy; + return version; } -void cJSON_InitHooks(cJSON_Hooks *hooks) +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) { - if (!hooks) { /* Reset hooks */ - cJSON_malloc = malloc; - cJSON_free = free; - return; - } + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} - cJSON_malloc = (hooks->malloc_fn) ? hooks->malloc_fn : malloc; - cJSON_free = (hooks->free_fn) ? hooks->free_fn : free; +typedef struct internal_hooks +{ + void *(CJSON_CDECL *allocate)(size_t size); + void (CJSON_CDECL *deallocate)(void *pointer); + void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ +static void * CJSON_CDECL internal_malloc(size_t size) +{ + return malloc(size); +} +static void CJSON_CDECL internal_free(void *pointer) +{ + free(pointer); +} +static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) +{ + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +/* strlen of character literals resolved at compile time */ +#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) + +static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } } /* Internal constructor. */ -static cJSON *cJSON_New_Item(void) +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) { - cJSON *node = (cJSON *)cJSON_malloc(sizeof(cJSON)); - if (node) - memset(node, 0, sizeof(cJSON)); - return node; + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; } /* Delete a cJSON structure. */ -void cJSON_Delete(cJSON *c) -{ - cJSON *next; - while (c) { - next = c->next; - if (!(c->type & cJSON_IsReference) && c->child) - cJSON_Delete(c->child); - if (!(c->type & cJSON_IsReference) && c->valuestring) - cJSON_free(c->valuestring); - if (!(c->type & cJSON_StringIsConst) && c->string) - cJSON_free(c->string); - cJSON_free(c); - c = next; - } -} - -#ifdef CJSON_DOUBLE_SUPPORT -/* Parse the input text to generate a number, and populate the result into item. - */ -static const char *parse_number(cJSON *item, const char *num) -{ - double n = 0, sign = 1, scale = 0; - int subscale = 0, signsubscale = 1; - - if (*num == '-') - sign = -1, num++; /* Has sign? */ - if (*num == '0') - num++; /* is zero */ - if (*num >= '1' && *num <= '9') - do - n = (n * 10.0) + (*num++ - '0'); - while (*num >= '0' && *num <= '9'); /* Number? */ - if (*num == '.' && num[1] >= '0' && num[1] <= '9') { - num++; - do - n = (n * 10.0) + (*num++ - '0'), scale--; - while (*num >= '0' && *num <= '9'); - } /* Fractional part? */ - if (*num == 'e' || *num == 'E') /* Exponent? */ - { - num++; - if (*num == '+') - num++; - else if (*num == '-') - signsubscale = -1, num++; /* With sign? */ - while (*num >= '0' && *num <= '9') - subscale = - (subscale * 10) + (*num++ - '0'); /* Number? */ - } - - n = sign * n * - pow(10.0, - (scale + - subscale * signsubscale)); /* number = +/- number.fraction * - 10^+/- exponent */ - - item->valuedouble = n; - item->valueint = (int)n; - item->type = cJSON_Number; - return num; +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } } + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ +#ifdef ENABLE_LOCALES + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; #else -/* Parse the input text to generate a number, and populate the result into item. - */ -static const char *parse_number(cJSON *item, const char *num) + return '.'; +#endif +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) { - int n = 0, sign = 1; + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} - if (*num == '-') - sign = -1, num++; /* Has sign? */ - if (*num == '0') - num++; /* is zero */ - if (*num >= '1' && *num <= '9') - do - n = (n * 10) + (*num++ - '0'); - while (*num >= '0' && *num <= '9'); /* Number? */ +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} - n = sign * n; +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; - item->valueint = (int)n; - item->type = cJSON_Number; - return num; +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + if (newbuffer) + { + memcpy(newbuffer, p->buffer, p->offset + 1); + } + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; } -#endif -static int pow2gt(int x) +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) { - --x; - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - x |= x >> 8; - x |= x >> 16; - return x + 1; + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); } -typedef struct { - char *buffer; - int length; - int offset; -} printbuffer; - -static char *ensure(printbuffer *p, int needed) +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) { - char *newbuffer; - int newsize; - if (!p || !p->buffer) - return 0; - needed += p->offset; - if (needed <= p->length) - return p->buffer + p->offset; + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26]; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if ((d * 0) != 0) + { + length = snprintf((char*)number_buffer, sizeof(number_buffer), "null"); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = snprintf((char*)number_buffer, sizeof(number_buffer), "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d)) + { + /* If not, print with 17 decimal places of precision */ + length = snprintf((char*)number_buffer, sizeof(number_buffer), "%1.17g", d); + } + } + + /* snprintf failed or buffer overrun occurred */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} - newsize = pow2gt(needed); - newbuffer = (char *)cJSON_malloc(newsize); - if (!newbuffer) { - cJSON_free(p->buffer); - p->length = 0, p->buffer = 0; - return 0; - } - if (newbuffer) - memcpy(newbuffer, p->buffer, p->length); - cJSON_free(p->buffer); - p->length = newsize; - p->buffer = newbuffer; - return newbuffer + p->offset; +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; } -static int update(printbuffer *p) +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) { - char *str; - if (!p || !p->buffer) - return 0; - str = p->buffer + p->offset; - return p->offset + strlen(str); + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; } -/* Render the number nicely from the given item into a string. */ -static char *print_number(cJSON *item, printbuffer *p) -{ - char *str = 0; - double d = item->valuedouble; - if (d == 0) { - if (p) - str = ensure(p, 2); - else - str = (char *)cJSON_malloc(2); /* special case for 0. */ - if (str) - strcpy(str, "0"); - } else if (fabs(((double)item->valueint) - d) <= DBL_EPSILON && - d <= INT_MAX && d >= INT_MIN) { - if (p) - str = ensure(p, 21); - else - str = (char *)cJSON_malloc( - 21); /* 2^64+1 can be represented in 21 chars. - */ - if (str) - sprintf(str, "%d", item->valueint); - } else { - if (p) - str = ensure(p, 64); - else - str = (char *)cJSON_malloc( - 64); /* This is a nice tradeoff. */ - if (str) { - if (fabs(floor(d) - d) <= DBL_EPSILON && - fabs(d) < 1.0e60) - sprintf(str, "%.0f", d); - else if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9) - sprintf(str, "%e", d); - else - sprintf(str, "%f", d); - } - } - return str; -} - -static unsigned parse_hex4(const char *str) -{ - unsigned h = 0; - if (*str >= '0' && *str <= '9') - h += (*str) - '0'; - else if (*str >= 'A' && *str <= 'F') - h += 10 + (*str) - 'A'; - else if (*str >= 'a' && *str <= 'f') - h += 10 + (*str) - 'a'; - else - return 0; - h = h << 4; - str++; - if (*str >= '0' && *str <= '9') - h += (*str) - '0'; - else if (*str >= 'A' && *str <= 'F') - h += 10 + (*str) - 'A'; - else if (*str >= 'a' && *str <= 'f') - h += 10 + (*str) - 'a'; - else - return 0; - h = h << 4; - str++; - if (*str >= '0' && *str <= '9') - h += (*str) - '0'; - else if (*str >= 'A' && *str <= 'F') - h += 10 + (*str) - 'A'; - else if (*str >= 'a' && *str <= 'f') - h += 10 + (*str) - 'a'; - else - return 0; - h = h << 4; - str++; - if (*str >= '0' && *str <= '9') - h += (*str) - '0'; - else if (*str >= 'A' && *str <= 'F') - h += 10 + (*str) - 'A'; - else if (*str >= 'a' && *str <= 'f') - h += 10 + (*str) - 'a'; - else - return 0; - return h; -} - -/* Parse the input text into an unescaped cstring, and populate item. */ -static const unsigned char firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0, - 0xF0, 0xF8, 0xFC}; -static const char *parse_string(cJSON *item, const char *str) -{ - const char *ptr = str + 1; - char *ptr2; - char *out; - int len = 0; - unsigned uc, uc2; - if (*str != '\"') { - ep = str; - return 0; - } /* not a string! */ - - while (*ptr != '\"' && *ptr && ++len) - if (*ptr++ == '\\') - ptr++; /* Skip escaped quotes. */ - - out = (char *)cJSON_malloc( - len + - 1); /* This is how long we need for the string, roughly. */ - if (!out) - return 0; - - ptr = str + 1; - ptr2 = out; - while (*ptr != '\"' && *ptr) { - if (*ptr != '\\') - *ptr2++ = *ptr++; - else { - ptr++; - switch (*ptr) { - case 'b': - *ptr2++ = '\b'; - break; - case 'f': - *ptr2++ = '\f'; - break; - case 'n': - *ptr2++ = '\n'; - break; - case 'r': - *ptr2++ = '\r'; - break; - case 't': - *ptr2++ = '\t'; - break; - case 'u': /* transcode utf16 to utf8. */ - uc = parse_hex4(ptr + 1); - ptr += 4; /* get the unicode char. */ - - if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) - break; /* check for invalid. */ - - if (uc >= 0xD800 && - uc <= 0xDBFF) /* UTF16 surrogate pairs. - */ - { - if (ptr[1] != '\\' || ptr[2] != 'u') - break; /* missing second-half of - surrogate. */ - uc2 = parse_hex4(ptr + 3); - ptr += 6; - if (uc2 < 0xDC00 || uc2 > 0xDFFF) - break; /* invalid second-half of - surrogate. */ - uc = 0x10000 + (((uc & 0x3FF) << 10) | - (uc2 & 0x3FF)); - } - - len = 4; - if (uc < 0x80) - len = 1; - else if (uc < 0x800) - len = 2; - else if (uc < 0x10000) - len = 3; - ptr2 += len; - - switch (len) { - case 4: - *--ptr2 = ((uc | 0x80) & 0xBF); - uc >>= 6; - case 3: - *--ptr2 = ((uc | 0x80) & 0xBF); - uc >>= 6; - case 2: - *--ptr2 = ((uc | 0x80) & 0xBF); - uc >>= 6; - case 1: - *--ptr2 = (uc | firstByteMark[len]); - } - ptr2 += len; - break; - default: - *ptr2++ = *ptr; - break; - } - ptr++; - } - } - *ptr2 = 0; - if (*ptr == '\"') - ptr++; - item->valuestring = out; - item->type = cJSON_String; - return ptr; +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; } /* Render the cstring provided to an escaped version that can be printed. */ -static char *print_string_ptr(const char *str, printbuffer *p) -{ - const char *ptr; - char *ptr2, *out; - int len = 0, flag = 0; - unsigned char token; - - for (ptr = str; *ptr; ptr++) - flag |= ((*ptr > 0 && *ptr < 32) || (*ptr == '\"') || - (*ptr == '\\')) - ? 1 - : 0; - if (!flag) { - len = ptr - str; - if (p) - out = ensure(p, len + 3); - else - out = (char *)cJSON_malloc(len + 3); - if (!out) - return 0; - ptr2 = out; - *ptr2++ = '\"'; - strcpy(ptr2, str); - ptr2[len] = '\"'; - ptr2[len + 1] = 0; - return out; - } - - if (!str) { - if (p) - out = ensure(p, 3); - else - out = (char *)cJSON_malloc(3); - if (!out) - return 0; - strcpy(out, "\"\""); - return out; - } - ptr = str; - token = *ptr; - while ((token) && ++len) { - if (strchr("\"\\\b\f\n\r\t", token)) - len++; - else if (token < 32) - len += 5; - ptr++; - token = *ptr; - } - - if (p) - out = ensure(p, len + 3); - else - out = (char *)cJSON_malloc(len + 3); - if (!out) - return 0; - - ptr2 = out; - ptr = str; - *ptr2++ = '\"'; - while (*ptr) { - if ((unsigned char)*ptr > 31 && *ptr != '\"' && *ptr != '\\') - *ptr2++ = *ptr++; - else { - *ptr2++ = '\\'; - switch (token = *ptr++) { - case '\\': - *ptr2++ = '\\'; - break; - case '\"': - *ptr2++ = '\"'; - break; - case '\b': - *ptr2++ = 'b'; - break; - case '\f': - *ptr2++ = 'f'; - break; - case '\n': - *ptr2++ = 'n'; - break; - case '\r': - *ptr2++ = 'r'; - break; - case '\t': - *ptr2++ = 't'; - break; - default: - sprintf(ptr2, "u%04x", token); - ptr2 += 5; - break; /* escape and print */ - } - } - } - *ptr2++ = '\"'; - *ptr2++ = 0; - return out; -} -/* Invote print_string_ptr (which is useful) on an item. */ -static char *print_string(cJSON *item, printbuffer *p) -{ - return print_string_ptr(item->valuestring, p); +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } + + const char quotes[] = "\"\""; + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof(quotes)); + if (output == NULL) + { + return false; + } + strncpy((char*)output, quotes, output_buffer->length - output_buffer->offset); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof(quotes)); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + snprintf((char*)output_pointer, output_buffer->length - (output_pointer - output_buffer->buffer), "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); } /* Predeclare these prototypes. */ -static const char *parse_value(cJSON *item, const char *value); -static char *print_value(cJSON *item, int depth, int fmt, printbuffer *p); -static const char *parse_array(cJSON *item, const char *value); -static char *print_array(cJSON *item, int depth, int fmt, printbuffer *p); -static const char *parse_object(cJSON *item, const char *value); -static char *print_object(cJSON *item, int depth, int fmt, printbuffer *p); +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); /* Utility to jump whitespace and cr/lf */ -static const char *skip(const char *in) +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) { - while (in && *in && (unsigned char)*in <= 32) - in++; - return in; + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { + buffer->offset += 3; + } + + return buffer; } /* Parse an object - create a new root, and populate. */ -cJSON *cJSON_ParseWithOpts(const char *value, const char **return_parse_end, - int require_null_terminated) -{ - const char *end = 0; - cJSON *c = cJSON_New_Item(); - ep = 0; - if (!c) - return 0; /* memory fail */ - - end = parse_value(c, skip(value)); - if (!end) { - cJSON_Delete(c); - return 0; - } /* parse failure. ep is set. */ - - /* if we require null-terminated JSON without appended garbage, skip and - * then check for a null terminator */ - if (require_null_terminated) { - end = skip(end); - if (*end) { - cJSON_Delete(c); - ep = end; - return 0; - } - } - if (return_parse_end) - *return_parse_end = end; - return c; +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = strlen((const char*)value) + sizeof(""); + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; } + /* Default options for cJSON_Parse */ -cJSON *cJSON_Parse(const char *value) +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +#define cjson_min(a, b) ((a < b) ? a : b) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) { - return cJSON_ParseWithOpts(value, 0, 0); + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); + if (printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + } + + if (printed != NULL) + { + hooks->deallocate(printed); + } + + return NULL; } /* Render a cJSON item/entity/structure to text. */ -char *cJSON_Print(cJSON *item) { return print_value(item, 0, 1, 0); } -char *cJSON_PrintUnformatted(cJSON *item) { return print_value(item, 0, 0, 0); } +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} -char *cJSON_PrintBuffered(cJSON *item, int prebuffer, int fmt) +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) { - printbuffer p; - p.buffer = (char *)cJSON_malloc(prebuffer); - p.length = prebuffer; - p.offset = 0; - return print_value(item, 0, fmt, &p); + return (char*)print(item, false, &global_hooks); } +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((len < 0) || (buf == NULL)) + { + return false; + } + + p.buffer = (unsigned char*)buf; + p.length = (size_t)len; + p.offset = 0; + p.noalloc = true; + p.format = fmt; + p.hooks = global_hooks; + + return print_value(item, &p); +} /* Parser core - when encountering text, process appropriately. */ -static const char *parse_value(cJSON *item, const char *value) -{ - if (!value) - return 0; /* Fail on null. */ - if (!strncmp(value, "null", 4)) { - item->type = cJSON_NULL; - return value + 4; - } - if (!strncmp(value, "false", 5)) { - item->type = cJSON_False; - return value + 5; - } - if (!strncmp(value, "true", 4)) { - item->type = cJSON_True; - item->valueint = 1; - return value + 4; - } - if (*value == '\"') { - return parse_string(item, value); - } - if (*value == '-' || (*value >= '0' && *value <= '9')) { - return parse_number(item, value); - } - if (*value == '[') { - return parse_array(item, value); - } - if (*value == '{') { - return parse_object(item, value); - } - - ep = value; - return 0; /* failure. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + return false; } /* Render a value to text. */ -static char *print_value(cJSON *item, int depth, int fmt, printbuffer *p) -{ - char *out = 0; - if (!item) - return 0; - if (p) { - switch ((item->type) & 255) { - case cJSON_NULL: { - out = ensure(p, 5); - if (out) - strcpy(out, "null"); - break; - } - case cJSON_False: { - out = ensure(p, 6); - if (out) - strcpy(out, "false"); - break; - } - case cJSON_True: { - out = ensure(p, 5); - if (out) - strcpy(out, "true"); - break; - } - case cJSON_Number: - out = print_number(item, p); - break; - case cJSON_String: - out = print_string(item, p); - break; - case cJSON_Array: - out = print_array(item, depth, fmt, p); - break; - case cJSON_Object: - out = print_object(item, depth, fmt, p); - break; - } - } else { - switch ((item->type) & 255) { - case cJSON_NULL: - out = cJSON_strdup("null"); - break; - case cJSON_False: - out = cJSON_strdup("false"); - break; - case cJSON_True: - out = cJSON_strdup("true"); - break; - case cJSON_Number: - out = print_number(item, 0); - break; - case cJSON_String: - out = print_string(item, 0); - break; - case cJSON_Array: - out = print_array(item, depth, fmt, 0); - break; - case cJSON_Object: - out = print_object(item, depth, fmt, 0); - break; - } - } - return out; +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + { + const char buff[] = "null"; + output = ensure(output_buffer, sizeof(buff)); + if (output == NULL) + { + return false; + } + strncpy((char*)output, buff, output_buffer->length - output_buffer->offset); + return true; + } + case cJSON_False: + { + const char buff[] = "false"; + output = ensure(output_buffer, sizeof(buff)); + if (output == NULL) + { + return false; + } + strncpy((char*)output, buff, output_buffer->length - output_buffer->offset); + return true; + } + case cJSON_True: + { + const char buff[] = "true"; + output = ensure(output_buffer, sizeof(buff)); + if (output == NULL) + { + return false; + } + strncpy((char*)output, buff, output_buffer->length - output_buffer->offset); + return true; + } + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } } /* Build an array from input text. */ -static const char *parse_array(cJSON *item, const char *value) -{ - cJSON *child; - if (*value != '[') { - ep = value; - return 0; - } /* not an array! */ - - item->type = cJSON_Array; - value = skip(value + 1); - if (*value == ']') - return value + 1; /* empty array. */ - - item->child = child = cJSON_New_Item(); - if (!item->child) - return 0; /* memory fail */ - value = skip(parse_value( - child, skip(value))); /* skip any spacing, get the value. */ - if (!value) - return 0; - - while (*value == ',') { - cJSON *new_item; - new_item = cJSON_New_Item(); - if (!new_item) - return 0; /* memory fail */ - child->next = new_item; - new_item->prev = child; - child = new_item; - value = skip(parse_value(child, skip(value + 1))); - if (!value) - return 0; /* memory fail */ - } - - if (*value == ']') - return value + 1; /* end of array */ - ep = value; - return 0; /* malformed. */ +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; } /* Render an array to text */ -static char *print_array(cJSON *item, int depth, int fmt, printbuffer *p) -{ - char **entries; - char *out = 0, *ptr, *ret; - int len = 5; - cJSON *child = item->child; - int numentries = 0, i = 0, fail = 0; - size_t tmplen = 0; - - /* How many entries in the array? */ - while (child) - numentries++, child = child->next; - /* Explicitly handle numentries==0 */ - if (!numentries) { - if (p) - out = ensure(p, 3); - else - out = (char *)cJSON_malloc(3); - if (out) - strcpy(out, "[]"); - return out; - } - - if (p) { - /* Compose the output array. */ - i = p->offset; - ptr = ensure(p, 1); - if (!ptr) - return 0; - *ptr = '['; - p->offset++; - child = item->child; - while (child && !fail) { - print_value(child, depth + 1, fmt, p); - p->offset = update(p); - if (child->next) { - len = fmt ? 2 : 1; - ptr = ensure(p, len + 1); - if (!ptr) - return 0; - *ptr++ = ','; - if (fmt) - *ptr++ = ' '; - *ptr = 0; - p->offset += len; - } - child = child->next; - } - ptr = ensure(p, 2); - if (!ptr) - return 0; - *ptr++ = ']'; - *ptr = 0; - out = (p->buffer) + i; - } else { - /* Allocate an array to hold the values for each */ - entries = (char **)cJSON_malloc(numentries * sizeof(char *)); - if (!entries) - return 0; - memset(entries, 0, numentries * sizeof(char *)); - /* Retrieve all the results: */ - child = item->child; - while (child && !fail) { - ret = print_value(child, depth + 1, fmt, 0); - entries[i++] = ret; - if (ret) - len += strlen(ret) + 2 + (fmt ? 1 : 0); - else - fail = 1; - child = child->next; - } - - /* If we didn't fail, try to malloc the output string */ - if (!fail) - out = (char *)cJSON_malloc(len); - /* If that fails, we fail. */ - if (!out) - fail = 1; - - /* Handle failure. */ - if (fail) { - for (i = 0; i < numentries; i++) - if (entries[i]) - cJSON_free(entries[i]); - cJSON_free(entries); - return 0; - } - - /* Compose the output array. */ - *out = '['; - ptr = out + 1; - *ptr = 0; - for (i = 0; i < numentries; i++) { - tmplen = strlen(entries[i]); - memcpy(ptr, entries[i], tmplen); - ptr += tmplen; - if (i != numentries - 1) { - *ptr++ = ','; - if (fmt) - *ptr++ = ' '; - *ptr = 0; - } - cJSON_free(entries[i]); - } - cJSON_free(entries); - *ptr++ = ']'; - *ptr++ = 0; - } - return out; +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; } /* Build an object from the text. */ -static const char *parse_object(cJSON *item, const char *value) -{ - cJSON *child; - if (*value != '{') { - ep = value; - return 0; - } /* not an object! */ - - item->type = cJSON_Object; - value = skip(value + 1); - if (*value == '}') - return value + 1; /* empty array. */ - - item->child = child = cJSON_New_Item(); - if (!item->child) - return 0; - value = skip(parse_string(child, skip(value))); - if (!value) - return 0; - child->string = child->valuestring; - child->valuestring = 0; - if (*value != ':') { - ep = value; - return 0; - } /* fail! */ - value = skip(parse_value( - child, skip(value + 1))); /* skip any spacing, get the value. */ - if (!value) - return 0; - - while (*value == ',') { - cJSON *new_item; - new_item = cJSON_New_Item(); - if (!(new_item)) - return 0; /* memory fail */ - child->next = new_item; - new_item->prev = child; - child = new_item; - value = skip(parse_string(child, skip(value + 1))); - if (!value) - return 0; - child->string = child->valuestring; - child->valuestring = 0; - if (*value != ':') { - ep = value; - return 0; - } /* fail! */ - value = skip(parse_value( - child, skip(value + - 1))); /* skip any spacing, get the value. */ - if (!value) - return 0; - } - - if (*value == '}') - return value + 1; /* end of array */ - ep = value; - return 0; /* malformed. */ +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* failed to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; } /* Render an object to text. */ -static char *print_object(cJSON *item, int depth, int fmt, printbuffer *p) -{ - char **entries = 0, **names = 0; - char *out = 0, *ptr, *ret, *str; - int len = 7, i = 0, j; - cJSON *child = item->child; - int numentries = 0, fail = 0; - size_t tmplen = 0; - /* Count the number of entries. */ - while (child) - numentries++, child = child->next; - /* Explicitly handle empty object case */ - if (!numentries) { - if (p) - out = ensure(p, fmt ? depth + 4 : 3); - else - out = (char *)cJSON_malloc(fmt ? depth + 4 : 3); - if (!out) - return 0; - ptr = out; - *ptr++ = '{'; - if (fmt) { - *ptr++ = '\n'; - for (i = 0; i < depth - 1; i++) - *ptr++ = '\t'; - } - *ptr++ = '}'; - *ptr++ = 0; - return out; - } - if (p) { - /* Compose the output: */ - i = p->offset; - len = fmt ? 2 : 1; - ptr = ensure(p, len + 1); - if (!ptr) - return 0; - *ptr++ = '{'; - if (fmt) - *ptr++ = '\n'; - *ptr = 0; - p->offset += len; - child = item->child; - depth++; - while (child) { - if (fmt) { - ptr = ensure(p, depth); - if (!ptr) - return 0; - for (j = 0; j < depth; j++) - *ptr++ = '\t'; - p->offset += depth; - } - print_string_ptr(child->string, p); - p->offset = update(p); - - len = fmt ? 2 : 1; - ptr = ensure(p, len); - if (!ptr) - return 0; - *ptr++ = ':'; - if (fmt) - *ptr++ = '\t'; - p->offset += len; - - print_value(child, depth, fmt, p); - p->offset = update(p); - - len = (fmt ? 1 : 0) + (child->next ? 1 : 0); - ptr = ensure(p, len + 1); - if (!ptr) - return 0; - if (child->next) - *ptr++ = ','; - if (fmt) { - *ptr++ = '\n'; - }; - *ptr = 0; - p->offset += len; - child = child->next; - } - ptr = ensure(p, fmt ? (depth + 1) : 2); - if (!ptr) - return 0; - if (fmt) - for (i = 0; i < depth - 1; i++) - *ptr++ = '\t'; - *ptr++ = '}'; - *ptr = 0; - out = (p->buffer) + i; - } else { - /* Allocate space for the names and the objects */ - entries = (char **)cJSON_malloc(numentries * sizeof(char *)); - if (!entries) - return 0; - names = (char **)cJSON_malloc(numentries * sizeof(char *)); - if (!names) { - cJSON_free(entries); - return 0; - } - memset(entries, 0, sizeof(char *) * numentries); - memset(names, 0, sizeof(char *) * numentries); - - /* Collect all the results into our arrays: */ - child = item->child; - depth++; - if (fmt) - len += depth; - while (child) { - names[i] = str = print_string_ptr(child->string, 0); - entries[i++] = ret = print_value(child, depth, fmt, 0); - if (str && ret) - len += strlen(ret) + strlen(str) + 2 + - (fmt ? 2 + depth : 0); - else - fail = 1; - child = child->next; - } - - /* Try to allocate the output string */ - if (!fail) - out = (char *)cJSON_malloc(len); - if (!out) - fail = 1; - - /* Handle failure */ - if (fail) { - for (i = 0; i < numentries; i++) { - if (names[i]) - cJSON_free(names[i]); - if (entries[i]) - cJSON_free(entries[i]); - } - cJSON_free(names); - cJSON_free(entries); - return 0; - } - - /* Compose the output: */ - *out = '{'; - ptr = out + 1; - if (fmt) - *ptr++ = '\n'; - *ptr = 0; - for (i = 0; i < numentries; i++) { - if (fmt) - for (j = 0; j < depth; j++) - *ptr++ = '\t'; - tmplen = strlen(names[i]); - memcpy(ptr, names[i], tmplen); - ptr += tmplen; - *ptr++ = ':'; - if (fmt) - *ptr++ = '\t'; - strcpy(ptr, entries[i]); - ptr += strlen(entries[i]); - if (i != numentries - 1) - *ptr++ = ','; - if (fmt) { - *ptr++ = '\n'; - }; - *ptr = 0; - cJSON_free(names[i]); - cJSON_free(entries[i]); - } - - cJSON_free(names); - cJSON_free(entries); - if (fmt) - for (i = 0; i < depth - 1; i++) - *ptr++ = '\t'; - *ptr++ = '}'; - *ptr++ = 0; - } - return out; +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; } /* Get Array size/item / object item. */ -int cJSON_GetArraySize(cJSON *array) +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) { - cJSON *c = array->child; - int i = 0; - while (c) - i++, c = c->next; - return i; + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; } -cJSON *cJSON_GetArrayItem(cJSON *array, int item) + +static cJSON* get_array_item(const cJSON *array, size_t index) { - cJSON *c = array->child; - while (c && item > 0) - item--, c = c->next; - return c; + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; } -cJSON *cJSON_GetObjectItem(cJSON *object, const char *string) + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) { - cJSON *c = object->child; - while (c && cJSON_strcasecmp(c->string, string)) - c = c->next; - return c; + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + if ((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; } /* Utility for array list handling. */ static void suffix_object(cJSON *prev, cJSON *item) { - prev->next = item; - item->prev = prev; + prev->next = item; + item->prev = prev; } + /* Utility for handling references. */ -static cJSON *create_reference(cJSON *item) +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) { - cJSON *ref = cJSON_New_Item(); - if (!ref) - return 0; - memcpy(ref, item, sizeof(cJSON)); - ref->string = 0; - ref->type |= cJSON_IsReference; - ref->next = ref->prev = 0; - return ref; + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL)) + { + return false; + } + + child = array->child; + + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + } + else + { + /* append to the end */ + while (child->next) + { + child = child->next; + } + suffix_object(child, item); + } + + return true; } /* Add item to array/object. */ -void cJSON_AddItemToArray(cJSON *array, cJSON *item) +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) { - cJSON *c = array->child; - if (!item) - return; - if (!c) { - array->child = item; - } else { - while (c && c->next) - c = c->next; - suffix_object(c, item); - } + add_item_to_array(array, item); } -void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) + +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) { - if (!item) - return; - if (item->string) - cJSON_free(item->string); - item->string = cJSON_strdup(string); - cJSON_AddItemToArray(object, item); + return (void*)string; } -void cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + + +static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) { - if (!item) - return; - if (!(item->type & cJSON_StringIsConst) && item->string) - cJSON_free(item->string); - item->string = (char *)string; - item->type |= cJSON_StringIsConst; - cJSON_AddItemToArray(object, item); + char *new_key = NULL; + int new_type = cJSON_Invalid; + + if ((object == NULL) || (string == NULL) || (item == NULL)) + { + return false; + } + + if (constant_key) + { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } + else + { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if (new_key == NULL) + { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); } -void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) + +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { + return; + } + + add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) { - cJSON_AddItemToArray(array, create_reference(item)); + if ((object == NULL) || (string == NULL)) + { + return; + } + + add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); } -void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, - cJSON *item) + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) { - cJSON_AddItemToObject(object, string, create_reference(item)); + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) + { + return null; + } + + cJSON_Delete(null); + return NULL; } -cJSON *cJSON_DetachItemFromArray(cJSON *array, int which) +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) { - cJSON *c = array->child; - while (c && which > 0) - c = c->next, which--; - if (!c) - return 0; - if (c->prev) { - c->prev->next = c->next; - }; - if (c->next) { - c->next->prev = c->prev; - }; - if (c == array->child) { - array->child = c->next; - }; - c->prev = c->next = 0; - return c; + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) + { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; } -void cJSON_DeleteItemFromArray(cJSON *array, int which) + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) { - cJSON_Delete(cJSON_DetachItemFromArray(array, which)); + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) + { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; } -cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string) + +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) { - int i = 0; - cJSON *c = object->child; - while (c && cJSON_strcasecmp(c->string, string)) - i++, c = c->next; - if (c) - return cJSON_DetachItemFromArray(object, i); - return 0; + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; } -void cJSON_DeleteItemFromObject(cJSON *object, const char *string) + +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) { - cJSON_Delete(cJSON_DetachItemFromObject(object, string)); + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) +{ + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) + { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) +{ + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) +{ + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) + { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) +{ + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) + { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item->prev != NULL) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); } /* Replace array/object items with new ones. */ -void cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) -{ - cJSON *c = array->child; - while (c && which > 0) - c = c->next, which--; - if (!c) { - cJSON_AddItemToArray(array, newitem); - return; - } - newitem->next = c; - newitem->prev = c->prev; - c->prev = newitem; - if (c == array->child) - array->child = newitem; - else - newitem->prev->next = newitem; -} -void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) -{ - cJSON *c = array->child; - while (c && which > 0) - c = c->next, which--; - if (!c) - return; - newitem->next = c->next; - newitem->prev = c->prev; - if (newitem->next) - newitem->next->prev = newitem; - if (c == array->child) - array->child = newitem; - else - newitem->prev->next = newitem; - c->next = c->prev = 0; - cJSON_Delete(c); -} -void cJSON_ReplaceItemInObject(cJSON *object, const char *string, - cJSON *newitem) -{ - int i = 0; - cJSON *c = object->child; - while (c && cJSON_strcasecmp(c->string, string)) - i++, c = c->next; - if (c) { - newitem->string = cJSON_strdup(string); - cJSON_ReplaceItemInArray(object, i, newitem); - } +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0) + { + return; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + add_item_to_array(array, newitem); + return; + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (replacement == NULL) || (item == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (parent->child == item) + { + parent->child = replacement; + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return; + } + + cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, true); } /* Create basic types: */ -cJSON *cJSON_CreateNull(void) +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) { - cJSON *item = cJSON_New_Item(); - if (item) - item->type = cJSON_NULL; - return item; + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; } -cJSON *cJSON_CreateTrue(void) + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) { - cJSON *item = cJSON_New_Item(); - if (item) - item->type = cJSON_True; - return item; + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; } -cJSON *cJSON_CreateFalse(void) + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) { - cJSON *item = cJSON_New_Item(); - if (item) - item->type = cJSON_False; - return item; + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; } -cJSON *cJSON_CreateBool(int b) + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) { - cJSON *item = cJSON_New_Item(); - if (item) - item->type = b ? cJSON_True : cJSON_False; - return item; + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = b ? cJSON_True : cJSON_False; + } + + return item; } -cJSON *cJSON_CreateNumber(double num) + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) { - cJSON *item = cJSON_New_Item(); - if (item) { - item->type = cJSON_Number; - item->valuedouble = num; - item->valueint = (int)num; - } - return item; + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; } -cJSON *cJSON_CreateString(const char *string) + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) + { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) { - cJSON *item = cJSON_New_Item(); - if (item) { - item->type = cJSON_String; - item->valuestring = cJSON_strdup(string); - } - return item; + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; } -cJSON *cJSON_CreateArray(void) + +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) { - cJSON *item = cJSON_New_Item(); - if (item) - item->type = cJSON_Array; - return item; + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; } -cJSON *cJSON_CreateObject(void) + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) { - cJSON *item = cJSON_New_Item(); - if (item) - item->type = cJSON_Object; - return item; + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; } /* Create Arrays: */ -cJSON *cJSON_CreateIntArray(const int *numbers, int count) -{ - int i; - cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); - for (i = 0; a && i < count; i++) { - n = cJSON_CreateNumber(numbers[i]); - if (!i) - a->child = n; - else - suffix_object(p, n); - p = n; - } - return a; -} -cJSON *cJSON_CreateFloatArray(const float *numbers, int count) -{ - int i; - cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); - for (i = 0; a && i < count; i++) { - n = cJSON_CreateNumber(numbers[i]); - if (!i) - a->child = n; - else - suffix_object(p, n); - p = n; - } - return a; -} -cJSON *cJSON_CreateDoubleArray(const double *numbers, int count) -{ - int i; - cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); - for (i = 0; a && i < count; i++) { - n = cJSON_CreateNumber(numbers[i]); - if (!i) - a->child = n; - else - suffix_object(p, n); - p = n; - } - return a; -} -cJSON *cJSON_CreateStringArray(const char **strings, int count) -{ - int i; - cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); - for (i = 0; a && i < count; i++) { - n = cJSON_CreateString(strings[i]); - if (!i) - a->child = n; - else - suffix_object(p, n); - p = n; - } - return a; +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0;a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + return a; } /* Duplication */ -cJSON *cJSON_Duplicate(cJSON *item, int recurse) -{ - cJSON *newitem, *cptr, *nptr = 0, *newchild; - /* Bail on bad ptr */ - if (!item) - return 0; - /* Create new item */ - newitem = cJSON_New_Item(); - if (!newitem) - return 0; - /* Copy over all vars */ - newitem->type = item->type & (~cJSON_IsReference), - newitem->valueint = item->valueint, - newitem->valuedouble = item->valuedouble; - if (item->valuestring) { - newitem->valuestring = cJSON_strdup(item->valuestring); - if (!newitem->valuestring) { - cJSON_Delete(newitem); - return 0; - } - } - if (item->string) { - newitem->string = cJSON_strdup(item->string); - if (!newitem->string) { - cJSON_Delete(newitem); - return 0; - } - } - /* If non-recursive, then we're done! */ - if (!recurse) - return newitem; - /* Walk the ->next chain for the child. */ - cptr = item->child; - while (cptr) { - newchild = cJSON_Duplicate(cptr, - 1); /* Duplicate (with recurse) each - item in the ->next chain */ - if (!newchild) { - cJSON_Delete(newitem); - return 0; - } - if (nptr) { - nptr->next = newchild, newchild->prev = nptr; - nptr = newchild; - } /* If newitem->child already set, then crosswire ->prev and - ->next and move on */ - else { - newitem->child = newchild; - nptr = newchild; - } /* Set newitem->child and move to it */ - cptr = cptr->next; - } - return newitem; -} - -void cJSON_Minify(char *json) -{ - char *into = json; - while (*json) { - if (*json == ' ') - json++; - else if (*json == '\t') - json++; /* Whitespace characters. */ - else if (*json == '\r') - json++; - else if (*json == '\n') - json++; - else if (*json == '/' && json[1] == '/') - while (*json && *json != '\n') - json++; /* double-slash comments, to end of - line. */ - else if (*json == '/' && json[1] == '*') { - while (*json && !(*json == '*' && json[1] == '/')) - json++; - json += 2; - } /* multiline comments. */ - else if (*json == '\"') { - *into++ = *json++; - while (*json && *json != '\"') { - if (*json == '\\') - *into++ = *json++; - *into++ = *json++; - } - *into++ = *json++; - } /* string literals, which are \" sensitive. */ - else - *into++ = *json++; /* All other characters. */ - } - *into = 0; /* and null-terminate. */ -} - -/*lint -restore */ \ No newline at end of file +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +static void skip_oneline_comment(char **input) +{ + *input += static_strlen("//"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if ((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; + } + } +} + +static void skip_multiline_comment(char **input) +{ + *input += static_strlen("/*"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if (((*input)[0] == '*') && ((*input)[1] == '/')) + { + *input += static_strlen("*/"); + return; + } + } +} + +static void minify_string(char **input, char **output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + + for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; + + if ((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); + } + } +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + char *into = json; + + if (json == NULL) + { + return; + } + + while (json[0] != '\0') + { + switch (json[0]) + { + case ' ': + case '\t': + case '\r': + case '\n': + json++; + break; + + case '/': + if (json[1] == '/') + { + skip_oneline_comment(&json); + } + else if (json[1] == '*') + { + skip_multiline_comment(&json); + } else { + json++; + } + break; + + case '\"': + minify_string(&json, (char**)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (a->valuedouble == b->valuedouble) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); +} diff --git a/ext/cjson/cJSON.h b/ext/cjson/cJSON.h index 117351dd9ead..42c1cc617b18 100644 --- a/ext/cjson/cJSON.h +++ b/ext/cjson/cJSON.h @@ -1,187 +1,288 @@ -/* - Copyright (c) 2009 Dave Gamble - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -#ifndef cJSON__h -#define cJSON__h - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/* cJSON Types: */ -#define cJSON_False 0 -#define cJSON_True 1 -#define cJSON_NULL 2 -#define cJSON_Number 3 -#define cJSON_String 4 -#define cJSON_Array 5 -#define cJSON_Object 6 - -#define cJSON_IsReference 256 -#define cJSON_StringIsConst 512 - -/* The cJSON structure: */ -typedef struct cJSON { - struct cJSON *next, *prev; /* next/prev allow you to walk array/object - chains. Alternatively, use - GetArraySize/GetArrayItem/GetObjectItem */ - struct cJSON *child; /* An array or object item will have a child - pointer pointing to a chain of the items in the - array/object. */ - - int type; /* The type of the item, as above. */ - - char *valuestring; /* The item's string, if type==cJSON_String */ - int valueint; /* The item's number, if type==cJSON_Number */ - double valuedouble; /* The item's number, if type==cJSON_Number */ - - char *string; /* The item's name string, if this item is the child of, - or is in the list of subitems of an object. */ -} cJSON; - -typedef struct cJSON_Hooks { - void *(*malloc_fn)(size_t sz); - void (*free_fn)(void *ptr); -} cJSON_Hooks; - -/* Supply malloc, realloc and free functions to cJSON */ -extern void cJSON_InitHooks(cJSON_Hooks *hooks); - - -/* Supply a block of JSON, and this returns a cJSON object you can interrogate. - * Call cJSON_Delete when finished. */ -extern cJSON *cJSON_Parse(const char *value); -/* Render a cJSON entity to text for transfer/storage. Free the char* when - * finished. */ -extern char *cJSON_Print(cJSON *item); -/* Render a cJSON entity to text for transfer/storage without any formatting. - * Free the char* when finished. */ -extern char *cJSON_PrintUnformatted(cJSON *item); -/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess - * at the final size. guessing well reduces reallocation. fmt=0 gives - * unformatted, =1 gives formatted */ -extern char *cJSON_PrintBuffered(cJSON *item, int prebuffer, int fmt); -/* Delete a cJSON entity and all subentities. */ -extern void cJSON_Delete(cJSON *c); - -/* Returns the number of items in an array (or object). */ -extern int cJSON_GetArraySize(cJSON *array); -/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. - */ -extern cJSON *cJSON_GetArrayItem(cJSON *array, int item); -/* Get item "string" from object. Case insensitive. */ -extern cJSON *cJSON_GetObjectItem(cJSON *object, const char *string); - -/* For analysing failed parses. This returns a pointer to the parse error. - * You'll probably need to look a few chars back to make sense of it. Defined - * when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ -extern const char *cJSON_GetErrorPtr(void); - -/* These calls create a cJSON item of the appropriate type. */ -extern cJSON *cJSON_CreateNull(void); -extern cJSON *cJSON_CreateTrue(void); -extern cJSON *cJSON_CreateFalse(void); -extern cJSON *cJSON_CreateBool(int b); -extern cJSON *cJSON_CreateNumber(double num); -extern cJSON *cJSON_CreateString(const char *string); -extern cJSON *cJSON_CreateArray(void); -extern cJSON *cJSON_CreateObject(void); - -/* These utilities create an Array of count items. */ -extern cJSON *cJSON_CreateIntArray(const int *numbers, int count); -extern cJSON *cJSON_CreateFloatArray(const float *numbers, int count); -extern cJSON *cJSON_CreateDoubleArray(const double *numbers, int count); -extern cJSON *cJSON_CreateStringArray(const char **strings, int count); - -/* Append item to the specified array/object. */ -extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); -extern void cJSON_AddItemToObject(cJSON *object, const char *string, - cJSON *item); -extern void cJSON_AddItemToObjectCS( - cJSON *object, const char *string, - cJSON *item); /* Use this when string is definitely const (i.e. a - literal, or as good as), and will definitely survive - the cJSON object */ -/* Append reference to item to the specified array/object. Use this when you - * want to add an existing cJSON to a new cJSON, but don't want to corrupt your - * existing cJSON. */ -extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); -extern void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, - cJSON *item); - -/* Remove/Detatch items from Arrays/Objects. */ -extern cJSON *cJSON_DetachItemFromArray(cJSON *array, int which); -extern void cJSON_DeleteItemFromArray(cJSON *array, int which); -extern cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string); -extern void cJSON_DeleteItemFromObject(cJSON *object, const char *string); - -/* Update array items. */ -extern void cJSON_InsertItemInArray( - cJSON *array, int which, - cJSON *newitem); /* Shifts pre-existing items to the right. */ -extern void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); -extern void cJSON_ReplaceItemInObject(cJSON *object, const char *string, - cJSON *newitem); - -/* Duplicate a cJSON item */ -extern cJSON *cJSON_Duplicate(cJSON *item, int recurse); -/* Duplicate will create a new, identical cJSON item to the one you pass, in new -memory that will need to be released. With recurse!=0, it will duplicate any -children connected to the item. The item->next and ->prev pointers are always -zero on return from Duplicate. */ - -/* ParseWithOpts allows you to require (and check) that the JSON is null - * terminated, and to retrieve the pointer to the final byte parsed. */ -extern cJSON *cJSON_ParseWithOpts(const char *value, - const char **return_parse_end, - int require_null_terminated); - -extern void cJSON_Minify(char *json); - -/* Macros for creating things quickly. */ -#define cJSON_AddNullToObject(object, name) \ - cJSON_AddItemToObject(object, name, cJSON_CreateNull()) -#define cJSON_AddTrueToObject(object, name) \ - cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) -#define cJSON_AddFalseToObject(object, name) \ - cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) -#define cJSON_AddBoolToObject(object, name, b) \ - cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) -#define cJSON_AddNumberToObject(object, name, n) \ - cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) -#define cJSON_AddStringToObject(object, name, s) \ - cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) - -/* When assigning an integer value, it needs to be propagated to valuedouble - * too. */ -#define cJSON_SetIntValue(object, val) \ - ((object) ? (object)->valueint = (object)->valuedouble = (val) : (val)) -#define cJSON_SetNumberValue(object, val) \ - ((object) ? (object)->valueint = (object)->valuedouble = (val) : (val)) - -#ifdef __cplusplus -} -#endif - -#endif \ No newline at end of file +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 12 + +#include + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON_Hooks +{ + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void *(CJSON_CDECL *malloc_fn)(size_t sz); + void (CJSON_CDECL *free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* Check if the item is a string and return its valuestring */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +/* Create an object/array that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); + +/* These utilities create an Array of count items. + * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detach items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will + * need to be released. With recurse!=0, it will duplicate any children connected to the item. + * The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + +/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. + * The input pointer json cannot point to a read-only address area, such as a string constant, + * but should point to a readable and writable adress area. */ +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/at_cmd.h b/include/at_cmd.h index a849fda52645..6e2436301e07 100644 --- a/include/at_cmd.h +++ b/include/at_cmd.h @@ -22,6 +22,7 @@ extern "C" { #endif #include +#include /** * @brief AT command return codes diff --git a/include/at_notif.h b/include/at_notif.h new file mode 100644 index 000000000000..9597adb53d49 --- /dev/null +++ b/include/at_notif.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +#ifndef AT_NOTIF_H_ +#define AT_NOTIF_H_ + +/** + * @file at_notif.h + * + * @defgroup at_notif AT command notification manager + * + * @{ + * + * @brief Public APIs for the AT command notification manager. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * @typedefs at_notif_handler_t + * + * Because this driver let multiple threads share the same socket, it must make + * sure that the correct thread gets the correct data returned from the AT + * interface. Notifications will be dispatched to handlers registered using + * the @ref at_notif_register_handler() function. Handlers can be de-registered + * using the @ref at_notif_deregister_handler() function. + * + * @param context Pointer to context provided by the module which has + * registered the handler. + * @param response Null terminated string containing the modem message + * + */ +typedef void (*at_notif_handler_t)(void *context, char *response); + +/**@brief Initialize AT command notification manager. + * + * @return Zero on success, non-zero otherwise. + */ +int at_notif_init(void); + +/** + * @brief Function to register AT command notification handler + * + * @note If the same combination of context and handler exists in the memory, + * then the request will be ignored and command execution will be + * regarded as finished successfully. + * + * @param context Pointer to context provided by the module which has + * registered the handler. + * @param handler Pointer to a received notification handler function of type + * @ref at_notif_handler_t. + * + * @retval 0 If command execution was successful. + * @retval -ENOBUFS If memory cannot be allocated. + * @retval -EINVAL If handler is a NULL pointer. + */ +int at_notif_register_handler(void *context, at_notif_handler_t handler); + +/** + * @brief Function to de-register AT command notification handler + * + * @param context Pointer to context provided by the module which has + * registered the handler. + * @param handler Pointer to a received notification handler function of type + * @ref at_notif_handler_t. + * + * @retval 0 If command execution was successful. + * @retval -ENXIO If the combination of context and handler cannot be + * found. + * @retval -EINVAL If handler is a NULL pointer. + */ +int at_notif_deregister_handler(void *context, at_notif_handler_t handler); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* AT_NOTIF_H_ */ diff --git a/include/at_notif.rst b/include/at_notif.rst new file mode 100644 index 000000000000..6ff4ac29edfc --- /dev/null +++ b/include/at_notif.rst @@ -0,0 +1,19 @@ +.. _at_notif_readme: + +AT command notifications +######################## + +AT command notifications can be dispatched to registered modules. +Modules can register a callback function to receive AT command notifications as raw string. +Multiple instances, which can be identified by pointers to contexts, are also supported. +Modules can de-register the callback function to stop receiving notifications. + +API documentation +***************** + +| Header file: :file:`include/at_notif.h` +| Source file: :file:`lib/at_notif/at_notif.c` + +.. doxygengroup:: at_notif + :project: nrf + :members: diff --git a/include/bl_crypto.h b/include/bl_crypto.h index b1e6634c6c3f..77fda8c8d926 100644 --- a/include/bl_crypto.h +++ b/include/bl_crypto.h @@ -8,7 +8,7 @@ #define BOOTLOADER_CRYPTO_H__ #include -#include +#include /* Placeholder defines. Values should be updated, if no existing errors can be * used instead. */ @@ -30,20 +30,14 @@ typedef u32_t bl_sha256_ctx_t[SHA256_CTX_SIZE/4]; #endif +/* ABI ID for the bl_root_of_trust_verify ABI. */ #define BL_ROT_VERIFY_ABI_ID 0x1001 -#define BL_ROT_VERIFY_ABI_FLAGS 2 -#define BL_ROT_VERIFY_ABI_VER 1 -#define BL_ROT_VERIFY_ABI_MAX_VER 0xFF +/* ABI ID for the bl_sha256_* ABI set. */ #define BL_SHA256_ABI_ID 0x1002 -#define BL_SHA256_ABI_FLAGS 0 -#define BL_SHA256_ABI_VER 1 -#define BL_SHA256_ABI_MAX_VER 0xFF +/* ABI ID for the bl_secp256r1_validate ABI. */ #define BL_SECP256R1_ABI_ID 0x1003 -#define BL_SECP256R1_ABI_FLAGS 1 -#define BL_SECP256R1_ABI_VER 1 -#define BL_SECP256R1_ABI_MAX_VER 0xFF /** * @brief Initialize bootloader crypto module. @@ -161,14 +155,14 @@ EXT_ABI_FUNCTION(int, bl_secp256r1_validate, const u8_t *hash, const u8_t *public_key); struct bl_rot_verify_abi { - struct fw_abi_info header; + struct fw_info_abi header; struct { bl_root_of_trust_verify_t bl_root_of_trust_verify; } abi; }; struct bl_sha256_abi { - struct fw_abi_info header; + struct fw_info_abi header; struct { bl_sha256_init_t bl_sha256_init; bl_sha256_update_t bl_sha256_update; @@ -179,7 +173,7 @@ struct bl_sha256_abi { }; struct bl_secp256r1_abi { - struct fw_abi_info header; + struct fw_info_abi header; struct { bl_secp256r1_validate_t bl_secp256r1_validate; } abi; diff --git a/include/bl_validation.h b/include/bl_validation.h new file mode 100644 index 000000000000..c4202c820225 --- /dev/null +++ b/include/bl_validation.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +#ifndef BL_VALIDATION_H__ +#define BL_VALIDATION_H__ + +/* + * The FW package will consist of (firmware | (padding) | validation_info), + * where the firmware contains the firmware_info at a predefined location. The + * padding is present if the validation_info needs alignment. The + * validation_info is not directly referenced from the firmware_info since the + * validation_info doesn't actually have to be placed after the firmware. + */ + +#include +#include + +bool bl_validate_firmware_local(u32_t fw_address, + const struct fw_info *fwinfo); + +#endif /* BL_VALIDATION_H__ */ diff --git a/include/bluetooth/mesh.rst b/include/bluetooth/mesh.rst new file mode 100644 index 000000000000..dbbe33a85df4 --- /dev/null +++ b/include/bluetooth/mesh.rst @@ -0,0 +1,17 @@ +.. _bt_mesh: + +Bluetooth Mesh Profile +####################### + +The nRF Connect SDK has experimental support for the Bluetooth Mesh +Specification through the Zephyr :ref:`zephyr:bluetooth_mesh` implementation. + +Nordic Semiconductor additionally provides some modules to aid in the +development of Bluetooth Mesh-based applications: + +.. toctree:: + :maxdepth: 1 + + mesh/models.rst + mesh/properties.rst + diff --git a/include/bluetooth/mesh/gen_battery.h b/include/bluetooth/mesh/gen_battery.h new file mode 100644 index 000000000000..1ab6ead350de --- /dev/null +++ b/include/bluetooth/mesh/gen_battery.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +/** + * @file + * @defgroup bt_mesh_battery Generic Battery models + * @{ + * @brief API for the Generic Battery models. + */ + +#ifndef BT_MESH_GEN_BATTERY_H__ +#define BT_MESH_GEN_BATTERY_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @cond INTERNAL_HIDDEN */ +#define BT_MESH_BATTERY_OP_GET BT_MESH_MODEL_OP_2(0x82, 0x23) +#define BT_MESH_BATTERY_OP_STATUS BT_MESH_MODEL_OP_2(0x82, 0x24) + +#define BT_MESH_BATTERY_MSG_LEN_GET 0 +#define BT_MESH_BATTERY_MSG_LEN_STATUS 8 +/** @endcond */ + +/** Unknown battery level. */ +#define BT_MESH_BATTERY_LVL_UNKNOWN 0xff + +/** Longest acceptable charge and discharge time in minutes */ +#define BT_MESH_BATTERY_TIME_MAX 0xfffffe +/** Unknown discharge time */ +#define BT_MESH_BATTERY_TIME_UNKNOWN 0xffffff + +/** Battery presence state. */ +enum bt_mesh_battery_presence { + /** The battery is not present. */ + BT_MESH_BATTERY_PRESENCE_NOT_PRESENT, + /** The battery is present and is removable. */ + BT_MESH_BATTERY_PRESENCE_PRESENT_REMOVABLE, + /** The battery is present and is non-removable. */ + BT_MESH_BATTERY_PRESENCE_PRESENT_NOT_REMOVABLE, + /** The battery presence is unknown. */ + BT_MESH_BATTERY_PRESENCE_UNKNOWN, +}; + +/** Indicator for the charge level of the battery. */ +enum bt_mesh_battery_indicator { + /** The battery charge is Critically Low Level. */ + BT_MESH_BATTERY_INDICATOR_CRITICALLY_LOW, + /** The battery charge is Low Level. */ + BT_MESH_BATTERY_INDICATOR_LOW, + /** The battery charge is Good Level. */ + BT_MESH_BATTERY_INDICATOR_GOOD, + /** The battery charge is unknown. */ + BT_MESH_BATTERY_INDICATOR_UNKNOWN, +}; + +/** Battery charging state. */ +enum bt_mesh_battery_charging { + /** The battery is not chargeable. */ + BT_MESH_BATTERY_CHARGING_NOT_CHARGEABLE, + /** The battery is chargeable and is not charging. */ + BT_MESH_BATTERY_CHARGING_CHARGEABLE_NOT_CHARGING, + /** The battery is chareable and is charging. */ + BT_MESH_BATTERY_CHARGING_CHARGEABLE_CHARGING, + /** The battery charging state is unknown. */ + BT_MESH_BATTERY_CHARGING_UNKNOWN, +}; + +/** Battery service state. */ +enum bt_mesh_battery_service { + /** Reserved for future use. */ + BT_MESH_BATTERY_SERVICE_INVALID, + /** The battery does not require service. */ + BT_MESH_BATTERY_SERVICE_NOT_REQUIRED, + /** The battery requires service. */ + BT_MESH_BATTERY_SERVICE_REQUIRED, + /** The battery servicability is unknown. */ + BT_MESH_BATTERY_SERVICE_UNKNOWN, +}; + +struct bt_mesh_battery_status { + /** + * Current battery level in percent, or + * @ref BT_MESH_BATTERY_LVL_UNKNOWN. + */ + u8_t battery_lvl; + /** Minutes until discharged, or @ref BT_MESH_BATTERY_TIME_UNKNOWN. */ + u32_t discharge_minutes; + /** Minutes until discharged, or @ref BT_MESH_BATTERY_TIME_UNKNOWN. */ + u32_t charge_minutes; + /** Presence state. */ + enum bt_mesh_battery_presence presence; + /** Charge level indicator. */ + enum bt_mesh_battery_indicator indicator; + /** Charging state. */ + enum bt_mesh_battery_charging charging; + /** Service state. */ + enum bt_mesh_battery_service service; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_BATTERY_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/gen_battery.rst b/include/bluetooth/mesh/gen_battery.rst new file mode 100644 index 000000000000..71691a092d93 --- /dev/null +++ b/include/bluetooth/mesh/gen_battery.rst @@ -0,0 +1,96 @@ +.. _bt_mesh_battery_readme: + +Generic Battery models +###################### + +The Generic Battery models allow remote monitoring of the battery status of +a mesh device. + +There are two Generic Battery models: + +- :ref:`bt_mesh_battery_srv_readme` +- :ref:`bt_mesh_battery_cli_readme` + +.. _bt_mesh_battery_srv_readme: + +Generic Battery Server +====================== + +The Generic Battery Server model provides information about the current battery +status of the device. + +States +******* + +**Generic Battery Status**: :cpp:type:`bt_mesh_battery_status` + +The Generic Battery Status is a composite state, containing various information +about the battery state. The battery state can only be changed locally, so a +Generic Battery Client is only able to observe it. + +The user is expected to hold the Generic Battery Status state memory and +provide access to the state through the :cpp:member:`bt_mesh_battery_srv::get` +handler function. All the fields in the Generic Battery Status have special +*unknown* values, which are used by default. + +Extended models +**************** + +None. + +Persistent storage +******************* + +None. + +API documentation +****************** + +| Header file: :file:`include/bluetooth/mesh/gen_battery_srv.h` +| Source file: :file:`subsys/bluetooth/mesh/gen_battery_srv.c` + +.. doxygengroup:: bt_mesh_battery_srv + :project: nrf + :members: + +---- + +.. _bt_mesh_battery_cli_readme: + +Generic Battery Client +====================== + +The Generic Battery Client model can query the state of a Generic Battery +Server model remotely, but does not have any ability to control the Battery +state. + +Extended models +**************** + +None. + +Persistent storage +******************* + +None. + +API documentation +****************** + +| Header file: :file:`include/bluetooth/mesh/gen_battery_cli.h` +| Source file: :file:`subsys/bluetooth/mesh/gen_battery_cli.c` + +.. doxygengroup:: bt_mesh_battery_cli + :project: nrf + :members: + +---- + +Common types +============= + +| Header file: :file:`include/bluetooth/mesh/gen_battery.h` + +.. doxygengroup:: bt_mesh_battery + :project: nrf + :members: diff --git a/include/bluetooth/mesh/gen_battery_cli.h b/include/bluetooth/mesh/gen_battery_cli.h new file mode 100644 index 000000000000..17bd796a14b6 --- /dev/null +++ b/include/bluetooth/mesh/gen_battery_cli.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +/** + * @file + * @defgroup bt_mesh_battery_cli Generic Battery Client model + * @{ + * @brief API for the Generic Battery Client model. + */ + +#ifndef BT_MESH_GEN_BATTERY_CLI_H__ +#define BT_MESH_GEN_BATTERY_CLI_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_battery_cli; + +/** @def BT_MESH_BATTERY_CLI_INIT + * + * @brief Initialization parameters for a @ref bt_mesh_battery_cli instance. + * + * @param[in] _status_handler Optional status message handler. + */ +#define BT_MESH_BATTERY_CLI_INIT(_status_handler) \ + { \ + .status_handler = _status_handler, \ + .pub = {.msg = NET_BUF_SIMPLE(BT_MESH_MODEL_BUF_LEN( \ + BT_MESH_BATTERY_OP_GET, \ + BT_MESH_BATTERY_MSG_LEN_GET)) } \ + } + +/** @def BT_MESH_MODEL_BATTERY_CLI + * + * @brief Generic Battery Client model composition data entry. + * + * @param[in] _cli Pointer to a @ref bt_mesh_battery_cli instance. + */ +#define BT_MESH_MODEL_BATTERY_CLI(_cli) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_BATTERY_CLI, \ + _bt_mesh_battery_cli_op, &(_cli)->pub, \ + BT_MESH_MODEL_USER_DATA(struct bt_mesh_battery_cli, \ + _cli), \ + &_bt_mesh_battery_cli_cb) + +/** + * Generic Battery Client structure. + * + * Should be initialized with the @ref BT_MESH_BATTERY_CLI_INIT macro. + */ +struct bt_mesh_battery_cli { + /** @brief Battery status message handler. + * + * @param[in] cli Client that received the status message. + * @param[in] ctx Context of the incoming message. + * @param[in] status Battery Status of the Generic Battery Server that + * published the message. + */ + void (*const status_handler)( + struct bt_mesh_battery_cli *cli, struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_battery_status *status); + + /** Response context for tracking acknowledged messages. */ + struct bt_mesh_model_ack_ctx ack_ctx; + /** Publish parameters. */ + struct bt_mesh_model_pub pub; + /** Composition data model entry pointer. */ + struct bt_mesh_model *model; +}; + +/** @brief Get the status of the bound srv. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will return, and the response will be passed to the + * @ref bt_mesh_battery_cli::status_handler callback. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[out] rsp Status response buffer, or NULL to keep from blocking. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_battery_cli_get(struct bt_mesh_battery_cli *cli, + struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_battery_status *rsp); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op _bt_mesh_battery_cli_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_battery_cli_cb; +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_BATTERY_CLI_H__ */ + +/* @} */ diff --git a/include/bluetooth/mesh/gen_battery_srv.h b/include/bluetooth/mesh/gen_battery_srv.h new file mode 100644 index 000000000000..cb39595d1c74 --- /dev/null +++ b/include/bluetooth/mesh/gen_battery_srv.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +/** + * @file + * @defgroup bt_mesh_battery_srv Generic Battery Server model + * @ingroup bt_mesh_battery + * @{ + * @brief API for the Generic Battery Server model. + */ + +#ifndef BT_MESH_GEN_BATTERY_SRV_H__ +#define BT_MESH_GEN_BATTERY_SRV_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_battery_srv; + +/** @def BT_MESH_BATTERY_SRV_INIT + * + * @brief Init parameters for a @ref bt_mesh_battery_srv instance. + * + * @param[in] _get_handler Get handler function for the Battery state. + */ +#define BT_MESH_BATTERY_SRV_INIT(_get_handler) \ + { \ + .get = _get_handler, \ + .pub = { \ + .update = _bt_mesh_battery_srv_update_handler, \ + .msg = NET_BUF_SIMPLE(BT_MESH_MODEL_BUF_LEN( \ + BT_MESH_BATTERY_OP_STATUS, \ + BT_MESH_BATTERY_MSG_LEN_STATUS)), \ + }, \ + } + +/** @def BT_MESH_MODEL_BATTERY_SRV + * + * @brief Generic Battery Server model composition data entry. + * + * @param[in] _srv Pointer to a @ref bt_mesh_battery_srv instance. + */ +#define BT_MESH_MODEL_BATTERY_SRV(_srv) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_BATTERY_SRV, \ + _bt_mesh_battery_srv_op, &(_srv)->pub, \ + BT_MESH_MODEL_USER_DATA(struct bt_mesh_battery_srv, \ + _srv), \ + &_bt_mesh_battery_srv_cb) + +/** + * Generic Battery Server instance. Should primarily be initialized with the + * @ref BT_MESH_BATTERY_SRV_INIT macro. + */ +struct bt_mesh_battery_srv { + /** Pointer to the model entry in the composition data. */ + struct bt_mesh_model *model; + /** Publication parameters. */ + struct bt_mesh_model_pub pub; + + /** @brief Get the Battery state. + * + * @note This handler is mandatory. + * + * @param[in] srv Server instance to get the state of. + * @param[in] ctx Message context for the message that triggered the + * change, or NULL if the change is not coming from a message. + * @param[out] rsp Response structure to be filled. All fields are + * initialized to special @em unknown values before calling, so only + * states that have known values need to be filled. + */ + void (*const get)(struct bt_mesh_battery_srv *srv, + struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_battery_status *rsp); +}; + +/** @brief Publish the Generic Battery Server model status. + * + * Asynchronously publishes a Generic Battery status message with the + * configured publish parameters. + * + * @note This API is only used publishing unprompted status messages. Response + * messages for get and set messages are handled internally. + * + * @param[in] srv Server instance to publish on. + * @param[in] ctx Message context to send with, or NULL to send with the + * default publish parameters. + * @param[in] status Current status. + * + * @retval 0 Successfully published a Generic Battery Status message. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + */ +s32_t bt_mesh_battery_srv_pub(struct bt_mesh_battery_srv *srv, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_battery_status *status); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op _bt_mesh_battery_srv_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_battery_srv_cb; +int _bt_mesh_battery_srv_update_handler(struct bt_mesh_model *model); +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_BATTERY_SRV_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/gen_dtt.h b/include/bluetooth/mesh/gen_dtt.h new file mode 100644 index 000000000000..18327acbd97b --- /dev/null +++ b/include/bluetooth/mesh/gen_dtt.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +/** + * @file + * @defgroup bt_mesh_dtt Generic Default Transition Time models + * @{ + * @brief API for the Generic Default Transition Time models. + */ + +#ifndef BT_MESH_GEN_DTT_H__ +#define BT_MESH_GEN_DTT_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @cond INTERNAL_HIDDEN */ +#define BT_MESH_DTT_OP_GET BT_MESH_MODEL_OP_2(0x82, 0x0D) +#define BT_MESH_DTT_OP_SET BT_MESH_MODEL_OP_2(0x82, 0x0E) +#define BT_MESH_DTT_OP_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x0F) +#define BT_MESH_DTT_OP_STATUS BT_MESH_MODEL_OP_2(0x82, 0x10) + +#define BT_MESH_DTT_MSG_LEN_GET 0 +#define BT_MESH_DTT_MSG_LEN_SET 1 +#define BT_MESH_DTT_MSG_LEN_STATUS 1 +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_DTT_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/gen_dtt.rst b/include/bluetooth/mesh/gen_dtt.rst new file mode 100644 index 000000000000..bdc017a7c503 --- /dev/null +++ b/include/bluetooth/mesh/gen_dtt.rst @@ -0,0 +1,115 @@ +.. _bt_mesh_dtt_readme: + +Generic Default Transition Time models +###################################### + +The Generic Default Transition Time (DTT) models are used to control the +transition of any other states on the same element as a DTT Server. +The DTT Client can remotely control the default transition time state +of a server. + +There are two Default Transition Time models: + +- :ref:`bt_mesh_dtt_srv_readme` +- :ref:`bt_mesh_dtt_cli_readme` + +.. _bt_mesh_dtt_srv_readme: + +Generic Default Transition Time Server +====================================== + +The DTT Server provides a common way to specify the state transition time for +other models on the same element. If other generic models on the same element +receive state change commands without transition parameters, they will use the +default transition time specified by the DTT Server model. This way, the DTT +Server can define a consistent transition time for all states on their +elements, without depending on client configurations. + +Configuration +************** + +The Generic DTT Server has one associated configuration option: + +- :option:`CONFIG_BT_MESH_DTT_SRV_PERSISTENT`: Control whether changes to the + Generic Default Transition Time are stored persistently. Note that this + option is only available if :option:`CONFIG_BT_SETTINGS` is enabled. + +States +******* + +**Generic Default Transition Time**: ``s32_t`` + +The Default Transition Time can either be 0, a positive number of milliseconds, +or ``K_FOREVER`` if the transition is undefined. On the air, the transition +time is encoded into a single byte, and loses some of its granularity: + +- Step count: 6 bits (0-0x3e) +- Step resolution: 2 bits + - Step 0: 100 millisecond resolution + - Step 1: 1 second resolution + - Step 2: 10 second resolution + - Step 3: 10 minutes resolution + +The state is encoded with the highest resolution available, and rounded to the +nearest representation. Values lower than 100 milliseconds, but higher than 0 +are encoded as 100 milliseconds. Values higher than the max value of 620 +minutes are encoded as "undefined". + +The DTT Server holds the memory for this state itself, and optionally +notifies the user of any changes through +:cpp:member:`bt_mesh_dtt_srv::update_handler`. If the user changes the +transition time manually, the change should be published using +:cpp:func:`bt_mesh_dtt_srv_pub`. + +Extended models +**************** + +None. + +Persistent storage +******************* + +The Generic Default Transition Time is stored persistently if +:option:`CONFIG_BT_MESH_DTT_SRV_PERSISTENT` is enabled. + + +API documentation +****************** + +| Header file: :file:`include/bluetooth/mesh/gen_dtt_srv.h` +| Source file: :file:`subsys/bluetooth/mesh/gen_dtt_srv.c` + +.. doxygengroup:: bt_mesh_dtt_srv + :project: nrf + :members: + +---- + +.. _bt_mesh_dtt_cli_readme: + +Generic Default Transition Time Client +====================================== + +The DTT Client remotely controls the transition time state of a DTT Server. +This can be used to set up the server's default transition time behavior +when changing states in other models on the same element. + +Extended models +**************** + +None. + +Persistent storage +******************* + +None. + +API documentation +****************** + +| Header file: :file:`include/bluetooth/mesh/gen_dtt_cli.h` +| Source file: :file:`subsys/bluetooth/mesh/gen_dtt_cli.c` + +.. doxygengroup:: bt_mesh_dtt_cli + :project: nrf + :members: diff --git a/include/bluetooth/mesh/gen_dtt_cli.h b/include/bluetooth/mesh/gen_dtt_cli.h new file mode 100644 index 000000000000..ae414f830905 --- /dev/null +++ b/include/bluetooth/mesh/gen_dtt_cli.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +/** + * + * @file + * @defgroup bt_mesh_dtt_cli Generic Default Transition Time Client API + * @{ + * @brief API for the Generic Default Transition Time (DTT) Client. + */ + +#ifndef BT_MESH_GEN_DTT_CLI_H__ +#define BT_MESH_GEN_DTT_CLI_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_dtt_cli; + +/** @def BT_MESH_DTT_CLI_INIT + * + * @brief Initialization parameters for the @ref bt_mesh_dtt_cli. + * + * @param[in] _status_handler Optional status message handler. + * @sa bt_mesh_dtt_cli::status_handler + */ +#define BT_MESH_DTT_CLI_INIT(_status_handler) \ + { \ + .pub = { .msg = NET_BUF_SIMPLE(BT_MESH_MODEL_BUF_LEN( \ + BT_MESH_DTT_OP_SET, \ + BT_MESH_DTT_MSG_LEN_SET)) }, \ + .status_handler = _status_handler, \ + } + +/** @def BT_MESH_MODEL_DTT_CLI + * + * @brief Generic DTT Client model composition data entry. + * + * @param[in] _cli Pointer to a @ref bt_mesh_dtt_cli instance. + */ +#define BT_MESH_MODEL_DTT_CLI(_cli) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_CLI, \ + _bt_mesh_dtt_cli_op, &(_cli)->pub, \ + BT_MESH_MODEL_USER_DATA(struct bt_mesh_dtt_cli, \ + _cli), \ + &_bt_mesh_dtt_cli_cb) + +/** + * Generic DTT client structure. + * + * Should be initialized using the @ref BT_MESH_DTT_CLI_INIT macro. + */ +struct bt_mesh_dtt_cli { + /** @brief Default Transition Time status message handler. + * + * @param[in] cli Client that received the message. + * @param[in] ctx Message context. + * @param[in] transition_time Transition time presented in the message. + */ + void (*const status_handler)(struct bt_mesh_dtt_cli *cli, + struct bt_mesh_msg_ctx *ctx, + s32_t transition_time); + + /** Response context for tracking acknowledged messages. */ + struct bt_mesh_model_ack_ctx ack_ctx; + /** Model publish parameters. */ + struct bt_mesh_model_pub pub; + /** Composition data model entry pointer. */ + struct bt_mesh_model *model; +}; + +/** @brief Get the Default Transition Time of the server. + * + * This call is blocking if the @p rsp_transition_time buffer is non-NULL. + * Otherwise, this function will not request a response from the server, and + * return immediately. + * + * @param[in] cli Client making the request. + * @param[in] ctx Message context to use for sending, or NULL to publish with + * the configured parameters. + * @param[out] rsp_transition_time Pointer to a response buffer. Cannot be + * NULL. Note that the response is a signed value, that can be K_FOREVER if the + * current state is unknown or too large to represent. + * + * @retval 0 Successfully retrieved the status of the bound srv. + * @retval -EALREADY A blocking operation is already in progress in this model. + * @retval -EAGAIN The request timed out. + */ +int bt_mesh_dtt_get(struct bt_mesh_dtt_cli *cli, struct bt_mesh_msg_ctx *ctx, + s32_t *rsp_transition_time); + +/** @brief Set the Default Transition Time of the server. + * + * This call is blocking if the @p rsp_transition_time buffer is non-NULL. + * Otherwise, this function will not request a response from the server, and + * return immediately. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context to use for sending, or NULL to publish with + * the configured parameters. + * @param[in] transition_time Transition time to set (in milliseconds). Must be + * less than @ref BT_MESH_MODEL_TRANSITION_TIME_MAX_MS. + * @param[out] rsp_transition_time Response buffer, or NULL to send an + * unacknowledged message. Note that the response is a signed value, that can + * be K_FOREVER if the current state is unknown or too large to represent. + * + * @retval 0 Successfully sent the message. If the message is acknowledged, + * the response buffer has been set according to the srv's response. + * @retval -EINVAL The given transition time is invalid. + * @retval -EALREADY A blocking operation is already in progress in this model. + * @retval -EAGAIN The request timed out. + */ +int bt_mesh_dtt_set(struct bt_mesh_dtt_cli *cli, struct bt_mesh_msg_ctx *ctx, + u32_t transition_time, s32_t *rsp_transition_time); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op _bt_mesh_dtt_cli_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_dtt_cli_cb; +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_DTT_CLI_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/gen_dtt_srv.h b/include/bluetooth/mesh/gen_dtt_srv.h new file mode 100644 index 000000000000..fc9739e62f0f --- /dev/null +++ b/include/bluetooth/mesh/gen_dtt_srv.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +/** + * @file + * @defgroup bt_mesh_dtt_srv Generic Default Transition Time Server + * model + * @{ + * @brief API for the Generic Default Transition Time (DTT) Server model. + */ + +#ifndef BT_MESH_GEN_DTT_SRV_H__ +#define BT_MESH_GEN_DTT_SRV_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_dtt_srv; + +/** @def BT_MESH_DTT_SRV_INIT + * + * @brief Initialization parameters for @ref bt_mesh_dtt_srv. + * + * @param[in] _update Update handler called on every change to the transition + * time in this server. + */ +#define BT_MESH_DTT_SRV_INIT(_update) \ + { \ + .update = _update, \ + .pub = {.update = _bt_mesh_dtt_srv_update_handler, \ + .msg = NET_BUF_SIMPLE(BT_MESH_MODEL_BUF_LEN( \ + BT_MESH_DTT_OP_STATUS, \ + BT_MESH_DTT_MSG_LEN_STATUS)) } \ + } + +/** @def BT_MESH_MODEL_DTT_SRV + * + * @brief Generic Default Transition Time Server model composition data entry. + * + * @param[in] _srv Pointer to a @ref bt_mesh_dtt_srv instance. + */ +#define BT_MESH_MODEL_DTT_SRV(_srv) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_SRV, \ + _bt_mesh_dtt_srv_op, &(_srv)->pub, \ + BT_MESH_MODEL_USER_DATA(struct bt_mesh_dtt_srv, \ + _srv), \ + &_bt_mesh_dtt_srv_cb) + +/** Generic Default Transition Time server instance. */ +struct bt_mesh_dtt_srv { + /** Current transition time in milliseconds */ + u32_t transition_time; + + /** @brief Update handler for the transition time state. + * + * Called every time the transition time is updated. + * + * @param[in] srv Server instance that was updated. + * @param[in] ctx Context of the set message that caused the update, or + * NULL if the update was not a result of a set message. + * @param[in] old_transition_time The transition time prior to the + * update. + * @param[in] new_transition_time The new transition time after the + * update. + */ + void (*const update)(struct bt_mesh_dtt_srv *srv, + struct bt_mesh_msg_ctx *ctx, + u32_t old_transition_time, + u32_t new_transition_time); + /** Composition data Model entry pointer. */ + struct bt_mesh_model *model; + /** Model publish parameters. */ + struct bt_mesh_model_pub pub; +}; + +/** @brief Set the Default Transition Time of the DTT server. + * + * If an update handler is set, it'll be called with the updated transition + * time. If publication is configured, the change will cause the server to + * publish. + * + * @param[in] srv Server to set the DTT of. + * @param[in] transition_time New Default Transition Time. + */ +void bt_mesh_dtt_srv_set(struct bt_mesh_dtt_srv *srv, u32_t transition_time); + +/** @brief Publish the current transition time. + * + * @param[in] srv Server instance to publish with. + * @param[in] ctx Message context to publish with, or NULL to publish with the + * configured parameters. + * + * @retval 0 Successfully published the current transition time. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + */ +int bt_mesh_dtt_srv_pub(struct bt_mesh_dtt_srv *srv, + struct bt_mesh_msg_ctx *ctx); + +/** @brief Find the Generic DTT server in a given element. + * + * @param[in] elem Element to find the DTT server in. + * + * @return A pointer to the DTT server instance, or NULL if no instance is + * found. + */ +static inline struct bt_mesh_dtt_srv * +bt_mesh_dtt_srv_get(const struct bt_mesh_elem *elem) +{ + struct bt_mesh_model *mod = bt_mesh_model_find( + elem, BT_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_SRV); + + return (struct bt_mesh_dtt_srv *)(mod ? mod->user_data : NULL); +} + +/** @brief Get the default transition parameters for the given model. + * + * @param[in] mod Model to get the DTT for. + * @param[out] transition Transition buffer. + * + * @return Whether the transition was set. + */ +static inline bool +bt_mesh_dtt_srv_transition_get(struct bt_mesh_model *mod, + struct bt_mesh_model_transition *transition) +{ + struct bt_mesh_dtt_srv *srv = + bt_mesh_dtt_srv_get(bt_mesh_model_elem(mod)); + + transition->time = srv ? srv->transition_time : 0; + transition->delay = 0; + return (transition->time != 0); +} + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op _bt_mesh_dtt_srv_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_dtt_srv_cb; +int _bt_mesh_dtt_srv_update_handler(struct bt_mesh_model *model); +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_DTT_SRV_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/gen_loc.h b/include/bluetooth/mesh/gen_loc.h new file mode 100644 index 000000000000..c6ce868180f6 --- /dev/null +++ b/include/bluetooth/mesh/gen_loc.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +/** + * @file + * @defgroup bt_mesh_loc Generic Location models + * @{ + * @brief API for the Generic Location models. + */ + +#ifndef BT_MESH_GEN_LOC_H__ +#define BT_MESH_GEN_LOC_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @cond INTERNAL_HIDDEN */ +#define BT_MESH_LOC_OP_GLOBAL_GET BT_MESH_MODEL_OP_2(0x82, 0x25) +#define BT_MESH_LOC_OP_GLOBAL_STATUS BT_MESH_MODEL_OP_1(0x40) +#define BT_MESH_LOC_OP_GLOBAL_SET BT_MESH_MODEL_OP_1(0x41) +#define BT_MESH_LOC_OP_GLOBAL_SET_UNACK BT_MESH_MODEL_OP_1(0x42) +#define BT_MESH_LOC_OP_LOCAL_GET BT_MESH_MODEL_OP_2(0x82, 0x26) +#define BT_MESH_LOC_OP_LOCAL_STATUS BT_MESH_MODEL_OP_2(0x82, 0x27) +#define BT_MESH_LOC_OP_LOCAL_SET BT_MESH_MODEL_OP_2(0x82, 0x28) +#define BT_MESH_LOC_OP_LOCAL_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x29) + +#define BT_MESH_LOC_MSG_LEN_GLOBAL_GET 0 +#define BT_MESH_LOC_MSG_LEN_GLOBAL_STATUS 10 +#define BT_MESH_LOC_MSG_LEN_GLOBAL_SET 10 +#define BT_MESH_LOC_MSG_LEN_LOCAL_GET 0 +#define BT_MESH_LOC_MSG_LEN_LOCAL_STATUS 9 +#define BT_MESH_LOC_MSG_LEN_LOCAL_SET 9 +/** @endcond */ + +/** + * @defgroup bt_mesh_loc_altitude_defines Defines for altitude. + * Boundaries and special values for the altitude. + * @{ + */ +/** Highest altitude that can be represented */ +#define BT_MESH_LOC_ALTITUDE_MAX 32765 +/** Altitude is unknown or not configured. */ +#define BT_MESH_LOC_ALTITUDE_UNKNOWN 0x7fff +/** Altitude is out of bounds. */ +#define BT_MESH_LOC_ALTITUDE_TOO_LARGE 0x7ffe +/** @} */ + +/** + * @defgroup bt_mesh_loc_floor_number_defines Defines for floor number. + * Boundaries and special values for the local floor number. + * @{ + */ +/** Lowest floor number that can be represented. */ +#define BT_MESH_LOC_FLOOR_NUMBER_MIN (-20) +/** Highest floor number that can be represented. */ +#define BT_MESH_LOC_FLOOR_NUMBER_MAX 232 +/** Ground floor (where the ground floor is referred to as floor 0) */ +#define BT_MESH_LOC_FLOOR_NUMBER_GROUND_FLOOR_0 0xfc +/** Ground floor (where the ground floor is referred to as floor 1) */ +#define BT_MESH_LOC_FLOOR_NUMBER_GROUND_FLOOR_1 0xfd +/** Unknown or unconfigured floor number. */ +#define BT_MESH_LOC_FLOOR_NUMBER_UNKNOWN 0xff +/** @} */ + +/** Global location parameters. */ +struct bt_mesh_loc_global { + /** Global WGS84 North coordinate in degrees. */ + double latitude; + /** Global WGS84 East coordinate in degrees. */ + double longitude; + /** + * Global altitude above the WGS84 datum in meters. + * @sa bt_mesh_loc_altitude_defines + */ + s16_t altitude; +}; + +/** Local location parameters. */ +struct bt_mesh_loc_local { + /** Local north position in decimeters. */ + s16_t north; + /** Local east position in decimeters. */ + s16_t east; + /** Local altitude in decimeters. @sa bt_mesh_loc_altitude_defines */ + s16_t altitude; + /** Floor number. @sa bt_mesh_loc_floor_number_defines */ + s16_t floor_number; + /** Whether the device is movable. */ + bool is_mobile; + /** Time since the previous position update, @ref K_FOREVER. */ + s32_t time_delta; + /** Precision of the location in millimeters. */ + u32_t precision_mm; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_LOC_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/gen_loc.rst b/include/bluetooth/mesh/gen_loc.rst new file mode 100644 index 000000000000..f0854c15e1dd --- /dev/null +++ b/include/bluetooth/mesh/gen_loc.rst @@ -0,0 +1,144 @@ +.. _bt_mesh_loc_readme: + +Generic Location models +####################### + +The Generic Location models allow remote monitoring of the location status of +a mesh device. + +There are two Generic Location models: + +- :ref:`bt_mesh_loc_srv_readme` +- :ref:`bt_mesh_loc_cli_readme` + +.. _bt_mesh_loc_srv_readme: + +Generic Location Server +======================= + +The Generic Location Server provides location information about the device. +The location data is split up into two separate states: Global and Local. +The Global Location represents the device location in the world, while the +Local Location represents the device location relative to a local coordinate +system, for instance inside a building. + +The Generic Location Server adds two model instances in the composition data: + +- The Generic Location Server +- The Generic Location Setup Server + +The two model instances share the states of the Generic Location Server, but +accept different messages. This allows fine-grained control of the access +rights for the location states, as the two model instances can be bound to +different application keys. + +The Generic Location Server allows observation of the location states, as it +only exposes get-messages for the location states. This is typically the +user facing model instance, as the device location shouldn't normally be +configurable by the users. + +The Generic Location Setup Server allows configuration of the location states +by exposing set-messages for the location states. This model instance can be +used to configure the location information of the device, for instance during +deployment. + +If the device is capable of determining its own location information, the +Generic Location Setup Server is redundant, and can be deactivated by +removing all bindings it has to any application keys. + +.. note:: + + The Generic Location Server does not store any of its states persistently, + so if the Generic Location Server is meant to get its location configured + during setup, this must be stored by the application. + +States +******* + +**Global Location**: :cpp:type:`bt_mesh_loc_global` + +The Global Location state is a composite state representing a global location +as a WGS84 coordinate point. The latitude and longitude (in degrees) are +represented as a ``double``, and are each encoded as a signed 32 bit integer. +The altitude is represented as a signed 16 bit integer, measured in meters. + +The memory for the Global Location state is owned by the user, and should be +exposed to the model through the callbacks in a +:cpp:type:`bt_mesh_loc_srv_handlers` structure. + +**Local Location**: :cpp:type:`bt_mesh_loc_local` + +The Local Location state represents the device's location within a locally +defined coordinate system, like the inside of a building. It contains +parameters for a three dimensional device location measured in decimeters, as +well as a floor number and parameters to determine the precision of the +location parameters. + +The memory for the Local Location state is owned by the user, and should be +exposed to the model through the callbacks in a +:cpp:type:`bt_mesh_loc_srv_handlers` structure. + +Extended models +**************** + +None. + +Persistent storage +******************* + +None. + +API documentation +****************** + +| Header file: :file:`include/bluetooth/mesh/gen_loc_srv.h` +| Source file: :file:`subsys/bluetooth/mesh/gen_loc_srv.c` + +.. doxygengroup:: bt_mesh_loc_srv + :project: nrf + :members: + +---- + +.. _bt_mesh_loc_cli_readme: + +Generic Location Client +======================= + +The Generic Location Client model can get and set the state of a Generic +Location Server model remotely. + +Contrary to the Server model, the Client only creates a single model instance +in the mesh composition data. The Generic Location Client may send messages to +both the Generic Location Server and the Generic Location Setup Server, as long +as it has the right application keys. + +Extended models +**************** + +None. + +Persistent storage +******************* + +None. + +API documentation +****************** + +| Header file: :file:`include/bluetooth/mesh/gen_loc_cli.h` +| Source file: :file:`subsys/bluetooth/mesh/gen_loc_cli.c` + +.. doxygengroup:: bt_mesh_loc_cli + :project: nrf + :members: + +---- + +Common types +============= + +| Header file: :file:`include/bluetooth/mesh/gen_loc.h` + +.. doxygenfile:: gen_loc.h + :project: nrf diff --git a/include/bluetooth/mesh/gen_loc_cli.h b/include/bluetooth/mesh/gen_loc_cli.h new file mode 100644 index 000000000000..8d734136d089 --- /dev/null +++ b/include/bluetooth/mesh/gen_loc_cli.h @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +/** + * @file + * @defgroup bt_mesh_loc_cli Generic Location Client model + * @{ + * @brief API for the Generic Location Client model. + */ + +#ifndef BT_MESH_GEN_LOC_CLI_H__ +#define BT_MESH_GEN_LOC_CLI_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_loc_cli; + +/** @def BT_MESH_LOC_CLI_INIT + * + * @brief Initialization parameters for a @ref bt_mesh_loc_cli instance. + * + * @param[in] _handlers Pointer to a @ref bt_mesh_loc_cli_handlers instance. + */ +#define BT_MESH_LOC_CLI_INIT(_handlers) \ + { \ + .handlers = _handlers, \ + .pub = {.msg = NET_BUF_SIMPLE(MAX( \ + BT_MESH_MODEL_BUF_LEN( \ + BT_MESH_LOC_OP_LOCAL_SET, \ + BT_MESH_LOC_MSG_LEN_LOCAL_SET), \ + BT_MESH_MODEL_BUF_LEN( \ + BT_MESH_LOC_OP_GLOBAL_SET, \ + BT_MESH_LOC_MSG_LEN_GLOBAL_SET))) } \ + } + +/** @def BT_MESH_MODEL_LOC_CLI + * + * @brief Generic Location Client model composition data entry. + * + * @param[in] _cli Pointer to a @ref bt_mesh_loc_cli instance. + */ +#define BT_MESH_MODEL_LOC_CLI(_cli) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_LOCATION_CLI, \ + _bt_mesh_loc_cli_op, &(_cli)->pub, \ + BT_MESH_MODEL_USER_DATA(struct bt_mesh_loc_cli, \ + _cli), \ + &_bt_mesh_loc_cli_cb) + +/** Location handler functions */ +struct bt_mesh_loc_cli_handlers { + /** @brief Global Location status message handler. + * + * @param[in] cli Client that received the status message. + * @param[in] ctx Context of the incoming message. + * @param[in] status Global Location parameters of the Generic Location + * Server that published the message. + */ + void (*const global_status)(struct bt_mesh_loc_cli *cli, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_loc_global *status); + + /** @brief Local Location status message handler. + * + * @param[in] cli Client that received the status message. + * @param[in] ctx Context of the incoming message. + * @param[in] status Local Location parameters of the Generic Location + * Server that published the message. + */ + void (*const local_status)(struct bt_mesh_loc_cli *cli, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_loc_local *status); +}; + +/** + * Generic Location Client structure. + * + * Should be initialized with the @ref BT_MESH_LOC_CLI_INIT macro. + */ +struct bt_mesh_loc_cli { + /** Handler function structure */ + const struct bt_mesh_loc_cli_handlers *const handlers; + /** Response context for tracking acknowledged messages. */ + struct bt_mesh_model_ack_ctx ack_ctx; + struct bt_mesh_model_pub pub; /**< Publish parameters. */ + struct bt_mesh_model *model; /**< Access model pointer. */ +}; + +/** @brief Get the global location of the bound srv. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will return, and the response will be passed to the + * @ref bt_mesh_loc_cli_handlers::global_status callback. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[out] rsp Global Location response buffer, or NULL to keep from + * blocking. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_loc_cli_global_get(struct bt_mesh_loc_cli *cli, + struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_loc_global *rsp); + +/** @brief Set the Global Location in the server. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will not request a response from the server, and return + * immediately. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[in] loc New Global Location value to set. + * @param[out] rsp Response status buffer, or NULL to send an unacknowledged + * message. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_loc_cli_global_set(struct bt_mesh_loc_cli *cli, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_loc_global *loc, + struct bt_mesh_loc_global *rsp); + +/** @brief Get the local location of the bound srv. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will return, and the response will be passed to the + * @ref bt_mesh_loc_cli_handlers::local_status callback. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[out] rsp Local Location response buffer, or NULL to keep from + * blocking. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_loc_cli_local_get(struct bt_mesh_loc_cli *cli, + struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_loc_local *rsp); + +/** @brief Set the Local Location in the server. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will not request a response from the server, and return + * immediately. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[in] loc New Local Location value to set. + * @param[out] rsp Response status buffer, or NULL to send an unacknowledged + * message. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_loc_cli_local_set(struct bt_mesh_loc_cli *cli, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_loc_local *loc, + struct bt_mesh_loc_local *rsp); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op _bt_mesh_loc_cli_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_loc_cli_cb; +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_LOC_CLI_H__ */ + +/* @} */ diff --git a/include/bluetooth/mesh/gen_loc_srv.h b/include/bluetooth/mesh/gen_loc_srv.h new file mode 100644 index 000000000000..7f6f57380919 --- /dev/null +++ b/include/bluetooth/mesh/gen_loc_srv.h @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +/** + * @file + * @defgroup bt_mesh_loc_srv Generic Location Server model + * @ingroup bt_mesh_loc + * @{ + * @brief API for the Generic Location Server model. + */ + +#ifndef BT_MESH_GEN_LOC_SRV_H__ +#define BT_MESH_GEN_LOC_SRV_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_loc_srv; + +/** @def BT_MESH_LOC_SRV_INIT + * + * @brief Init parameters for a @ref bt_mesh_loc_srv instance. + * + * @param[in] _handlers Handler function structure. + */ +#define BT_MESH_LOC_SRV_INIT(_handlers) \ + { \ + .handlers = _handlers, \ + .pub = { \ + .update = _bt_mesh_loc_srv_update_handler, \ + .msg = NET_BUF_SIMPLE(MAX( \ + BT_MESH_MODEL_BUF_LEN( \ + BT_MESH_LOC_OP_LOCAL_STATUS, \ + BT_MESH_LOC_MSG_LEN_LOCAL_STATUS), \ + BT_MESH_MODEL_BUF_LEN( \ + BT_MESH_LOC_OP_GLOBAL_STATUS, \ + BT_MESH_LOC_MSG_LEN_GLOBAL_STATUS))) \ + }, \ + } + +/** @def BT_MESH_MODEL_LOC_SRV + * + * @brief Generic Location Server model composition data entry. + * + * @param[in] _srv Pointer to a @ref bt_mesh_loc_srv instance. + */ +#define BT_MESH_MODEL_LOC_SRV(_srv) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_LOCATION_SRV, \ + _bt_mesh_loc_srv_op, &(_srv)->pub, \ + BT_MESH_MODEL_USER_DATA(struct bt_mesh_loc_srv, \ + _srv), \ + &_bt_mesh_loc_srv_cb) + +/** Location Server handler functions. */ +struct bt_mesh_loc_srv_handlers { + /** @brief Get the Global Location state. + * + * @note This handler is mandatory. + * + * @param[in] srv Server to get the Global Location state of. + * @param[in] ctx Context of the get message that triggered the query, + * or NULL if it was not triggered by a message. + * @param[out] rsp Global Location state to fill. + */ + void (*const global_get)(struct bt_mesh_loc_srv *srv, + struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_loc_global *rsp); + + /** @brief Set the Global Location state. + * + * Any changes to the new @c loc parameter will be taken into account + * before a response is sent to the client. + * + * @note This handler is mandatory. + * + * @param[in] srv Server to set the Global Location state of. + * @param[in] ctx Context of the set message that triggered the change, + * or NULL if it was not triggered by a message. + * @param[in,out] loc New Global Location state. + */ + void (*const global_set)(struct bt_mesh_loc_srv *srv, + struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_loc_global *loc); + + /** @brief Get the Local Location state. + * + * @note This handler is mandatory. + * + * @param[in] srv Server to get the Local Location state of. + * @param[in] ctx Context of the get message that triggered the query, + * or NULL if it was not triggered by a message. + * @param[out] rsp Local Location state to fill. + */ + void (*const local_get)(struct bt_mesh_loc_srv *srv, + struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_loc_local *rsp); + + /** @brief Set the Local Location state. + * + * Any changes to the new @c loc parameter will be taken into account + * before a response is sent to the client. + * + * @note This handler is mandatory. + * + * @param[in] srv Server to set the Local Location state of. + * @param[in] ctx Context of the set message that triggered the change, + * or NULL if it was not triggered by a message. + * @param[in,out] loc New Local Location state. + */ + void (*const local_set)(struct bt_mesh_loc_srv *srv, + struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_loc_local *loc); +}; + +/** + * Generic Location Server instance. Should primarily be initialized with the + * @ref BT_MESH_LOC_SRV_INIT macro. + */ +struct bt_mesh_loc_srv { + /** Pointer to the model instance. */ + struct bt_mesh_model *model; + /** Publish parameters for this model instance. */ + struct bt_mesh_model_pub pub; + + /** Current opcode being published. */ + u16_t pub_op; + /** Pointer to a handler structure. */ + const struct bt_mesh_loc_srv_handlers *const handlers; +}; + +/** @brief Publish the Global Location state. + * + * Asynchronously publishes a Global Location status message with the + * configured publish parameters. + * + * @note This API is only used publishing unprompted status messages. Response + * messages for get and set are handled internally. + * + * @note The server will only publish one state at the time. Calling this + * function will terminate any publishing of the Local Location state. + * + * @param[in] srv Server instance to publish on. + * @param[in] ctx Message context to send with, or NULL to send with the + * default publish parameters. + * @param[in] global Current global location. + * + * @retval 0 Successfully published a Global Location status message. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + */ +s32_t bt_mesh_loc_srv_global_pub(struct bt_mesh_loc_srv *srv, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_loc_global *global); + +/** @brief Publish the Local Location state. + * + * Asynchronously publishes a Local Location status message with the configured + * publish parameters. + * + * @note This API is only used publishing unprompted status messages. Response + * messages for get and set are handled internally. + * + * @note The server will only publish one state at the time. Calling this + * function will terminate any publishing of the Global Location state. + * + * @param[in] srv Server instance to publish on. + * @param[in] ctx Message context to send with, or NULL to send with the + * default publish parameters. + * @param[in] local Current local location. + * + * @retval 0 Successfully published a Local Location status message. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + */ +s32_t bt_mesh_loc_srv_local_pub(struct bt_mesh_loc_srv *srv, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_loc_local *local); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op _bt_mesh_loc_srv_op[]; +extern const struct bt_mesh_model_op _bt_mesh_loc_setup_srv_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_loc_srv_cb; +int _bt_mesh_loc_srv_update_handler(struct bt_mesh_model *model); +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_LOC_SRV_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/gen_lvl.h b/include/bluetooth/mesh/gen_lvl.h new file mode 100644 index 000000000000..6f05827b43c3 --- /dev/null +++ b/include/bluetooth/mesh/gen_lvl.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +/** + * @file + * @defgroup bt_mesh_lvl Bluetooth Mesh Generic Level models + * @{ + * @brief Common API for the Bluetooth Mesh Generic Level models. + */ +#ifndef BT_MESH_GEN_LVL_H__ +#define BT_MESH_GEN_LVL_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @cond INTERNAL_HIDDEN */ +#define BT_MESH_LVL_OP_GET BT_MESH_MODEL_OP_2(0x82, 0x05) +#define BT_MESH_LVL_OP_SET BT_MESH_MODEL_OP_2(0x82, 0x06) +#define BT_MESH_LVL_OP_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x07) +#define BT_MESH_LVL_OP_STATUS BT_MESH_MODEL_OP_2(0x82, 0x08) +#define BT_MESH_LVL_OP_DELTA_SET BT_MESH_MODEL_OP_2(0x82, 0x09) +#define BT_MESH_LVL_OP_DELTA_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x0A) +#define BT_MESH_LVL_OP_MOVE_SET BT_MESH_MODEL_OP_2(0x82, 0x0B) +#define BT_MESH_LVL_OP_MOVE_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x0C) + +#define BT_MESH_LVL_MSG_LEN_GET 0 +#define BT_MESH_LVL_MSG_MINLEN_SET 3 +#define BT_MESH_LVL_MSG_MAXLEN_SET 5 +#define BT_MESH_LVL_MSG_MINLEN_STATUS 2 +#define BT_MESH_LVL_MSG_MAXLEN_STATUS 5 +#define BT_MESH_LVL_MSG_MINLEN_DELTA_SET 5 +#define BT_MESH_LVL_MSG_MAXLEN_DELTA_SET 7 +#define BT_MESH_LVL_MSG_MINLEN_MOVE_SET 3 +#define BT_MESH_LVL_MSG_MAXLEN_MOVE_SET 5 +/** @endcond */ + +/** Generic Level minimum value. */ +#define BT_MESH_LVL_MIN INT16_MIN +/** Generic Level maximum value. */ +#define BT_MESH_LVL_MAX INT16_MAX + +/** Generic Level set message parameters. */ +struct bt_mesh_lvl_set { + /** New level. */ + s16_t lvl; + /** Whether this is a new action. */ + bool new_transaction; + /** + * Transition time parameters for the state change. Setting the + * transition to NULL makes the server use its default transition time + * parameters. + */ + const struct bt_mesh_model_transition *transition; +}; + +/** Generic Level delta set message parameters. */ +struct bt_mesh_lvl_delta_set { + /** Translation from original value. */ + s32_t delta; + /** + * Whether this is a new transaction. If true, the delta should be + * relative to the current value. If false, the delta should be + * relative to the original value in the previous delta_set command. + */ + bool new_transaction; + /** + * Transition time parameters for the state change. Setting the + * transition to NULL makes the server use its default transition time + * parameters. + */ + const struct bt_mesh_model_transition *transition; +}; + +/** Generic Level move set message parameters. */ +struct bt_mesh_lvl_move_set { + /** Translation to make for every transition step. */ + s16_t delta; + /** Whether this is a new action. */ + bool new_transaction; + /** + * Transition parameters. @c delay indicates time until the move + * should start, @c transition indicates the amount of time each + * @c delta step should take. Setting the transition to NULL makes the + * server use its default transition time parameters. + */ + const struct bt_mesh_model_transition *transition; +}; + +/** Generic Level status message parameters. */ +struct bt_mesh_lvl_status { + /** Current level value. */ + s16_t current; + /** + * Target value for the ongoing transition. If there's no ongoing + * transition, @c target should match @c value. + */ + s16_t target; + /** + * Time remaining of the ongoing transition, or @ref K_FOREVER. + * If there's no ongoing transition, @c remaining_time is 0. + */ + s32_t remaining_time; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_LVL_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/gen_lvl.rst b/include/bluetooth/mesh/gen_lvl.rst new file mode 100644 index 000000000000..9d854a03b6de --- /dev/null +++ b/include/bluetooth/mesh/gen_lvl.rst @@ -0,0 +1,96 @@ +.. _bt_mesh_lvl_readme: + +Generic Level models +#################### + +The Generic Level models allows remote control of integer states on a mesh +device. + +There are two Generic Level models: + +- :ref:`bt_mesh_lvl_srv_readme` +- :ref:`bt_mesh_lvl_cli_readme` + +.. _bt_mesh_lvl_srv_readme: + +Generic Level Server +==================== + +The Generic Level Server model owns a single Generic Level state. + +States +******* + +**Generic Level**: ``s16_t`` + +The user is expected to hold the state memory and provide access to the state +through the :cpp:type:`bt_mesh_lvl_srv_handlers` handler structure. + +Changes to the Generic Level state may include transition parameters. While +transitioning to a new level state, any requests to read out the current level +should report the actual current level in the transition, as well as the +terminal level and remaining time in milliseconds, including delay. + +If the transition includes a delay, the state shall remain unchanged until the +delay expires. + +Extended models +**************** + +None. + +Persistent storage +******************* + +None. + +API documentation +****************** + +| Header file: :file:`include/bluetooth/mesh/gen_lvl_srv.h` +| Source file: :file:`subsys/bluetooth/mesh/gen_lvl_srv.c` + +.. doxygengroup:: bt_mesh_lvl_srv + :project: nrf + :members: + +---- + +.. _bt_mesh_lvl_cli_readme: + +Generic Level Client +==================== + +The Generic Level Client model remotely controls the state of a Generic Level +Server model. + +Extended models +**************** + +None. + +Persistent storage +******************* + +None. + +API documentation +****************** + +| Header file: :file:`include/bluetooth/mesh/gen_lvl_cli.h` +| Source file: :file:`subsys/bluetooth/mesh/gen_lvl_cli.c` + +.. doxygengroup:: bt_mesh_lvl_cli + :project: nrf + :members: + +---- + +Common types +============= + +| Header file: :file:`include/bluetooth/mesh/gen_lvl.h` + +.. doxygengroup:: bt_mesh_lvl + :project: nrf + :members: diff --git a/include/bluetooth/mesh/gen_lvl_cli.h b/include/bluetooth/mesh/gen_lvl_cli.h new file mode 100644 index 000000000000..466e9f8749fe --- /dev/null +++ b/include/bluetooth/mesh/gen_lvl_cli.h @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ +/** + * @file + * @defgroup bt_mesh_lvl_cli Generic Level Client model + * @{ + * @brief API for the Generic Level Client model. + */ +#ifndef BT_MESH_GEN_LVL_CLI_H__ +#define BT_MESH_GEN_LVL_CLI_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_lvl_cli; + +/** @def BT_MESH_LVL_CLI_INIT + * + * @brief Initialization parameters for the @ref bt_mesh_lvl_cli. + * + * @param[in] _status_handler Optional status message handler. + * @sa bt_mesh_lvl_cli::status_handler + */ +#define BT_MESH_LVL_CLI_INIT(_status_handler) \ + { \ + .pub = { .msg = NET_BUF_SIMPLE(BT_MESH_MODEL_BUF_LEN( \ + BT_MESH_LVL_OP_DELTA_SET, \ + BT_MESH_LVL_MSG_MAXLEN_DELTA_SET)) }, \ + .status_handler = _status_handler, \ + } + +/** @def BT_MESH_MODEL_LVL_CLI + * + * @brief Generic Level Client model composition data entry. + * + * @param[in] _cli Pointer to a @ref bt_mesh_lvl_cli instance. + */ +#define BT_MESH_MODEL_LVL_CLI(_cli) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_LEVEL_CLI, _bt_mesh_lvl_cli_op, \ + &(_cli)->pub, \ + BT_MESH_MODEL_USER_DATA(struct bt_mesh_lvl_cli, \ + _cli), \ + &_bt_mesh_lvl_cli_cb) + +/** + * Generic Level Client instance. + * + * Should be initialized with @ref BT_MESH_LVL_CLI_INIT. + */ +struct bt_mesh_lvl_cli { + /** Model entry. */ + struct bt_mesh_model *model; + /** Publish parameters. */ + struct bt_mesh_model_pub pub; + /** Acknowledged message tracking. */ + struct bt_mesh_model_ack_ctx ack_ctx; + /** Current transaction ID. */ + u8_t tid; + + /** @brief Level status message handler. + * + * @param[in] cli Client that received the status message. + * @param[in] ctx Context of the message. + * @param[in] status Generic level status contained in the message. + */ + void (*const status_handler)(struct bt_mesh_lvl_cli *cli, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_lvl_status *status); +}; + +/** @brief Get the status of the bound server. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will return, and the response will be passed to the + * @ref bt_mesh_lvl_cli::status_handler callback. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[out] rsp Status response buffer, or NULL to keep from blocking. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_lvl_cli_get(struct bt_mesh_lvl_cli *cli, + struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_lvl_status *rsp); + +/** @brief Set the level state in the server. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will not request a response from the server, and return + * immediately. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[in] set New level value to set. Set @p set::transition to NULL to use + * the server's default transition parameters. + * @param[out] rsp Response status buffer, or NULL to send an unacknowledged + * message. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_lvl_cli_set(struct bt_mesh_lvl_cli *cli, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_lvl_set *set, + struct bt_mesh_lvl_status *rsp); + +/** @brief Trigger a differential level state change in the server. + * + * Makes the server move its level state by some delta value. If multiple + * delta_set messages are sent in a row (with less than 6 seconds interval), + * and @p delta_set::new_transaction is set to false, the server will continue + * using the same base value for its delta as in the first message, unless + * some other client made changes to the server. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will not request a response from the server, and return + * immediately. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[in] delta_set State change to make. Set @p set::transition to NULL to + * use the server's default transition parameters. + * @param[out] rsp Response status buffer, or NULL to send an unacknowledged + * message. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_lvl_cli_delta_set(struct bt_mesh_lvl_cli *cli, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_lvl_delta_set *delta_set, + struct bt_mesh_lvl_status *rsp); + +/** @brief Trigger a continuous level change in the server. + * + * Makes the server continuously move its level state by the set rate: + * + * @code + * rate_of_change = move_set->delta / move_set->transition->time + * @endcode + * + * The server will continue moving its level until it is told to stop, or until + * it reaches some application specific boundary value. The server may choose + * to wrap around the level value, depending on its usage. The move can be + * stopped by sending a new move message with a delta value of 0. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will not request a response from the server, and return + * immediately. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[in] move_set State change to make. Set @p set::transition to NULL to + * use the server's default transition parameters. + * @param[out] rsp Response status buffer, or NULL to send an unacknowledged + * message. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_lvl_cli_move_set(struct bt_mesh_lvl_cli *cli, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_lvl_move_set *move_set, + struct bt_mesh_lvl_status *rsp); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op _bt_mesh_lvl_cli_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_lvl_cli_cb; +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_LVL_CLI_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/gen_lvl_srv.h b/include/bluetooth/mesh/gen_lvl_srv.h new file mode 100644 index 000000000000..c90efc334b5f --- /dev/null +++ b/include/bluetooth/mesh/gen_lvl_srv.h @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +/** + * @file + * @defgroup bt_mesh_lvl_srv Bluetooth Mesh Generic Level Server model + * @ingroup bt_mesh_lvl + * @{ + * @brief API for the Generic Level Server model. + */ + +#ifndef BT_MESH_GEN_LVL_SRV_H__ +#define BT_MESH_GEN_LVL_SRV_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_lvl_srv; + +/** @def BT_MESH_LVL_SRV_INIT + * + * @brief Initialization parameters for a @ref bt_mesh_lvl_srv instance. + * + * @param[in] _handlers Pointer to a handler function structure. + */ +#define BT_MESH_LVL_SRV_INIT(_handlers) \ + { \ + .handlers = _handlers, \ + .pub = {.update = _bt_mesh_lvl_srv_update_handler, \ + .msg = NET_BUF_SIMPLE(BT_MESH_MODEL_BUF_LEN( \ + BT_MESH_LVL_OP_STATUS, \ + BT_MESH_LVL_MSG_MAXLEN_STATUS)), \ + } \ + } + +/** @def BT_MESH_MODEL_LVL_SRV + * + * @brief Generic Level Server model composition data entry. + * + * @param[in] _srv Pointer to a @ref bt_mesh_lvl_srv instance. + */ +#define BT_MESH_MODEL_LVL_SRV(_srv) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_LEVEL_SRV, _bt_mesh_lvl_srv_op, \ + &(_srv)->pub, \ + BT_MESH_MODEL_USER_DATA(struct bt_mesh_lvl_srv, \ + _srv), \ + &_bt_mesh_lvl_srv_cb) + +/** Handler functions for the generic level server. */ +struct bt_mesh_lvl_srv_handlers { + /** @brief Get the current level state. + * + * @note This handler is mandatory. + * + * @param[in] srv Level server to get the state of. + * @param[in] ctx Message context for the message that triggered the + * request, or NULL if the request is not coming from a message. + * @param[out] rsp Response structure to be filled. + */ + void (*const get)(struct bt_mesh_lvl_srv *srv, + struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_lvl_status *rsp); + + /** @brief Set the level state. + * + * @note This handler is mandatory. + * + * @param[in] srv Level server to set the state of. + * @param[in] ctx Message context for the message that triggered the + * change, or NULL if the change is not coming from a message. + * @param[in] set Parameters of the state change. Note that the + * transition will always be non-NULL. + * @param[out] rsp Response structure to be filled. + */ + void (*const set)(struct bt_mesh_lvl_srv *srv, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_lvl_set *set, + struct bt_mesh_lvl_status *rsp); + + /** + * @brief Change the level state relative to its current value. + * + * If @c delta_set::new_transaction is false, the state transition + * should use the same start point as the previous delta_set message, + * effectively overriding the previous message. If it's true, the level + * transition should start from the current level, stopping any + * ongoing transitions. + * + * @note This handler is mandatory. + * + * @param[in] srv Level server to change the state of. + * @param[in] ctx Message context for the message that triggered the + * change, or NULL if the change is not coming from a messge. + * @param[in] delta_set Parameters of the state change. Note that the + * transition will always be non-NULL. + * @param[out] rsp Response structure to be filled. + */ + void (*const delta_set)(struct bt_mesh_lvl_srv *srv, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_lvl_delta_set *delta_set, + struct bt_mesh_lvl_status *rsp); + + /** @brief Move the level state continuously at a given rate. + * + * The level state should move @c move_set::delta units for every + * @c move_set::transition::time milliseconds. For instance, if + * delta is 5 and the transition time is 100ms, the state should move + * at a rate of 50 per second. + * + * When reaching the border values for the state, the wraparound + * behavior is application specific. While the server is executing a + * move command, it should report its @c target value as + * @ref BT_MESH_LVL_MIN or @ref BT_MESH_LVL_MAX, depending on whether + * it's moving up or down. + * + * @note This handler is mandatory. + * + * @param[in] srv Level server to change the state of. + * @param[in] ctx Message context for the message that triggered the + * change, or NULL if the change is not coming from a messge. + * @param[in] move_set Parameters of the state change. Note that the + * transition will always be non-NULL. + * @param[out] rsp Response structure to be filled. + */ + void (*const move_set)(struct bt_mesh_lvl_srv *srv, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_lvl_move_set *move_set, + struct bt_mesh_lvl_status *rsp); +}; + +/** + * Generic Level Server instance. Should be initialized with the + * @ref BT_MESH_LVL_SRV_INIT macro. + */ +struct bt_mesh_lvl_srv { + /** Application handler functions. */ + const struct bt_mesh_lvl_srv_handlers *const handlers; + /** Pointer to the mesh model entry. */ + struct bt_mesh_model *model; + /** Model publication parameters. */ + struct bt_mesh_model_pub pub; + /** Transaction ID tracking. */ + struct bt_mesh_tid_ctx tid; +}; + +/** @brief Publish the generic level state. + * + * @param[in] srv Server whose level state should be published. + * @param[in] ctx Message context to publish with, or NULL to publish on the + * configured publish parameters. + * @param[in] status Current status. + * + * @retval 0 Successfully published a Generic Level Status message. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + */ +int bt_mesh_lvl_srv_pub(struct bt_mesh_lvl_srv *srv, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_lvl_status *status); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op _bt_mesh_lvl_srv_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_lvl_srv_cb; +int _bt_mesh_lvl_srv_update_handler(struct bt_mesh_model *model); +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_LVL_SRV_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/gen_onoff.h b/include/bluetooth/mesh/gen_onoff.h new file mode 100644 index 000000000000..e3291a6ad98b --- /dev/null +++ b/include/bluetooth/mesh/gen_onoff.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +/** + * @file + * @defgroup bt_mesh_onoff Generic OnOff Models + * @{ + * @brief Common API for the Generic OnOff models. + */ + +#ifndef BT_MESH_GEN_ONOFF_H__ +#define BT_MESH_GEN_ONOFF_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @cond INTERNAL_HIDDEN */ +#define BT_MESH_ONOFF_OP_GET BT_MESH_MODEL_OP_2(0x82, 0x01) +#define BT_MESH_ONOFF_OP_SET BT_MESH_MODEL_OP_2(0x82, 0x02) +#define BT_MESH_ONOFF_OP_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x03) +#define BT_MESH_ONOFF_OP_STATUS BT_MESH_MODEL_OP_2(0x82, 0x04) + +#define BT_MESH_ONOFF_MSG_LEN_GET 0 +#define BT_MESH_ONOFF_MSG_MINLEN_SET 2 +#define BT_MESH_ONOFF_MSG_MAXLEN_SET 4 +#define BT_MESH_ONOFF_MSG_MINLEN_STATUS 1 +#define BT_MESH_ONOFF_MSG_MAXLEN_STATUS 3 +/** @endcond */ + +/** Mandatory parameters for the Generic OnOff Set message. */ +struct bt_mesh_onoff_set { + /** State to set. */ + bool on_off; + /** Transition parameters. */ + const struct bt_mesh_model_transition *transition; +}; + +/** Parameters for the Generic OnOff Status message. */ +struct bt_mesh_onoff_status { + /** The present value of the Generic OnOff state. */ + bool present_on_off; + /** The target value of the Generic OnOff state (optional). */ + bool target_on_off; + /** Remaining time value in milliseconds. */ + s32_t remaining_time; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_ONOFF_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/gen_onoff.rst b/include/bluetooth/mesh/gen_onoff.rst new file mode 100644 index 000000000000..0256f828af50 --- /dev/null +++ b/include/bluetooth/mesh/gen_onoff.rst @@ -0,0 +1,104 @@ +.. _bt_mesh_onoff_readme: + +Generic OnOff models +#################### + +The Generic OnOff models allows remote control of boolean states on a mesh +device. + +There are two Generic OnOff models: + +- :ref:`bt_mesh_onoff_srv_readme` +- :ref:`bt_mesh_onoff_cli_readme` + +.. _bt_mesh_onoff_srv_readme: + +Generic OnOff Server +==================== + +The Generic OnOff Server represents a single controllable On/Off value. + + +States +******* + +This model has the following states: + +**Generic OnOff:** ``boolean`` + +Generic boolean state representing an On/Off state. The user is expected +to hold the state memory and provide access to the state through the +:cpp:type:`bt_mesh_onoff_srv_handlers` handler structure. + +Changes to the Generic OnOff state may include transition parameters. When +transitioning to a new OnOff state, the state should be `on` during the entire +transition, regardless of target state. This ensures that any bound non-binary +states can have non-0 values during the transition. + +Any requests to read out the current OnOff state while in a transition should +report the current OnOff value being `on`. + +The ``remaining_time`` parameter should be reported in milliseconds, including +delay. If the transition parameters includes a delay, the state shall remain +unchanged until the delay expires. + +Extended models +**************** + +None. + +Persistent storage +******************* + +None. + +API documentation +****************** + +| Header file: :file:`include/bluetooth/mesh/gen_onoff_srv.h` +| Source file: :file:`subsys/bluetooth/mesh/gen_onoff_srv.c` + +.. doxygengroup:: bt_mesh_onoff_srv + :project: nrf + :members: + +---- + +.. _bt_mesh_onoff_cli_readme: + +Generic OnOff Client +==================== + +The Generic OnOff Client model remotely controls the state of a Generic OnOff +Server model. + +Extended models +**************** + +None. + +Persistent storage +******************* + +None. + +API documentation +****************** + +| Header file: :file:`include/bluetooth/mesh/gen_onoff_cli.h` +| Source file: :file:`subsys/bluetooth/mesh/gen_onoff_cli.c` + +.. doxygengroup:: bt_mesh_onoff_cli + :project: nrf + :members: + +---- + +Common types +============= + +| Header file: :file:`include/bluetooth/mesh/gen_onoff.h` + +.. doxygengroup:: bt_mesh_onoff + :project: nrf + :members: diff --git a/include/bluetooth/mesh/gen_onoff_cli.h b/include/bluetooth/mesh/gen_onoff_cli.h new file mode 100644 index 000000000000..539488367980 --- /dev/null +++ b/include/bluetooth/mesh/gen_onoff_cli.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +/** + * @file + * @defgroup bt_mesh_onoff_cli Generic OnOff Client model + * @{ + * @brief API for the Generic OnOff Client model. + */ + +#ifndef BT_MESH_GEN_ONOFF_CLI_H__ +#define BT_MESH_GEN_ONOFF_CLI_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_onoff_cli; + +/** @def BT_MESH_ONOFF_CLI_INIT + * + * @brief Initialization parameters for a @ref bt_mesh_onoff_cli instance. + * + * @param[in] _status_handler Optional status message handler. + */ +#define BT_MESH_ONOFF_CLI_INIT(_status_handler) \ + { \ + .status_handler = _status_handler, \ + .pub = {.msg = NET_BUF_SIMPLE(BT_MESH_MODEL_BUF_LEN( \ + BT_MESH_ONOFF_OP_SET, \ + BT_MESH_ONOFF_MSG_MAXLEN_SET)) } \ + } + +/** @def BT_MESH_MODEL_ONOFF_CLI + * + * @brief Generic OnOff Client model composition data entry. + * + * @param[in] _cli Pointer to a @ref bt_mesh_onoff_cli instance. + */ +#define BT_MESH_MODEL_ONOFF_CLI(_cli) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, \ + _bt_mesh_onoff_cli_op, &(_cli)->pub, \ + BT_MESH_MODEL_USER_DATA(struct bt_mesh_onoff_cli, \ + _cli), \ + &_bt_mesh_onoff_cli_cb) + +/** + * Generic OnOff Client structure. + * + * Should be initialized with the @ref BT_MESH_ONOFF_CLI_INIT macro. + */ +struct bt_mesh_onoff_cli { + /** @brief OnOff status message handler. + * + * @param[in] cli Client that received the status message. + * @param[in] ctx Context of the incoming message. + * @param[in] status OnOff Status of the Generic OnOff Server that + * published the message. + */ + void (*const status_handler)(struct bt_mesh_onoff_cli *cli, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_onoff_status *status); + /** Current Transaction ID. */ + u8_t tid; + /** Response context for tracking acknowledged messages. */ + struct bt_mesh_model_ack_ctx ack_ctx; + /** Publish parameters. */ + struct bt_mesh_model_pub pub; + /** Access model pointer. */ + struct bt_mesh_model *model; +}; + +/** @brief Get the status of the bound srv. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will return, and the response will be passed to the + * @ref bt_mesh_onoff_cli::status_handler callback. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[out] rsp Status response buffer, or NULL to keep from blocking. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_onoff_cli_get(struct bt_mesh_onoff_cli *cli, + struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_onoff_status *rsp); + +/** @brief Set the OnOff state in the srv. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will not request a response from the server, and return + * immediately. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[in] set New OnOff parameters to set. @p set::transition can either + * point to a transition structure, or be left to NULL to use the default + * transition parameters on the server. + * @param[out] rsp Response status buffer, or NULL to send an unacknowledged + * message. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_onoff_cli_set(struct bt_mesh_onoff_cli *cli, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_onoff_set *set, + struct bt_mesh_onoff_status *rsp); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op _bt_mesh_onoff_cli_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_onoff_cli_cb; +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_ONOFF_CLI_H__ */ + +/* @} */ diff --git a/include/bluetooth/mesh/gen_onoff_srv.h b/include/bluetooth/mesh/gen_onoff_srv.h new file mode 100644 index 000000000000..7eaa8ef4f481 --- /dev/null +++ b/include/bluetooth/mesh/gen_onoff_srv.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +/** + * @file + * @defgroup bt_mesh_onoff_srv Generic OnOff Server model + * @ingroup bt_mesh_onoff + * @{ + * @brief API for the Generic OnOff Server model. + */ + +#ifndef BT_MESH_GEN_ONOFF_SRV_H__ +#define BT_MESH_GEN_ONOFF_SRV_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_onoff_srv; + +/** @def BT_MESH_ONOFF_SRV_INIT + * + * @brief Init parameters for a @ref bt_mesh_onoff_srv instance. + * + * @param[in] _handlers State access handlers to use in the model instance. + */ +#define BT_MESH_ONOFF_SRV_INIT(_handlers) \ + { \ + .handlers = _handlers, \ + .pub = { \ + .update = _bt_mesh_onoff_srv_update_handler, \ + .msg = NET_BUF_SIMPLE(BT_MESH_MODEL_BUF_LEN( \ + BT_MESH_ONOFF_OP_STATUS, \ + BT_MESH_ONOFF_MSG_MAXLEN_STATUS)), \ + }, \ + } + +/** @def BT_MESH_MODEL_ONOFF_SRV + * + * @brief Generic OnOff Server model composition data entry. + * + * @param[in] _srv Pointer to a @ref bt_mesh_onoff_srv instance. + */ +#define BT_MESH_MODEL_ONOFF_SRV(_srv) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, \ + _bt_mesh_onoff_srv_op, &(_srv)->pub, \ + BT_MESH_MODEL_USER_DATA(struct bt_mesh_onoff_srv, \ + _srv), \ + &_bt_mesh_onoff_srv_cb) + +/** Generic OnOff Server state access handlers. */ +struct bt_mesh_onoff_srv_handlers { + /** @brief Set the OnOff state. + * + * @note This handler is mandatory. + * + * @param[in] srv Server instance to set the state of. + * @param[in] ctx Message context for the message that triggered the + * change, or NULL if the change is not coming from a message. + * @param[in] set Parameters of the state change. + * @param[out] rsp Response structure to be filled, or NULL if no + * response is required. + */ + void (*const set)(struct bt_mesh_onoff_srv *srv, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_onoff_set *set, + struct bt_mesh_onoff_status *rsp); + + /** @brief Get the OnOff state. + * + * @note This handler is mandatory. + * + * @param[in] srv Server instance to get the state of. + * @param[in] ctx Message context for the message that triggered the + * change, or NULL if the change is not coming from a message. + * @param[out] rsp Response structure to be filled. + */ + void (*const get)(struct bt_mesh_onoff_srv *srv, + struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_onoff_status *rsp); +}; + +/** + * Generic OnOff Server instance. Should primarily be initialized with the + * @ref BT_MESH_ONOFF_SRV_INIT macro. + */ +struct bt_mesh_onoff_srv { + /** Transaction ID tracker. */ + struct bt_mesh_tid_ctx prev_transaction; + /** Handler function structure. */ + const struct bt_mesh_onoff_srv_handlers *handlers; + /** Access model pointer. */ + struct bt_mesh_model *model; + /** Publish parameters. */ + struct bt_mesh_model_pub pub; +}; + +/** @brief Publish the Generic OnOff Server model status. + * + * Asynchronously publishes a Generic OnOff status message with the configured + * publish parameters. + * + * @note This API is only used publishing unprompted status messages. Response + * messages for get and set messages are handled internally. + * + * @param[in] srv Server instance to publish on. + * @param[in] ctx Message context to send with, or NULL to send with the + * default publish parameters. + * @param[in] status Current status. + * + * @retval 0 Successfully published a Generic OnOff Status message. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + */ +s32_t bt_mesh_onoff_srv_pub(struct bt_mesh_onoff_srv *srv, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_onoff_status *status); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op _bt_mesh_onoff_srv_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_onoff_srv_cb; +int _bt_mesh_onoff_srv_update_handler(struct bt_mesh_model *model); +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_ONOFF_SRV_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/gen_plvl.h b/include/bluetooth/mesh/gen_plvl.h new file mode 100644 index 000000000000..9fe16a3bc876 --- /dev/null +++ b/include/bluetooth/mesh/gen_plvl.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ +/** + * @file + * @defgroup bt_mesh_plvl Generic Power Level Models + * @{ + * @brief API for the Generic Power Level models. + */ + +#ifndef BT_MESH_GEN_PLVL_H__ +#define BT_MESH_GEN_PLVL_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @cond INTERNAL_HIDDEN */ +#define BT_MESH_PLVL_OP_LEVEL_GET BT_MESH_MODEL_OP_2(0x82, 0x15) +#define BT_MESH_PLVL_OP_LEVEL_SET BT_MESH_MODEL_OP_2(0x82, 0x16) +#define BT_MESH_PLVL_OP_LEVEL_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x17) +#define BT_MESH_PLVL_OP_LEVEL_STATUS BT_MESH_MODEL_OP_2(0x82, 0x18) +#define BT_MESH_PLVL_OP_LAST_GET BT_MESH_MODEL_OP_2(0x82, 0x19) +#define BT_MESH_PLVL_OP_LAST_STATUS BT_MESH_MODEL_OP_2(0x82, 0x1A) +#define BT_MESH_PLVL_OP_DEFAULT_GET BT_MESH_MODEL_OP_2(0x82, 0x1B) +#define BT_MESH_PLVL_OP_DEFAULT_STATUS BT_MESH_MODEL_OP_2(0x82, 0x1C) +#define BT_MESH_PLVL_OP_RANGE_GET BT_MESH_MODEL_OP_2(0x82, 0x1D) +#define BT_MESH_PLVL_OP_RANGE_STATUS BT_MESH_MODEL_OP_2(0x82, 0x1E) +#define BT_MESH_PLVL_OP_DEFAULT_SET BT_MESH_MODEL_OP_2(0x82, 0x1F) +#define BT_MESH_PLVL_OP_DEFAULT_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x20) +#define BT_MESH_PLVL_OP_RANGE_SET BT_MESH_MODEL_OP_2(0x82, 0x21) +#define BT_MESH_PLVL_OP_RANGE_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x22) + +#define BT_MESH_PLVL_MSG_LEN_LEVEL_GET 0 +#define BT_MESH_PLVL_MSG_MINLEN_LEVEL_SET 3 +#define BT_MESH_PLVL_MSG_MAXLEN_LEVEL_SET 5 +#define BT_MESH_PLVL_MSG_MINLEN_LEVEL_STATUS 2 +#define BT_MESH_PLVL_MSG_MAXLEN_LEVEL_STATUS 5 +#define BT_MESH_PLVL_MSG_LEN_LAST_GET 0 +#define BT_MESH_PLVL_MSG_LEN_LAST_STATUS 2 +#define BT_MESH_PLVL_MSG_LEN_DEFAULT_GET 0 +#define BT_MESH_PLVL_MSG_LEN_DEFAULT_STATUS 2 +#define BT_MESH_PLVL_MSG_LEN_RANGE_GET 0 +#define BT_MESH_PLVL_MSG_LEN_RANGE_STATUS 4 +#define BT_MESH_PLVL_MSG_LEN_DEFAULT_SET 2 +#define BT_MESH_PLVL_MSG_LEN_RANGE_SET 4 +/** @endcond */ + +/** Power Level set message parameters. */ +struct bt_mesh_plvl_set { + /** Power Level. */ + u16_t power_lvl; + /** + * Transition time parameters for the state change. Setting the + * transition to NULL makes the server use its default transition time + * parameters. + */ + const struct bt_mesh_model_transition *transition; +}; + +/** Power Level status message parameters. */ +struct bt_mesh_plvl_status { + /** Current Power Level. */ + u16_t current; + /** Target Power Level. */ + u16_t target; + /** + * Time remaining of the ongoing transition, or @ref K_FOREVER. + * If there's no ongoing transition, @c remaining_time is 0. + */ + s32_t remaining_time; +}; + +/** Power Level range parameters. */ +struct bt_mesh_plvl_range { + u16_t min; /**< Minimum allowed Power Level. */ + u16_t max; /**< Maximum allowed Power Level. */ +}; + +/** Power Level range message parameters. */ +struct bt_mesh_plvl_range_status { + /** Status of the previous operation. */ + enum bt_mesh_model_status status; + /** Current Power Level range. */ + struct bt_mesh_plvl_range range; +}; + +/** + * @brief Convert Power Level to a percent. + * + * @param[in] plvl Raw Power Level. + * + * @return The Power Level in percent. + */ +static inline u8_t bt_mesh_plvl_to_percent(u16_t plvl) +{ + return (100UL * plvl) / UINT16_MAX; +} + +/** + * @brief Convert percent to raw Power Level. + * + * @param[in] plvl_percent Power Level in percent. + * + * @return The raw Power Level. + */ +static inline u16_t bt_mesh_plvl_from_percent(u8_t plvl_percent) +{ + return (UINT16_MAX * plvl_percent) / 100; +} + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_PLVL_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/gen_plvl.rst b/include/bluetooth/mesh/gen_plvl.rst new file mode 100644 index 000000000000..a75d6439c472 --- /dev/null +++ b/include/bluetooth/mesh/gen_plvl.rst @@ -0,0 +1,183 @@ +.. _bt_mesh_plvl_readme: + +Generic Power Level models +########################## + +The Generic Power Level models allow remote control of power levels on a +mesh device. Typical applications for the Generic Power Level model are +heaters, fans and dimmer outlets. + +There are two Generic Power Level models: + +- :ref:`bt_mesh_plvl_srv_readme` +- :ref:`bt_mesh_plvl_cli_readme` + +.. _bt_mesh_plvl_srv_readme: + +Generic Power Level Server +========================== + +The Generic Power Level Server controls the output power level of a peripheral +on the Mesh-enabled device. + +Generic Power Level Server adds two model instances in the composition data: + +- The Generic Power Level Server +- The Generic Power Level Setup Server + +The two model instances share the states of the Generic Power Level Server, +but accept different messages. This allows fine-grained control of the access +rights for the Generic Power Level states, as the two model instances can be +bound to different application keys. + +The Generic Power Level Server is the "user facing" model instance in the pair, +and only provides access to the Generic Power Level state, and its last +non-zero value. + +The Generic Power Level Setup Server provides access to the two metastates, +Default Power and Power Range, allowing configurator devices to set up the +range and default value for the Generic Power Level state. + +States +******* + +**Generic Power Level**: ``u16_t`` + +The Generic Power Level state controls the Power level of an element, and +ranges from 0 to 65535. The Generic Power Level state is bound to the +Generic Level State of the :ref:`bt_mesh_lvl_srv_readme`: + +:: + + Generic Power Level = Generic Level + 32768 + +The Generic OnOff state of the :ref:`bt_mesh_onoff_srv_readme` (extended +through the :ref:`bt_mesh_ponoff_srv_readme`) is derived from the Power state: + +:: + + Generic OnOff = (Generic Power Level > 0) + +Conversely, if the Generic OnOff state is changed to Off, the Generic Power +Level is set to 0. If the Generic OnOff state is changed to On and the Default +Level state is set, the Generic Power level is set to the value of the Default +level state. If the Generic OnOff state is changed to On and the Default Level +state is not set, the Generic Power Level state is set to the last known +non-zero value. + +The Power state power up behavior is determined by the On Power Up state of the +extended :ref:`bt_mesh_ponoff_srv_readme`: + +- :cpp:enumerator:`BT_MESH_ON_POWER_UP_OFF `: + The Power level is set to 0 on power up. +- :cpp:enumerator:`BT_MESH_ON_POWER_UP_ON `: + The Power level is set to Default Level on power up, or the last known + non-zero Power level if the Default level is not set. +- :cpp:enumerator:`BT_MESH_ON_POWER_UP_RESTORE `: + The Power level is set to the last known Power level (zero or otherwise). + +The user is expected to hold the state memory and provide access to the state +through the :cpp:type:`bt_mesh_plvl_srv_handlers` handler structure. + +**Default Power**: ``s16_t`` + +The Default Power state is a metastate that controls the default non-zero +Generic Power Level. It is used when the Generic Power Level turns on, but its +exact level is not specified. + +The memory for the Default Power state is held by the model, and the +application may receive updates on state changes through the +:cpp:member:`bt_mesh_plvl_srv_handlers::default_update` callback. + +**Power Range**: :cpp:type:`bt_mesh_plvl_range` + +The Power Range state is a metastate that determines the accepted Generic Power +Level range. + +If the Generic Power Level is set to a value outside the current Power Range, +the actual Generic Power Level is moved to fit inside the range. + +If the Power Level Range changes to exclude the current Generic Power Level, +the Generic Power Level should be changed accordingly. Note that the Generic +Power Level may always be set to zero, even if this is outside the current +Power Range. + +The memory for the Power Range state is held by the model, and the +application may receive updates on state changes through the +:cpp:member:`bt_mesh_plvl_srv_handlers::range_update` callback. + +Extended models +**************** + +The Generic Power Level Server extends the following models: + +- :ref:`bt_mesh_lvl_srv_readme` +- :ref:`bt_mesh_ponoff_srv_readme` + +As the states of both extended models are bound to states in the Generic Power +Level Server, the states of the extended models are not exposed directly to the +application. + +Persistent storage +******************* + +The Generic Power Level Server stores any changes to the Default Power and +Power Range states, as well as the last known non-zero Generic Power Level and +whether the Generic Power Level is on or off. This information is used to +reestablish the correct Generic Power Level when the device powers up. + +API documentation +****************** + +| Header file: :file:`include/bluetooth/mesh/gen_plvl_srv.h` +| Source file: :file:`subsys/bluetooth/mesh/gen_plvl_srv.c` + +.. doxygengroup:: bt_mesh_plvl_srv + :project: nrf + :members: + +---- + +.. _bt_mesh_plvl_cli_readme: + +Generic Power Level Client +========================== + +The Generic Power Level Client model remotely controls the state of a Generic +Power Level Server model. + +Contrary to the Server model, the Client only creates a single model instance +in the mesh composition data. The Generic Power Level Client may send +messages to both the Generic Power Level Server and the Generic Power Level +Setup Server, as long as it has the right application keys. + +Extended models +**************** + +None. + +Persistent storage +******************* + +None. + +API documentation +****************** + +| Header file: :file:`include/bluetooth/mesh/gen_plvl_cli.h` +| Source file: :file:`subsys/bluetooth/mesh/gen_plvl_cli.c` + +.. doxygengroup:: bt_mesh_plvl_cli + :project: nrf + :members: + +---- + +Common types +============= + +| Header file: :file:`include/bluetooth/mesh/gen_plvl.h` + +.. doxygengroup:: bt_mesh_plvl + :project: nrf + :members: diff --git a/include/bluetooth/mesh/gen_plvl_cli.h b/include/bluetooth/mesh/gen_plvl_cli.h new file mode 100644 index 000000000000..dd9a4c5a4978 --- /dev/null +++ b/include/bluetooth/mesh/gen_plvl_cli.h @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +/** + * @file + * @defgroup bt_mesh_plvl_cli Generic Power Level Client model + * @{ + * @brief API for the Generic Power Level Client model. + */ + +#ifndef BT_MESH_GEN_PLVL_CLI_H__ +#define BT_MESH_GEN_PLVL_CLI_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_plvl_cli; + +/** @def BT_MESH_PLVL_CLI_INIT + * + * @brief Initialization parameters for the @ref bt_mesh_plvl_cli. + * + * @param[in] _handlers Optional message handler structure. + * @sa bt_mesh_plvl_cli_handlers. + */ +#define BT_MESH_PLVL_CLI_INIT(_handlers) \ + { \ + .pub = { .msg = NET_BUF_SIMPLE(BT_MESH_MODEL_BUF_LEN( \ + BT_MESH_PLVL_OP_LEVEL_SET, \ + BT_MESH_PLVL_MSG_MAXLEN_LEVEL_SET)) }, \ + .handlers = _handlers, \ + } + +/** @def BT_MESH_MODEL_PLVL_CLI + * + * @brief Generic Power Level Client model composition data entry. + * + * @param[in] _cli Pointer to a @ref bt_mesh_plvl_cli instance. + */ +#define BT_MESH_MODEL_PLVL_CLI(_cli) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_POWER_LEVEL_CLI, \ + _bt_mesh_plvl_cli_op, &(_cli)->pub, \ + BT_MESH_MODEL_USER_DATA(struct bt_mesh_plvl_cli, \ + _cli), \ + &_bt_mesh_plvl_cli_cb) + +/** Handler function for the Power Level Client. */ +struct bt_mesh_plvl_cli_handlers { + /** @brief Power Level status message handler. + * + * @param[in] cli Client that received the status message. + * @param[in] ctx Context of the message. + * @param[in] status Generic Power Level status contained in the + * message. + */ + void (*const power_status)(struct bt_mesh_plvl_cli *cli, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_plvl_status *status); + + /** @brief Default Power status message handler. + * + * @param[in] cli Client that received the status message. + * @param[in] ctx Context of the message. + * @param[in] default_power Default Power Level of the server. + */ + void (*const default_status)(struct bt_mesh_plvl_cli *cli, + struct bt_mesh_msg_ctx *ctx, + u16_t default_power); + + /** @brief Power Range status message handler. + * + * @param[in] cli Client that received the status message. + * @param[in] ctx Context of the message. + * @param[in] status Power Range state of the server. + */ + void (*const range_status)( + struct bt_mesh_plvl_cli *cli, struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_plvl_range_status *status); + + /** @brief Last non-zero Power Level status message handler. + * + * @param[in] cli Client that received the status message. + * @param[in] ctx Context of the message. + * @param[in] last Last non-zero Power Level of the server. + */ + void (*const last_status)(struct bt_mesh_plvl_cli *cli, + struct bt_mesh_msg_ctx *ctx, u16_t last); +}; + +/** + * Generic Power Level Client instance. Should be initialized with + * @ref BT_MESH_PLVL_CLI_INIT. + */ +struct bt_mesh_plvl_cli { + /** Model entry. */ + struct bt_mesh_model *model; + /** Publish parameters. */ + struct bt_mesh_model_pub pub; + /** Acknowledged message tracking. */ + struct bt_mesh_model_ack_ctx ack_ctx; + /** Current transaction ID. */ + u8_t tid; + /** Collection of handler callbacks */ + const struct bt_mesh_plvl_cli_handlers *const handlers; +}; + +/** @brief Get the Power Level of the bound server. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will return, and the response will be passed to the + * @ref bt_mesh_plvl_cli_handlers::power_status callback. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[in] rsp Status response buffer, or NULL to keep from blocking. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_plvl_cli_power_get(struct bt_mesh_plvl_cli *cli, + struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_plvl_status *rsp); + +/** @brief Set the Power Level of the server. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will not request a response from the server, and return + * immediately. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[in] set New Power Level value to set. Set @p set::transition to NULL + * to use the server's default transition parameters. + * @param[in] rsp Response status buffer, or NULL to send an unacknowledged + * message. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_plvl_cli_power_set(struct bt_mesh_plvl_cli *cli, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_plvl_set *set, + struct bt_mesh_plvl_status *rsp); + +/** @brief Get the Power Range of the bound server. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will return, and the response will be passed to the + * @ref bt_mesh_plvl_cli_handlers::range_status callback. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[in] rsp Status response buffer, or NULL to keep from blocking. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_plvl_cli_range_get(struct bt_mesh_plvl_cli *cli, + struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_plvl_range_status *rsp); + +/** @brief Set the Power Range state in the server. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will not request a response from the server, and return + * immediately. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[in] range New Power Range value to set. + * @param[in] rsp Response status buffer, or NULL to send an unacknowledged + * message. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_plvl_cli_range_set(struct bt_mesh_plvl_cli *cli, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_plvl_range *range, + struct bt_mesh_plvl_range_status *rsp); + +/** @brief Get the Default Power of the bound server. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will return, and the response will be passed to the + * @ref bt_mesh_plvl_cli_handlers::default_status callback. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[in] rsp Status response buffer, or NULL to keep from blocking. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_plvl_cli_default_get(struct bt_mesh_plvl_cli *cli, + struct bt_mesh_msg_ctx *ctx, u16_t *rsp); + +/** @brief Set the Default Power state in the server. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will not request a response from the server, and return + * immediately. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[in] default_power New Default Power value to set. + * @param[in] rsp Response status buffer, or NULL to send an unacknowledged + * message. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_plvl_cli_default_set(struct bt_mesh_plvl_cli *cli, + struct bt_mesh_msg_ctx *ctx, + u16_t default_power, u16_t *rsp); + +/** @brief Get the last non-zero Power Level of the bound server. + * + * The last non-zero Power Level is the Power Level that will be restored if + * the Power Level changes from off to on and no Default Power is set. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will return, and the response will be passed to the + * @ref bt_mesh_plvl_cli_handlers::last_status callback. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[in] rsp Status response buffer, or NULL to keep from blocking. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_plvl_cli_last_get(struct bt_mesh_plvl_cli *cli, + struct bt_mesh_msg_ctx *ctx, u16_t *rsp); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op _bt_mesh_plvl_cli_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_plvl_cli_cb; +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_PLVL_CLI_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/gen_plvl_srv.h b/include/bluetooth/mesh/gen_plvl_srv.h new file mode 100644 index 000000000000..609c92508548 --- /dev/null +++ b/include/bluetooth/mesh/gen_plvl_srv.h @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +/** + * @file + * @defgroup bt_mesh_plvl_srv Generic Power Level Server model + * @{ + * @brief API for the Generic Power Level Server. + */ + +#ifndef BT_MESH_GEN_PLVL_SRV_H__ +#define BT_MESH_GEN_PLVL_SRV_H__ + +#include + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_plvl_srv; + +/** @def BT_MESH_PLVL_SRV_INIT + * + * @brief Initialization parameters for @ref bt_mesh_plvl_srv. + * + * @param[in] _handlers Handler callback structure. + */ +#define BT_MESH_PLVL_SRV_INIT(_handlers) \ + { \ + .lvl = BT_MESH_LVL_SRV_INIT(&bt_mesh_plvl_srv_lvl_handlers), \ + .ponoff = BT_MESH_PONOFF_SRV_INIT( \ + &bt_mesh_plvl_srv_onoff_handlers, NULL, NULL), \ + .pub = { .update = _bt_mesh_plvl_srv_update_handler, \ + .msg = NET_BUF_SIMPLE(BT_MESH_MODEL_BUF_LEN( \ + BT_MESH_PLVL_OP_LEVEL_STATUS, \ + BT_MESH_PLVL_MSG_MAXLEN_LEVEL_STATUS)) }, \ + .handlers = _handlers, \ + } + +/** @def BT_MESH_MODEL_PLVL_SRV + * + * @brief Generic Power Level model entry. + * + * @param[in] _srv Pointer to a @ref bt_mesh_plvl_srv instance. + */ +#define BT_MESH_MODEL_PLVL_SRV(_srv) \ + BT_MESH_MODEL_LVL_SRV(&(_srv)->lvl), \ + BT_MESH_MODEL_PONOFF_SRV(&(_srv)->ponoff), \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_POWER_LEVEL_SRV, \ + _bt_mesh_plvl_srv_op, &(_srv)->pub, \ + BT_MESH_MODEL_USER_DATA( \ + struct bt_mesh_plvl_srv, _srv), \ + &_bt_mesh_plvl_srv_cb), \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_POWER_LEVEL_SETUP_SRV, \ + _bt_mesh_plvl_setup_srv_op, NULL, \ + BT_MESH_MODEL_USER_DATA( \ + struct bt_mesh_plvl_srv, _srv), \ + NULL) + +/** Collection of handler callbacks for the Generic Power Level Server. */ +struct bt_mesh_plvl_srv_handlers { + /** @brief Set the Power state. + * + * @note This handler is mandatory. + * + * @param[in] srv Server to set the Power state of. + * @param[in] ctx Message context, or NULL if the state change was not + * a result of a message. + * @param[in] set Parameters of the state change. + * @param[out] rsp Response structure to be filled. + */ + void (*const power_set)(struct bt_mesh_plvl_srv *srv, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_plvl_set *set, + struct bt_mesh_plvl_status *rsp); + + /** @brief Get the current Power state. + * + * @note This handler is mandatory. + * + * @param[in] srv Server to get the Power state of. + * @param[in] ctx Context of the get message that triggered the query, + * or NULL if it was not triggered by a message. + * @param[out] rsp Response structure to be filled. + */ + void (*const power_get)(struct bt_mesh_plvl_srv *srv, + struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_plvl_status *rsp); + + /** @brief The Default Power state has changed. + * + * The user may implement this handler to subscribe to changes to the + * Default Power state. + * + * @param[in] srv Server the Default Power state was changed on. + * @param[in] ctx Context of the set message that triggered the update, + * or NULL if it was not triggered by a message. + * @param[in] old_default The Default Power before the change. + * @param[in] new_default The Default Power after the change. + */ + void (*const default_update)(struct bt_mesh_plvl_srv *srv, + struct bt_mesh_msg_ctx *ctx, + u16_t old_default, u16_t new_default); + + /** @brief The Power Range state has changed. + * + * The user may implement this handler to subscribe to change to the + * Power Range state. If the change in range causes the current Power + * state to be out of range, the Power state should be changed to the + * nearest value inside the range. It's recommended to call + * @ref bt_mesh_plvl_srv_pub to notify the mesh if the Power state + * changes. + * + * @param[in] srv Server the Power Range state was changed on. + * @param[in] ctx Context of the set message that triggered the update, + * or NULL if it was not triggered by a message. + * @param[in] old_range The Power Range before the change. + * @param[in] new_range The Power Range after the change. + */ + void (*const range_update)(struct bt_mesh_plvl_srv *srv, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_plvl_range *old_range, + const struct bt_mesh_plvl_range *new_range); +}; + +/** + * Generic Power Level Server instance. + * + * Should be initialized with @ref BT_MESH_PLVL_SRV_INIT. + */ +struct bt_mesh_plvl_srv { + /** Generic Level Server instance. */ + struct bt_mesh_lvl_srv lvl; + /** Generic Power OnOff server instance. */ + struct bt_mesh_ponoff_srv ponoff; + /** Pointer to the model entry in the composition data. */ + struct bt_mesh_model *plvl_model; + /** Model publication parameters. */ + struct bt_mesh_model_pub pub; + /** Transaction ID tracker for the set messages. */ + struct bt_mesh_tid_ctx tid; + /** User handler functions. */ + const struct bt_mesh_plvl_srv_handlers *const handlers; + + /** Current Power Range. */ + struct bt_mesh_plvl_range range; + /** Current Default Power. */ + u16_t default_power; + /** The last known Power Level. */ + u16_t last; + /** Whether the Power is on. */ + bool is_on; +}; + +/** @brief Publish the current Power state. + * + * Publishes a Generic Power Level status message with the configured publish + * parameters, or using the given message context. + * + * @note This API is only used publishing unprompted status messages. Response + * messages for get and set messages are handled internally. + * + * @param[in] srv Server instance to publish with. + * @param[in] ctx Message context, or NULL to publish with the configured + * parameters. + * @param[in] status Status to publish. + * + * @return 0 Successfully published the current Power state. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + */ +int bt_mesh_plvl_srv_pub(struct bt_mesh_plvl_srv *srv, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_plvl_status *status); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_cb _bt_mesh_plvl_srv_cb; +extern const struct bt_mesh_model_op _bt_mesh_plvl_srv_op[]; +extern const struct bt_mesh_model_op _bt_mesh_plvl_setup_srv_op[]; +extern const struct bt_mesh_lvl_srv_handlers bt_mesh_plvl_srv_lvl_handlers; +extern const struct bt_mesh_onoff_srv_handlers bt_mesh_plvl_srv_onoff_handlers; +int _bt_mesh_plvl_srv_update_handler(struct bt_mesh_model *model); +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_PLVL_SRV_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/gen_ponoff.h b/include/bluetooth/mesh/gen_ponoff.h new file mode 100644 index 000000000000..22d1d4bc5cea --- /dev/null +++ b/include/bluetooth/mesh/gen_ponoff.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ +/** + * @file + * @defgroup bt_mesh_ponoff Generic Power OnOff Models + * @{ + * @brief API for the Generic Power OnOff models. + */ + +#ifndef BT_MESH_GEN_PONOFF_H__ +#define BT_MESH_GEN_PONOFF_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @cond INTERNAL_HIDDEN */ +#define BT_MESH_PONOFF_OP_GET BT_MESH_MODEL_OP_2(0x82, 0x11) +#define BT_MESH_PONOFF_OP_STATUS BT_MESH_MODEL_OP_2(0x82, 0x12) +#define BT_MESH_PONOFF_OP_SET BT_MESH_MODEL_OP_2(0x82, 0x13) +#define BT_MESH_PONOFF_OP_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x14) + +#define BT_MESH_PONOFF_MSG_LEN_GET 0 +#define BT_MESH_PONOFF_MSG_LEN_STATUS 1 +#define BT_MESH_PONOFF_MSG_LEN_SET 1 +/** @endcond */ + +/** Generic Power OnOff On Power Up state values. */ +enum bt_mesh_on_power_up { + /** On power up, set state to off. */ + BT_MESH_ON_POWER_UP_OFF, + /** On power up, set state to on. */ + BT_MESH_ON_POWER_UP_ON, + /** On power up, Restore the previous state value. */ + BT_MESH_ON_POWER_UP_RESTORE, + /** Invalid power up state. */ + BT_MESH_ON_POWER_UP_INVALID, +}; + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_PONOFF_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/gen_ponoff.rst b/include/bluetooth/mesh/gen_ponoff.rst new file mode 100644 index 000000000000..8ef1da391509 --- /dev/null +++ b/include/bluetooth/mesh/gen_ponoff.rst @@ -0,0 +1,140 @@ +.. _bt_mesh_ponoff_readme: + +Generic Power OnOff models +########################## + +The Generic Power OnOff models allow remote control of the power up behavior +on a mesh device. + +There are two Generic Power OnOff models: + +- :ref:`bt_mesh_ponoff_srv_readme` +- :ref:`bt_mesh_ponoff_cli_readme` + +.. _bt_mesh_ponoff_srv_readme: + +Generic Power OnOff Server +========================== + +The Generic Power OnOff server controls the power up behavior of other models +on its element. The Generic Power OnOff server depends on the +:option:`CONFIG_BT_SETTINGS` to be enabled to work properly. Unless +:option:`CONFIG_BT_SETTINGS` is explicitly disabled, including the Generic +Power OnOff Server will enable :option:`CONFIG_BT_SETTINGS`. + +Generic Power OnOff Server adds two model instances in the composition data: + +- The Generic Power OnOff Server +- The Generic Power OnOff Setup Server + +The two model instances share the states of the Generic Power OnOff Server, +but accept different messages. This allows fine-grained control of the access +rights for the Generic Power OnOff state, as the two model instances can be +bound to different application keys. + +The Generic Power OnOff Server only provides read access to the Generic +On Power Up state. + +The Generic Power OnOff Setup Server provides write access to the Generic +On Power Up state, allowing configurator devices to change the power up +behavior of the element. + +States +******* + +**Generic On Power Up**: :cpp:type:`bt_mesh_on_power_up`. + +The Generic On Power Up state controls the initial value of the extended +Generic OnOff state when the device powers up: + +- On Power Up is + :cpp:enumerator:`BT_MESH_ON_POWER_UP_OFF `: + The OnOff state is initialized to Off. +- On Power Up is + :cpp:enumerator:`BT_MESH_ON_POWER_UP_ON `: + The OnOff state is initialized to On. If any other states are bound to the On + Power Up state, they are initialized to their default values. +- On Power Up is + :cpp:enumerator:`BT_MESH_ON_POWER_UP_RESTORE `: + The OnOff state is initialized to its last known value. If any other states + are bound to the On Power Up state, they are initialized to their default + values. + +The memory for the Generic On Power Up state is contained in the model +structure, and state changes can be observed with the +:cpp:member:`bt_mesh_ponoff_srv::update` callback. + +Extended models +**************** + +The Generic Power OnOff Server extends the following models: + +- :ref:`bt_mesh_onoff_srv_readme` +- :ref:`bt_mesh_dtt_srv_readme` + +The On Power Up state is bound to the Generic OnOff state of the extended +Generic OnOff model through its power up behavior. No other state bindings +are present, and the callbacks for both the Generic OnOff server and the +Generic DTT server are forwarded to the application as they are. + +Persistent storage +******************* + +The Generic On Power Up state is stored persistently, along with the current +Generic OnOff state of the extended :ref:`bt_mesh_onoff_srv_readme`. + +API documentation +****************** + +| Header file: :file:`include/bluetooth/mesh/gen_ponoff_srv.h` +| Source file: :file:`subsys/bluetooth/mesh/gen_ponoff_srv.c` + +.. doxygengroup:: bt_mesh_ponoff_srv + :project: nrf + :members: + +---- + +.. _bt_mesh_ponoff_cli_readme: + +Generic Power OnOff Client +========================== + +The Generic Power OnOff Client model remotely controls the state of a Generic +Power OnOff Server model. + +Contrary to the Generic Power OnOff Server, the Generic Power OnOff Client only +adds one model instance to the composition data. The Generic Power OnOff Client +may send messages to both the Generic Power OnOff Server and the Generic Power +OnOff Setup server, as long as it has the right application keys. + +Extended models +**************** + +None. + +Persistent storage +******************* + +None. + +API documentation +****************** + +| Header file: :file:`include/bluetooth/mesh/gen_ponoff_cli.h` +| Source file: :file:`subsys/bluetooth/mesh/gen_ponoff_cli.c` + +.. doxygengroup:: bt_mesh_ponoff_cli + :project: nrf + :members: + +---- + +Common types +============= + +| Header file: :file:`include/bluetooth/mesh/gen_ponoff.h` + +.. doxygengroup:: bt_mesh_ponoff + :project: nrf + :members: diff --git a/include/bluetooth/mesh/gen_ponoff_cli.h b/include/bluetooth/mesh/gen_ponoff_cli.h new file mode 100644 index 000000000000..05e9ce3ba8ce --- /dev/null +++ b/include/bluetooth/mesh/gen_ponoff_cli.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +/** + * @file + * @defgroup bt_mesh_ponoff_cli Generic Power OnOff Client + * @{ + * @brief API for the Generic Power OnOff Client. + */ +#ifndef BT_MESH_GEN_PONOFF_CLI_H__ +#define BT_MESH_GEN_PONOFF_CLI_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_ponoff_cli; + +/** @def BT_MESH_PONOFF_CLI_INIT + * + * @brief Initialization parameters for @ref bt_mesh_ponoff_cli. + * + * @param[in] _power_onoff_status_handler OnPowerUp status handler. + */ +#define BT_MESH_PONOFF_CLI_INIT(_power_onoff_status_handler) \ + { \ + .status_handler = _power_onoff_status_handler, \ + .pub = { .msg = NET_BUF_SIMPLE(BT_MESH_MODEL_BUF_LEN( \ + BT_MESH_PONOFF_OP_SET, \ + BT_MESH_PONOFF_MSG_LEN_SET)) }, \ + } + +/** @def BT_MESH_MODEL_PONOFF_CLI + * + * @brief Generic Power OnOff Client model composition data entry. + * + * @param[in] _cli Pointer to a @ref bt_mesh_ponoff_cli instance. + */ +#define BT_MESH_MODEL_PONOFF_CLI(_cli) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_POWER_ONOFF_CLI, \ + _bt_mesh_ponoff_cli_op, &(_cli)->pub, \ + BT_MESH_MODEL_USER_DATA( \ + struct bt_mesh_ponoff_cli, _cli), \ + &_bt_mesh_ponoff_cli_cb) + +/** + * Generic Power OnOff client instance. + * + * Should be initialized with @ref BT_MESH_PONOFF_CLI_INIT. + */ +struct bt_mesh_ponoff_cli { + /** Model entry pointer. */ + struct bt_mesh_model *model; + /** Publish parameters. */ + struct bt_mesh_model_pub pub; + /** Response context for tracking acknowledged messages. */ + struct bt_mesh_model_ack_ctx ack_ctx; + + /** @brief OnPowerUp status message handler. + * + * @param[in] cli Client that received the status message. + * @param[in] ctx Message context the message was received with. + * @param[in] on_power_up The OnPowerUp state presented in the message. + */ + void (*const status_handler)(struct bt_mesh_ponoff_cli *cli, + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_on_power_up on_power_up); +}; + +/** @brief Get the OnPowerUp state of a server. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will return, and the response will be passed to the + * @ref bt_mesh_ponoff_cli::status_handler callback. + * + * @param[in] cli Power OnOff client to send the message on. + * @param[in] ctx Context of the message, or NULL to send on the configured + * publish parameters. + * @param[out] rsp Response buffer to put the received response in, or NULL to + * process the response in the status handler callback. + * + * @retval 0 Successfully sent a get message. If a response buffer is + * provided, it has been populated. + * @retval -EALREADY A blocking request is already in progress. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_ponoff_cli_on_power_up_get(struct bt_mesh_ponoff_cli *cli, + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_on_power_up *rsp); + +/** @brief Set the OnPowerUp state of a server. + * + * @param[in] cli Power OnOff client to send the message on. + * @param[in] ctx Context of the message, or NULL to send with the configured + * publish parameters. + * @param[in] on_power_up New OnPowerUp state of the server. + * @param[out] rsp Response buffer to put the received response in, or NULL to + * send an unacknowledged message. + * + * @retval 0 Successfully sent a set message. If a response buffer is + * provided, it has been populated. + * @retval -EALREADY A blocking request is already in progress. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_ponoff_cli_on_power_up_set(struct bt_mesh_ponoff_cli *cli, + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_on_power_up on_power_up, + enum bt_mesh_on_power_up *rsp); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op _bt_mesh_ponoff_cli_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_ponoff_cli_cb; +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_PONOFF_CLI_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/gen_ponoff_srv.h b/include/bluetooth/mesh/gen_ponoff_srv.h new file mode 100644 index 000000000000..322b2308f65f --- /dev/null +++ b/include/bluetooth/mesh/gen_ponoff_srv.h @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ +/** + * @file + * @defgroup bt_mesh_ponoff_srv Generic Power OnOff Server model + * @{ + * @brief API for the Generic Power OnOff Server. + */ + +#ifndef BT_MESH_GEN_PONOFF_SRV_H__ +#define BT_MESH_GEN_PONOFF_SRV_H__ + +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_ponoff_srv; + +/** @def BT_MESH_PONOFF_SRV_INIT + * + * @brief Initialization parameters for @ref bt_mesh_ponoff_srv. + * + * @param[in] _onoff_handlers Handlers for the underlying Generic OnOff Server. + * @param[in] _dtt_change_handler Handler function for changes to the Default + * Transition Time Server state. + * @param[in] _on_power_up_change_handler Handler function for changes to the + * OnPowerUp state. + */ +#define BT_MESH_PONOFF_SRV_INIT(_onoff_handlers, _dtt_change_handler, \ + _on_power_up_change_handler) \ + { \ + .onoff = BT_MESH_ONOFF_SRV_INIT( \ + &_bt_mesh_ponoff_onoff_intercept), \ + .dtt = BT_MESH_DTT_SRV_INIT(_dtt_change_handler), \ + .onoff_handlers = _onoff_handlers, \ + .pub = { .update = _bt_mesh_ponoff_srv_update_handler, \ + .msg = NET_BUF_SIMPLE(BT_MESH_MODEL_BUF_LEN( \ + BT_MESH_PONOFF_OP_STATUS, \ + BT_MESH_PONOFF_MSG_LEN_STATUS)) }, \ + .update = _on_power_up_change_handler, \ + } + +/** @def BT_MESH_MODEL_PONOFF_SRV + * + * @brief Generic Power OnOff model entry. + * + * @param[in] _srv Pointer to a @ref bt_mesh_ponoff_srv instance. + */ +#define BT_MESH_MODEL_PONOFF_SRV(_srv) \ + BT_MESH_MODEL_ONOFF_SRV(&(_srv)->onoff), \ + BT_MESH_MODEL_DTT_SRV(&(_srv)->dtt), \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_POWER_ONOFF_SRV, \ + _bt_mesh_ponoff_srv_op, &(_srv)->pub, \ + BT_MESH_MODEL_USER_DATA( \ + struct bt_mesh_ponoff_srv, _srv), \ + &_bt_mesh_ponoff_srv_cb), \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_POWER_ONOFF_SETUP_SRV, \ + _bt_mesh_ponoff_setup_srv_op, NULL, \ + BT_MESH_MODEL_USER_DATA( \ + struct bt_mesh_ponoff_srv, _srv), \ + NULL) + +/** + * Generic Power OnOff Server instance. + * + * Should be initialized with @ref BT_MESH_PONOFF_SRV_INIT. + */ +struct bt_mesh_ponoff_srv { + /** Generic OnOff Server instance. */ + struct bt_mesh_onoff_srv onoff; + /** Generic Default Transition Time server instance. */ + struct bt_mesh_dtt_srv dtt; + /** Pointer to the model entry in the composition data. */ + struct bt_mesh_model *ponoff_model; + /** Model publication parameters. */ + struct bt_mesh_model_pub pub; + /** Handlers for the Generic OnOff Server. */ + const struct bt_mesh_onoff_srv_handlers *const onoff_handlers; + + /** @brief Update handler. + * + * Called every time there's a change to the OnPowerUp state. + * + * @param[in] srv Server instance that was updated + * @param[in] ctx Context of the set message that triggered the update, + * or NULL if it was not triggered by a message. + * @param[in] old_on_power_up The previous OnPowerUp value. + * @param[in] new_on_power_up The new OnPowerUp value. + */ + void (*const update)(struct bt_mesh_ponoff_srv *srv, + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_on_power_up old_on_power_up, + enum bt_mesh_on_power_up new_on_power_up); + + /** Current OnPowerUp state. */ + enum bt_mesh_on_power_up on_power_up; +}; + +/** @brief Set the OnPowerUp state of a Power OnOff server. + * + * If an update handler is set, it'll be called with the updated OnPowerUp + * value. If publication is configured, the change will cause the server to + * publish. + * + * @param[in] srv Server to set the OnPowerUp state of. + * @param[in] on_power_up New OnPowerUp value. + */ +void bt_mesh_ponoff_srv_set(struct bt_mesh_ponoff_srv *srv, + enum bt_mesh_on_power_up on_power_up); + +/** @brief Publish the current OnPowerUp state. + * + * Publishes a Generic Power OnOff status message with the configured publish + * parameters, or using the given message context. + * + * @note This API is only used publishing unprompted status messages. Response + * messages for get and set messages are handled internally. + * + * @param[in] srv Server instance to publish with. + * @param[in] ctx Message context, or NULL to publish with the configured + * parameters. + * + * @return 0 Successfully published the current OnPowerUp state. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + */ +int bt_mesh_ponoff_srv_pub(struct bt_mesh_ponoff_srv *srv, + struct bt_mesh_msg_ctx *ctx); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_cb _bt_mesh_ponoff_srv_cb; +extern const struct bt_mesh_model_op _bt_mesh_ponoff_srv_op[]; +extern const struct bt_mesh_model_op _bt_mesh_ponoff_setup_srv_op[]; +extern const struct bt_mesh_onoff_srv_handlers _bt_mesh_ponoff_onoff_intercept; +int _bt_mesh_ponoff_srv_update_handler(struct bt_mesh_model *model); +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_PONOFF_SRV_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/gen_prop.h b/include/bluetooth/mesh/gen_prop.h new file mode 100644 index 000000000000..15aeb109c49f --- /dev/null +++ b/include/bluetooth/mesh/gen_prop.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +/** + * @file + * @defgroup bt_mesh_prop Generic Property models + * @{ + * @brief API for the Generic Property models. + */ + +#ifndef BT_MESH_GEN_PROP_H__ +#define BT_MESH_GEN_PROP_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @cond INTERNAL_HIDDEN */ +#define BT_MESH_PROP_OP_MFR_PROPS_GET BT_MESH_MODEL_OP_2(0x82, 0x2A) +#define BT_MESH_PROP_OP_MFR_PROPS_STATUS BT_MESH_MODEL_OP_1(0x43) +#define BT_MESH_PROP_OP_MFR_PROP_GET BT_MESH_MODEL_OP_2(0x82, 0x2B) +#define BT_MESH_PROP_OP_MFR_PROP_SET BT_MESH_MODEL_OP_1(0x44) +#define BT_MESH_PROP_OP_MFR_PROP_SET_UNACK BT_MESH_MODEL_OP_1(0x45) +#define BT_MESH_PROP_OP_MFR_PROP_STATUS BT_MESH_MODEL_OP_1(0x46) +#define BT_MESH_PROP_OP_ADMIN_PROPS_GET BT_MESH_MODEL_OP_2(0x82, 0x2C) +#define BT_MESH_PROP_OP_ADMIN_PROPS_STATUS BT_MESH_MODEL_OP_1(0x47) +#define BT_MESH_PROP_OP_ADMIN_PROP_GET BT_MESH_MODEL_OP_2(0x82, 0x2D) +#define BT_MESH_PROP_OP_ADMIN_PROP_SET BT_MESH_MODEL_OP_1(0x48) +#define BT_MESH_PROP_OP_ADMIN_PROP_SET_UNACK BT_MESH_MODEL_OP_1(0x49) +#define BT_MESH_PROP_OP_ADMIN_PROP_STATUS BT_MESH_MODEL_OP_1(0x4A) +#define BT_MESH_PROP_OP_USER_PROPS_GET BT_MESH_MODEL_OP_2(0x82, 0x2E) +#define BT_MESH_PROP_OP_USER_PROPS_STATUS BT_MESH_MODEL_OP_1(0x4B) +#define BT_MESH_PROP_OP_USER_PROP_GET BT_MESH_MODEL_OP_2(0x82, 0x2F) +#define BT_MESH_PROP_OP_USER_PROP_SET BT_MESH_MODEL_OP_1(0x4C) +#define BT_MESH_PROP_OP_USER_PROP_SET_UNACK BT_MESH_MODEL_OP_1(0x4D) +#define BT_MESH_PROP_OP_USER_PROP_STATUS BT_MESH_MODEL_OP_1(0x4E) +#define BT_MESH_PROP_OP_CLIENT_PROPS_GET BT_MESH_MODEL_OP_1(0x4F) +#define BT_MESH_PROP_OP_CLIENT_PROPS_STATUS BT_MESH_MODEL_OP_1(0x50) + +#define BT_MESH_PROP_MSG_LEN_PROPS_GET 0 +#define BT_MESH_PROP_MSG_MINLEN_PROPS_STATUS 0 +#define BT_MESH_PROP_MSG_MAXLEN_PROPS_STATUS (2 * CONFIG_BT_MESH_PROP_MAXCOUNT) +#define BT_MESH_PROP_MSG_LEN_PROP_GET 2 +#define BT_MESH_PROP_MSG_MINLEN_PROP_STATUS 3 +#define BT_MESH_PROP_MSG_MAXLEN_PROP_STATUS (3 + CONFIG_BT_MESH_PROP_MAXSIZE) +#define BT_MESH_PROP_MSG_LEN_MFR_PROP_SET 3 +#define BT_MESH_PROP_MSG_MINLEN_ADMIN_PROP_SET 3 +#define BT_MESH_PROP_MSG_MAXLEN_ADMIN_PROP_SET (3 + CONFIG_BT_MESH_PROP_MAXSIZE) +#define BT_MESH_PROP_MSG_MINLEN_USER_PROP_SET 2 +#define BT_MESH_PROP_MSG_MAXLEN_USER_PROP_SET (2 + CONFIG_BT_MESH_PROP_MAXSIZE) +/** @endcond */ + +/** Access flags for properties */ +enum bt_mesh_prop_access { + /** Access to the property is prohibited. */ + BT_MESH_PROP_ACCESS_PROHIBITED = 0, + /** Property may be read. */ + BT_MESH_PROP_ACCESS_READ = BIT(0), + /** Property may be written. */ + BT_MESH_PROP_ACCESS_WRITE = BIT(1), + /** Property may be read or written. */ + BT_MESH_PROP_ACCESS_READ_WRITE = + (BT_MESH_PROP_ACCESS_READ | BT_MESH_PROP_ACCESS_WRITE), +}; + +/** Property Server kinds. */ +enum bt_mesh_prop_srv_kind { + /** Manufacturer property server. */ + BT_MESH_PROP_SRV_KIND_MFR, + /** Admin property server. */ + BT_MESH_PROP_SRV_KIND_ADMIN, + /** User property server. */ + BT_MESH_PROP_SRV_KIND_USER, + /** Client property server. */ + BT_MESH_PROP_SRV_KIND_CLIENT, +}; + +/** Property representation. */ +struct bt_mesh_prop { + /** Property ID. @sa bt_mesh_property_ids. */ + u16_t id; + /** User access flags for the property. */ + enum bt_mesh_prop_access user_access; +}; + +/** Property value representation. */ +struct bt_mesh_prop_val { + struct bt_mesh_prop meta; /**< Metadata for this property. */ + size_t size; /**< Size of the property value. */ + u8_t *value; /**< Property value. */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_PROP_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/gen_prop.rst b/include/bluetooth/mesh/gen_prop.rst new file mode 100644 index 000000000000..c0290e20caab --- /dev/null +++ b/include/bluetooth/mesh/gen_prop.rst @@ -0,0 +1,150 @@ +.. _bt_mesh_prop_readme: + +Generic Property models +####################### + +The Generic Property models allow remote access to the Device Properties of a +mesh node. Read more about device properties in +:ref:`bt_mesh_properties_readme`. + +There Generic Property models fall into two categories: + +- :ref:`bt_mesh_prop_srv_readme` +- :ref:`bt_mesh_prop_cli_readme` + +Configuration +============== + +There are two configuration parameters associated with the Generic Property +models: + +- :option:`CONFIG_BT_MESH_PROP_MAXSIZE`: The largest available property value. +- :option:`CONFIG_BT_MESH_PROP_MAXCOUNT`: The largest number of properties + available on a single Generic Property Server. + +.. _bt_mesh_prop_srv_readme: + +Generic Property Servers +======================== + +A Generic Property Server holds a list of Device Properties. +There are four different property servers: + +- **Generic Manufacturer Property Server**: Provides access to Manufacturer + properties, which are read-only properties set by the device manufacturer. +- **Generic Admin Property Server**: Provides access to Admin properties, which + can be read and written to. +- **Generic User Property Server**: Provides access to both Admin and + Manufacturer properties on its element, but only if the Property owner allows + it. +- **Generic Client Property Server**: Provides a list of all properties + supported by the Generic Property Client on the same element. The Client + Property Server does not actually own any properties, and does not need the + user to provide access to any property values. + +Typically, only the administrator of the mesh network should have access to the +Admin or Manufacturer servers, all other mesh nodes should operate on the User +server. The Manufacturer and Admin servers may change the User property access +rights for each individual Property it owns at runtime. + +The Manufacturer and Admin Servers must provide a list of all Properties they +own in the initialization of the model. Access to the property values should be +provided through the server getter (:cpp:member:`bt_mesh_prop_srv::get`) and +setter (:cpp:member:`bt_mesh_prop_srv::set`) callbacks. + +The User Property server does not own any properties, but relies on Admin and +Manufacturer servers on the same element to provide access to theirs. An +element with a User Property Server and no Admin or Manufacturer Servers is +useless. + +The model keeps the user access field of each Admin and Manufacturer property +in persistent storage. The property values themselves have to be stored by the +application. + +The set of Property IDs and their order cannot be changed. + +States +******* + +**Device Properties**: :cpp:type:`bt_mesh_prop` + +A single server may own several Device Properties, which are accessed by their +Property ID. Each Property holds a value and a user access parameter, which +controls the Property's availability to a Generic User Property Server on the +same element. + +The Device properties have slightly different access restrictions depending on +the type of server that owns them: + +- **Manufacturer Property**: The value can only be read. +- **Admin Property**: The value can be read and written. +- **Client Property**: Has no associated value, and is only a listing of the + property IDs supported by the accompanying Generic Property Client. + +Extended models +**************** + +The Generic Admin Property Server and Generic Manufacturer Property Server both +extend the Generic User Property Server. As a consequence, a single element can +only hold one Generic Manufacturer or Generic Admin Property Server. + +Persistent storage +******************* + +The Generic Manufacturer Property Server and Generic Admin Property Server +models will store changes to the user access rights of their properties. +Any permanent changes to the property values themselves should be stored +manually by the application. + +API documentation +****************** + +| Header file: :file:`include/bluetooth/mesh/gen_prop_srv.h` +| Source file: :file:`subsys/bluetooth/mesh/gen_prop_srv.c` + +.. doxygengroup:: bt_mesh_prop_srv + :project: nrf + :members: + +---- + +.. _bt_mesh_prop_cli_readme: + +Generic Property Client +======================= + +The Generic Property Client model can access properties from a Property Server +remotely. The Property Client can talk directly to all types of Property +Servers, but only if it shares an application key with the target server. + +Generally, the Property Client should only target User Property Servers, unless +it is part of some network administrator node that is responsible for +configuring the other mesh nodes. + +To ease configuration, the Property Client can be paired with a Client Property +Server that lists which properties this Client will request. + +Extended models +**************** + +None. + +API documentation +****************** + +| Header file: :file:`include/bluetooth/mesh/gen_prop_cli.h` +| Source file: :file:`subsys/bluetooth/mesh/gen_prop_cli.c` + +.. doxygengroup:: bt_mesh_prop_cli + :project: nrf + :members: + +---- + +Common types +============= + +| Header file: :file:`include/bluetooth/mesh/gen_prop.h` + +.. doxygenfile:: gen_prop.h + :project: nrf diff --git a/include/bluetooth/mesh/gen_prop_cli.h b/include/bluetooth/mesh/gen_prop_cli.h new file mode 100644 index 000000000000..0db7fd31b9e9 --- /dev/null +++ b/include/bluetooth/mesh/gen_prop_cli.h @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ +/** + * @file + * @defgroup bt_mesh_prop_cli Generic Property Client model + * @{ + * @brief API for the Generic Property Client model. + */ +#ifndef BT_MESH_GEN_PROP_CLI_H__ +#define BT_MESH_GEN_PROP_CLI_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_prop_cli; + +/** @def BT_MESH_PROP_CLI_INIT + * + * @brief Initialization parameters for the @ref bt_mesh_prop_cli. + * + * @param[in] _prop_list_handler Optional status message handler. + * @sa bt_mesh_prop_cli::prop_list + * @param[in] _prop_status_handler Optional status message handler. + * @sa bt_mesh_prop_cli::prop_status + */ +#define BT_MESH_PROP_CLI_INIT(_prop_list_handler, _prop_status_handler) \ + { \ + .pub = { .msg = NET_BUF_SIMPLE(BT_MESH_MODEL_BUF_LEN( \ + BT_MESH_PROP_OP_ADMIN_PROP_SET, \ + BT_MESH_PROP_MSG_MAXLEN_ADMIN_PROP_SET)) }, \ + .prop_list = _prop_list_handler, \ + .prop_status = _prop_status_handler, \ + } + +/** @def BT_MESH_MODEL_PROP_CLI + * + * @brief Generic Property Client model composition data entry. + * + * @param[in] _cli Pointer to a @ref bt_mesh_prop_cli instance. + */ +#define BT_MESH_MODEL_PROP_CLI(_cli) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_PROP_CLI, _bt_mesh_prop_cli_op, \ + &(_cli)->pub, \ + BT_MESH_MODEL_USER_DATA(struct bt_mesh_prop_cli, \ + _cli), \ + &_bt_mesh_prop_cli_cb) + +/** List of property IDs. */ +struct bt_mesh_prop_list { + u8_t count; /**< Number of IDs in the list. */ + u16_t *ids; /**< Available Property IDs. */ +}; + +/** + * Generic Property Client instance. + * Should be initialized with @ref BT_MESH_PROP_CLI_INIT. + */ +struct bt_mesh_prop_cli { + /** Model entry. */ + struct bt_mesh_model *model; + /** Publish parameters. */ + struct bt_mesh_model_pub pub; + /** Acknowledged message tracking. */ + struct bt_mesh_model_ack_ctx ack_ctx; + + /** @brief Property list message handler. + * + * @param[in] cli Client that received the message. + * @param[in] ctx Context of the message. + * @param[in] kind Kind of Property Server that sent the message. + * @param[in] list List of properties supported by the server. + */ + void (*const prop_list)(struct bt_mesh_prop_cli *cli, + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_prop_srv_kind kind, + const struct bt_mesh_prop_list *list); + + /** @brief Property status message handler. + * + * @param[in] cli Client that received the message. + * @param[in] ctx Context of the message. + * @param[in] kind Kind of Property Server that sent the message. + * @param[in] prop Property value. + */ + void (*const prop_status)(struct bt_mesh_prop_cli *cli, + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_prop_srv_kind kind, + const struct bt_mesh_prop_val *prop); +}; + +/** @brief Get the list of properties of the bound server. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will return, and the response will be passed to the + * @ref bt_mesh_prop_cli::prop_list callback. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[in] kind Kind of Property Server to query. + * @param[out] rsp Response buffer, or NULL to keep from blocking. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EINVAL The @p rsp::ids list was NULL. + * @retval -ENOBUFS The client received a response, but the supplied response + * buffer was too small to hold all the properties. All property IDs that could + * fit in the response buffers were copied into it, and the @p rsp::count + * field was left unchanged. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_prop_cli_props_get(struct bt_mesh_prop_cli *cli, + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_prop_srv_kind kind, + struct bt_mesh_prop_list *rsp); + +/** @brief Get the value of a property in a server. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will return, and the response will be passed to the + * @ref bt_mesh_prop_cli::prop_status callback. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[in] kind Kind of Property Server to query. + * @param[in] id ID of the property to get. + * @param[out] rsp Response buffer, or NULL to keep from blocking. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EINVAL The @p rsp::ids list was NULL. + * @retval -ENOBUFS The client received a response, but the supplied response + * buffer was too small to hold all the properties. All property IDs that could + * fit in the response buffers were copied into it, and the @p rsp::count + * field was left unchanged. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_prop_cli_prop_get(struct bt_mesh_prop_cli *cli, + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_prop_srv_kind kind, u16_t id, + struct bt_mesh_prop_val *rsp); + +/** @brief Set a property value in a User Property Server. + * + * The User Property may only be set if the server enabled user write access to + * it. If this is not the case, the server will only respond with the set user + * access mode for the given property. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will not request a response from the server, and return + * immediately. + * + * @note The @p val::meta::user_access level will be ignored. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[in] val New property value to set. Note that the user_access mode + * will be ignored. + * @param[out] rsp Response status buffer, or NULL to send an unacknowledged + * message. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_prop_cli_user_prop_set(struct bt_mesh_prop_cli *cli, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_prop_val *val, + struct bt_mesh_prop_val *rsp); + +/** @brief Set a property value in an Admin Property server. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will not request a response from the server, and return + * immediately. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[in] val New property value to set. + * @param[out] rsp Response status buffer, or NULL to send an unacknowledged + * message. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_prop_cli_admin_prop_set(struct bt_mesh_prop_cli *cli, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_prop_val *val, + struct bt_mesh_prop_val *rsp); + +/** @brief Set the user access of a property in a Manufacturer Property server. + * + * This call is blocking if the @p rsp buffer is non-NULL. Otherwise, this + * function will not request a response from the server, and return + * immediately. + * + * @param[in] cli Client model to send on. + * @param[in] ctx Message context, or NULL to use the configured publish + * parameters. + * @param[in] val New property value to set. + * @param[out] rsp Response status buffer, or NULL to send an unacknowledged + * message. + * + * @retval 0 Successfully sent the message and populated the @p rsp buffer. + * @retval -EALREADY A blocking request is already in progress. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + * @retval -ETIMEDOUT The request timed out without a response. + */ +int bt_mesh_prop_cli_mfr_prop_set(struct bt_mesh_prop_cli *cli, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_prop *prop, + struct bt_mesh_prop_val *rsp); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op _bt_mesh_prop_cli_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_prop_cli_cb; +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_PROP_CLI_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/gen_prop_srv.h b/include/bluetooth/mesh/gen_prop_srv.h new file mode 100644 index 000000000000..737b88ce487a --- /dev/null +++ b/include/bluetooth/mesh/gen_prop_srv.h @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +/** + * @file + * @defgroup bt_mesh_prop_srv Generic Property server models + * @{ + * @brief API for the Generic Property server models. + */ +#ifndef BT_MESH_GEN_PROP_SRV_H__ +#define BT_MESH_GEN_PROP_SRV_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_prop_srv; + +/** @def BT_MESH_PROP_SRV_INIT + * + * @brief Initialization parameters for a @ref bt_mesh_prop_srv instance. + * + * @param[in] _properties Array of properties supported by the server. + * @param[in] _property_count Number of properties supported by the server. + * @param[in] _get Getter handler for property values. @sa + * bt_mesh_prop_srv::get. + * @param[in] _set Setter handler for property values. @sa + * bt_mesh_prop_srv::set. + */ +#define BT_MESH_PROP_SRV_INIT(_properties, _property_count, _get, _set) \ + { \ + .get = _get, .set = _set, .properties = _properties, \ + .property_count = _property_count, \ + .pub = { \ + .update = _bt_mesh_prop_srv_update_handler, \ + .msg = NET_BUF_SIMPLE(MAX( \ + BT_MESH_MODEL_BUF_LEN( \ + BT_MESH_PROP_OP_MFR_PROP_STATUS, \ + BT_MESH_PROP_MSG_MAXLEN_PROP_STATUS), \ + BT_MESH_MODEL_BUF_LEN( \ + BT_MESH_PROP_OP_MFR_PROPS_STATUS, \ + 2 * _property_count))), \ + }, \ + } + +/** @def BT_MESH_PROP_SRV_ADMIN_INIT + * + * @brief Initialization parameters for a @ref bt_mesh_prop_srv acting as a + * Generic Admin Property Server. + * + * @param[in] _properties Array of properties exposed by the server. + * @param[in] _get Getter function for property values. + * @param[in] _set Setter function for property values. + */ +#define BT_MESH_PROP_SRV_ADMIN_INIT(_properties, _get, _set) \ + BT_MESH_PROP_SRV_INIT(_properties, ARRAY_SIZE(_properties), _get, _set) + +/** @def BT_MESH_PROP_SRV_MFR_INIT + * + * @brief Initialization parameters for a @ref bt_mesh_prop_srv acting as a + * Generic Manufacturer Property Server. + * + * @param[in] _properties Array of properties exposed by the server. + * @param[in] _get Getter function for property values. + */ +#define BT_MESH_PROP_SRV_MFR_INIT(_properties, _get) \ + BT_MESH_PROP_SRV_INIT(_properties, ARRAY_SIZE(_properties), _get, NULL) + +/** @def BT_MESH_PROP_SRV_CLIENT_INIT + * + * @brief Initialization parameters for a @ref bt_mesh_prop_srv acting as a + * Generic Client Property Server. + * + * @param[in] _properties Array of properties exposed by the server. May be + * constant. + */ +#define BT_MESH_PROP_SRV_CLIENT_INIT(_properties) \ + BT_MESH_PROP_SRV_INIT((struct bt_mesh_prop *)_properties, \ + ARRAY_SIZE(_properties), NULL, NULL) + +/** @def BT_MESH_MODEL_PROP_SRV_USER + * + * @brief Generic User Property Server model composition data entry. + */ +#define BT_MESH_MODEL_PROP_SRV_USER \ + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_USER_PROP_SRV, \ + _bt_mesh_prop_user_srv_op, NULL, NULL) + +/** @def BT_MESH_MODEL_PROP_SRV_ADMIN + * + * @brief Generic Admin Property Server model composition data entry. + * + * @param[in] _srv Pointer to a @ref bt_mesh_prop_srv instance acting as an + * Admin Property Server. + */ +#define BT_MESH_MODEL_PROP_SRV_ADMIN(_srv) \ + BT_MESH_MODEL_PROP_SRV_USER, \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_ADMIN_PROP_SRV, \ + _bt_mesh_prop_admin_srv_op, &(_srv)->pub, \ + BT_MESH_MODEL_USER_DATA( \ + struct bt_mesh_prop_srv, _srv), \ + &_bt_mesh_prop_srv_cb) + +/** @def BT_MESH_MODEL_PROP_SRV_MFR + * + * @brief Generic Manufacturer Property Server model data entry. + * + * @param[in] _srv Pointer to a @ref bt_mesh_prop_srv instance acting as a + * Manufacturer Property Server. + */ +#define BT_MESH_MODEL_PROP_SRV_MFR(_srv) \ + BT_MESH_MODEL_PROP_SRV_USER, \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_MANUFACTURER_PROP_SRV, \ + _bt_mesh_prop_mfr_srv_op, &(_srv)->pub, \ + BT_MESH_MODEL_USER_DATA( \ + struct bt_mesh_prop_srv, _srv), \ + &_bt_mesh_prop_srv_cb) + +/** @def BT_MESH_MODEL_PROP_SRV_CLIENT + * + * @brief Generic Client Property Server model data entry. + * + * @param[in] _srv Pointer to a @ref bt_mesh_prop_srv instance acting as a + * Client Property Server. + */ +#define BT_MESH_MODEL_PROP_SRV_CLIENT(_srv) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_CLIENT_PROP_SRV, \ + _bt_mesh_prop_client_srv_op, &(_srv)->pub, \ + BT_MESH_MODEL_USER_DATA(struct bt_mesh_prop_srv, \ + _srv), \ + &_bt_mesh_prop_srv_cb) + +/** Enumeration of all property server states. */ +enum bt_mesh_prop_srv_state { + BT_MESH_PROP_SRV_STATE_NONE, /**< No state. */ + BT_MESH_PROP_SRV_STATE_LIST, /**< Property List. */ + BT_MESH_PROP_SRV_STATE_PROP, /**< Property value. */ +}; + +/** + * Generic Property Server instance. Should be initialized with the + * @ref BT_MESH_PROP_SRV_INIT, @ref BT_MESH_PROP_SRV_ADMIN_INIT, + * @ref BT_MESH_PROP_SRV_MFR_INIT or @ref BT_MESH_PROP_SRV_CLIENT_INIT macro. + */ +struct bt_mesh_prop_srv { + /** Pointer to the mesh model entry. */ + struct bt_mesh_model *mod; + /** Model publication parameters. */ + struct bt_mesh_model_pub pub; + /** Property ID currently being published. */ + u16_t pub_id; + /** Which state is currently being published. */ + enum bt_mesh_prop_srv_state pub_state; + + /** List of properties supported by the server. */ + struct bt_mesh_prop *const properties; + /** Number of properties supported by the server. */ + const u32_t property_count; + + /** @brief Set a property value. + * + * The handler may reject the value change by replacing the contents of + * @p buf and @p size with the current property value and size. Note + * that @p buf can only fit a value of size @c CONFIG_GEN_PROP_MAXSIZE. + * + * @note This handler is mandatory if the server is acting as an Admin + * Property Server, and ignored if the server is acting as a + * Manufacturer Property Server. + * + * @param[in] srv Property Server instance whose property to set. + * @param[in] ctx Message context for the message that triggered the + * change, or NULL if the change is not coming from a messge. + * @param[in,out] val Property value to set. Any changes to the value + * will be reflected in the response message. + */ + void (*const set)(struct bt_mesh_prop_srv *srv, + struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_prop_val *val); + + /** @brief Get a property value. + * + * Note that @p buf can only fit a value of size + * @c CONFIG_GEN_PROP_MAXSIZE. + * + * @note This handler is mandatory. + * + * @param[in] srv Property Server instance whose property to set. + * @param[in] ctx Message context for the message that triggered the + * change, or NULL if the change is not coming from a messge. + * @param[in,out] val Property value to get. Any changes to the value + * will be reflected in the response message. + */ + void (*const get)(struct bt_mesh_prop_srv *srv, + struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_prop_val *val); +}; + +/** @brief Publish a list of all properties on the server. + * + * @param[in] srv Server that owns the property. + * @param[in] ctx Message context to publish with, or NULL to publish on the + * configured publish parameters. + * + * @retval 0 Successfully publish a Generic Level Status message. + * @retval -EMSGSIZE The given property size is not supported. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + */ +int bt_mesh_prop_srv_pub_list(struct bt_mesh_prop_srv *srv, + struct bt_mesh_msg_ctx *ctx); + +/** @brief Publish a property value. + * + * @param[in] srv Server that owns the property. + * @param[in] ctx Message context to publish with, or NULL to publish on the + * configured publish parameters. + * @param[in] prop Property to publish. + * @param[in] value Value of the property. + * + * @retval 0 Successfully publish a Generic Level Status message. + * @retval -EINVAL The server is a Client Property server, which does not + * support publishing of property values. + * @retval -EMSGSIZE The given property size is not supported. + * @retval -ENOTSUP A message context was not provided and publishing is not + * supported. + * @retval -EADDRNOTAVAIL A message context was not provided and publishing is + * not configured. + * @retval -EAGAIN The device has not been provisioned. + */ +int bt_mesh_prop_srv_pub(struct bt_mesh_prop_srv *srv, + struct bt_mesh_msg_ctx *ctx, + const struct bt_mesh_prop_val *val); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_cb _bt_mesh_prop_srv_cb; +extern const struct bt_mesh_model_op _bt_mesh_prop_user_srv_op[]; +extern const struct bt_mesh_model_op _bt_mesh_prop_admin_srv_op[]; +extern const struct bt_mesh_model_op _bt_mesh_prop_mfr_srv_op[]; +extern const struct bt_mesh_model_op _bt_mesh_prop_client_srv_op[]; +int _bt_mesh_prop_srv_update_handler(struct bt_mesh_model *mod); +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_GEN_PROP_SRV_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/model_types.h b/include/bluetooth/mesh/model_types.h new file mode 100644 index 000000000000..41865328bbd7 --- /dev/null +++ b/include/bluetooth/mesh/model_types.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ +/** + * @file + * @defgroup bt_mesh_model_types Common model types + * @{ + * @brief Collection of types exposed in the model definitions. + */ + +#ifndef BT_MESH_MODEL_TYPES_H__ +#define BT_MESH_MODEL_TYPES_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Maximum permissible transition time in milliseconds */ +#define BT_MESH_MODEL_TRANSITION_TIME_MAX_MS (K_MINUTES(10) * (0x3e)) + +/** Generic Transition parameters for the model messages. */ +struct bt_mesh_model_transition { + u32_t time; /**< Transition time value in milliseconds */ + u32_t delay; /**< Message execution delay in milliseconds */ +}; + +/** + * Transaction ID context, storing information about the previous + * transaction in model spec messages. + */ +struct bt_mesh_tid_ctx { + u16_t src; /**< Source address. */ + u16_t dst; /**< Destination address. */ + u64_t timestamp; /**< System uptime of the transaction. */ + u8_t tid; /**< Transaction ID. */ +}; + +/** + * Acknowledged message context for tracking the status of model messages + * pending a response. + */ +struct bt_mesh_model_ack_ctx { + struct k_sem sem; /**< Sync semaphore. */ + u32_t op; /**< Opcode we're waiting for. */ + u16_t dst; /**< Address of the node that should respond. */ + void *user_data; /**< User specific parameter. */ +}; + +/** Model status values. */ +enum bt_mesh_model_status { + /** Command successfully processed. */ + BT_MESH_MODEL_SUCCESS, + /** The provided value for range min cannot be set. */ + BT_MESH_MODEL_ERROR_INVALID_RANGE_MIN, + /** The provided value for range max cannot be set. */ + BT_MESH_MODEL_ERROR_INVALID_RANGE_MAX, + /** Invalid status code. */ + BT_MESH_MODEL_STATUS_INVALID, +}; + +/** @cond INTERNAL_HIDDEN + * @def BT_MESH_MODEL_USER_DATA + * + * @brief Internal utility macro for type checking model user data. + * + * Produces a "Comparison of distinct pointer types" warning if @p _user_data + * is of the wrong type. + * + * This macro abuses the C language a bit, but when used in the + * @c BT_MESH_MODEL_ macros, it generates a compiler warning for a bug that is + * otherwise very hard to detect, and relatively easy to make: + * + * As the @ref bt_mesh_model::user_data is a void pointer, it does not have + * any type checking. The Mesh model implementations wrap this macro, often + * taking a pointer parameter to a context structure, passing it to the model + * user data. As the @c BT_MESH_MODEL_ macros are used in listing of models, + * users are likely to copy and paste them, but only change the suffix. + * If they do this, but fail to change the context pointer, the resulting + * misbehavior will be confusing, dangerous and potentially hard to detect. + * + * @param[in] _type Expected type of the user data. + * @param[in] _user_data User data pointer. + */ +#define BT_MESH_MODEL_USER_DATA(_type, _user_data) \ + (((_user_data) == (_type *)0) ? NULL : (_user_data)) +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_MODEL_TYPES_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/models.h b/include/bluetooth/mesh/models.h new file mode 100644 index 000000000000..f74be0be5043 --- /dev/null +++ b/include/bluetooth/mesh/models.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +#ifndef BT_MESH_MODELS_H__ +#define BT_MESH_MODELS_H__ + +#include + +#include + +/* Foundation models */ +#include +#include +#include +#include + +/* Generic models */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* BT_MESH_MODELS_H__ */ diff --git a/include/bluetooth/mesh/models.rst b/include/bluetooth/mesh/models.rst new file mode 100644 index 000000000000..f392fca49130 --- /dev/null +++ b/include/bluetooth/mesh/models.rst @@ -0,0 +1,32 @@ +.. _bt_mesh_models: + +Bluetooth Mesh Models +###################### + +Nordic Semiconductor provides a variety of model implementations from the Mesh +Model Specification. + +Here you can find documentation for these model implementations, including API +documentation. + +.. toctree:: + :maxdepth: 1 + :caption: Model implementations: + :glob: + + ../../../include/bluetooth/mesh/gen_* + +Common model types +=================== + +Models in the Bluetooth Mesh Model Specification share some common types, that +are collected in a single header file. + +API documentation +****************** + +| Header file: :file:`include/bluetooth/mesh/model_types.h` + +.. doxygengroup:: bt_mesh_model_types + :project: nrf + :members: diff --git a/include/bluetooth/mesh/properties.h b/include/bluetooth/mesh/properties.h new file mode 100644 index 000000000000..e6f6899ad21b --- /dev/null +++ b/include/bluetooth/mesh/properties.h @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ +/** + * @file + * @defgroup bt_mesh_properties Bluetooth Mesh Properties + * @{ + * @brief Device properties definitions. + */ +#ifndef BT_MESH_PROPERTIES_H__ +#define BT_MESH_PROPERTIES_H__ + +#include + +/** + * @defgroup bt_mesh_property_ids Property IDs + * All available Mesh Property IDs. See the Bluetooth Mesh Device Properties + * Specification for details. + * @{ + */ +/** Prohibited. */ +#define BT_MESH_PROP_ID_PROHIBITED 0x0000 +/** Average ambient temperature in a period of day. */ +#define BT_MESH_PROP_ID_AVG_AMB_TEMP_IN_A_PERIOD_OF_DAY 0x0001 +/** Average input current. */ +#define BT_MESH_PROP_ID_AVG_INPUT_CURRENT 0x0002 +/** Average input voltage. */ +#define BT_MESH_PROP_ID_AVG_INPUT_VOLTAGE 0x0003 +/** Average output current. */ +#define BT_MESH_PROP_ID_AVG_OUTPUT_CURRENT 0x0004 +/** Average output voltage. */ +#define BT_MESH_PROP_ID_AVG_OUTPUT_VOLTAGE 0x0005 +/** Center beam intensity at full power. */ +#define BT_MESH_PROP_ID_CENTER_BEAM_INTENSITY_AT_FULL_POWER 0x0006 +/** Chromaticity tolerance. */ +#define BT_MESH_PROP_ID_CHROMATICITY_TOLERANCE 0x0007 +/** Color rendering index R9. */ +#define BT_MESH_PROP_ID_COL_RENDERING_INDEX_R9 0x0008 +/** Color rendering index RA. */ +#define BT_MESH_PROP_ID_COL_RENDERING_INDEX_RA 0x0009 +/** Device appearance. */ +#define BT_MESH_PROP_ID_DEV_APPEARANCE 0x000A +/** Device country of origin. */ +#define BT_MESH_PROP_ID_DEV_COUNTRY_OF_ORIGIN 0x000B +/** Device date of manufacture. */ +#define BT_MESH_PROP_ID_DEV_DATE_OF_MANUFACTURE 0x000C +/** Device energy use since turn on. */ +#define BT_MESH_PROP_ID_DEV_ENERGY_USE_SINCE_TURN_ON 0x000D +/** Device firmware revision. */ +#define BT_MESH_PROP_ID_DEV_FW_REVISION 0x000E +/** Device global trade item number. */ +#define BT_MESH_PROP_ID_DEV_GLOBAL_TRADE_ITEM_NUM 0x000F +/** Device hardware revision. */ +#define BT_MESH_PROP_ID_DEV_HW_REVISION 0x0010 +/** Device manufacturer name. */ +#define BT_MESH_PROP_ID_DEV_MFR_NAME 0x0011 +/** Device model number. */ +#define BT_MESH_PROP_ID_DEV_MODEL_NUM 0x0012 +/** Device operating temperature range specification. */ +#define BT_MESH_PROP_ID_DEV_OP_TEMP_RANGE_SPEC 0x0013 +/** Device operating temperature statistical values. */ +#define BT_MESH_PROP_ID_DEV_OP_TEMP_STAT_VALUES 0x0014 +/** Device over temperature event statistics. */ +#define BT_MESH_PROP_ID_DEV_OVER_TEMP_EVT_STAT 0x0015 +/** Device power range specification. */ +#define BT_MESH_PROP_ID_DEV_POWER_RANGE_SPEC 0x0016 +/** Device runtime since turn on. */ +#define BT_MESH_PROP_ID_DEV_RUNTIME_SINCE_TURN_ON 0x0017 +/** Device runtime warranty. */ +#define BT_MESH_PROP_ID_DEV_RUNTIME_WARRANTY 0x0018 +/** Device serial number. */ +#define BT_MESH_PROP_ID_DEV_SERIAL_NUM 0x0019 +/** Device software revision. */ +#define BT_MESH_PROP_ID_DEV_SW_REVISION 0x001A +/** Device under temperature event statistics. */ +#define BT_MESH_PROP_ID_DEV_UNDER_TEMP_EVT_STAT 0x001B +/** Indoor ambient temperature statistical values. */ +#define BT_MESH_PROP_ID_INDOOR_AMB_TEMP_STAT_VALUES 0x001C +/** Initial CIE-1931 chromaticity coordinates. */ +#define BT_MESH_PROP_ID_INITIAL_CIE_1931_CHROMATICITY_COORDS 0x001D +/** Initial correlated color temperature. */ +#define BT_MESH_PROP_ID_INITIAL_CORRELATED_COL_TEMP 0x001E +/** Initial luminous flux. */ +#define BT_MESH_PROP_ID_INITIAL_LUMINOUS_FLUX 0x001F +/** Initial planckian distance. */ +#define BT_MESH_PROP_ID_INITIAL_PLANCKIAN_DISTANCE 0x0020 +/** Input current range specification. */ +#define BT_MESH_PROP_ID_INPUT_CURRENT_RANGE_SPEC 0x0021 +/** Input current statistics. */ +#define BT_MESH_PROP_ID_INPUT_CURRENT_STAT 0x0022 +/** Input over current event statistics. */ +#define BT_MESH_PROP_ID_INPUT_OVER_CURRENT_EVT_STAT 0x0023 +/** Input over ripple voltage event statistics. */ +#define BT_MESH_PROP_ID_INPUT_OVER_RIPPLE_VOLTAGE_EVT_STAT 0x0024 +/** Input over voltage event statistics. */ +#define BT_MESH_PROP_ID_INPUT_OVER_VOLTAGE_EVT_STAT 0x0025 +/** Input under current event statistics. */ +#define BT_MESH_PROP_ID_INPUT_UNDER_CURRENT_EVT_STAT 0x0026 +/** Input under voltage event statistics. */ +#define BT_MESH_PROP_ID_INPUT_UNDER_VOLTAGE_EVT_STAT 0x0027 +/** Input voltage range specification. */ +#define BT_MESH_PROP_ID_INPUT_VOLTAGE_RANGE_SPEC 0x0028 +/** Input voltage ripple specification. */ +#define BT_MESH_PROP_ID_INPUT_VOLTAGE_RIPPLE_SPEC 0x0029 +/** Input voltage statistics. */ +#define BT_MESH_PROP_ID_INPUT_VOLTAGE_STAT 0x002A +/** Light control ambient luxlevel on. */ +#define BT_MESH_PROP_ID_LIGHT_CTRL_AMB_LUXLEVEL_ON 0x002B +/** Light control ambient luxlevel prolong. */ +#define BT_MESH_PROP_ID_LIGHT_CTRL_AMB_LUXLEVEL_PROLONG 0x002C +/** Light control ambient luxlevel standby. */ +#define BT_MESH_PROP_ID_LIGHT_CTRL_AMB_LUXLEVEL_STANDBY 0x002D +/** Light control lightness on. */ +#define BT_MESH_PROP_ID_LIGHT_CTRL_LIGHTNESS_ON 0x002E +/** Light control lightness prolong. */ +#define BT_MESH_PROP_ID_LIGHT_CTRL_LIGHTNESS_PROLONG 0x002F +/** Light control lightness standby. */ +#define BT_MESH_PROP_ID_LIGHT_CTRL_LIGHTNESS_STANDBY 0x0030 +/** Light control regulator accuracy. */ +#define BT_MESH_PROP_ID_LIGHT_CTRL_REG_ACCURACY 0x0031 +/** Light control regulator kid. */ +#define BT_MESH_PROP_ID_LIGHT_CTRL_REG_KID 0x0032 +/** Light control regulator kiu. */ +#define BT_MESH_PROP_ID_LIGHT_CTRL_REG_KIU 0x0033 +/** Light control regulator kpd. */ +#define BT_MESH_PROP_ID_LIGHT_CTRL_REG_KPD 0x0034 +/** Light control regulator kpu. */ +#define BT_MESH_PROP_ID_LIGHT_CTRL_REG_KPU 0x0035 +/** Light control time fade. */ +#define BT_MESH_PROP_ID_LIGHT_CTRL_TIME_FADE 0x0036 +/** Light control time fade on. */ +#define BT_MESH_PROP_ID_LIGHT_CTRL_TIME_FADE_ON 0x0037 +/** Light control time fade standby auto. */ +#define BT_MESH_PROP_ID_LIGHT_CTRL_TIME_FADE_STANDBY_AUTO 0x0038 +/** Light control time fade standby manual. */ +#define BT_MESH_PROP_ID_LIGHT_CTRL_TIME_FADE_STANDBY_MANUAL 0x0039 +/** Light control time occupancy delay. */ +#define BT_MESH_PROP_ID_LIGHT_CTRL_TIME_OCCUPANCY_DELAY 0x003A +/** Light control time prolong. */ +#define BT_MESH_PROP_ID_LIGHT_CTRL_TIME_PROLONG 0x003B +/** Light control time run on. */ +#define BT_MESH_PROP_ID_LIGHT_CTRL_TIME_RUN_ON 0x003C +/** Lumen maintenance factor. */ +#define BT_MESH_PROP_ID_LUMEN_MAINTENANCE_FACTOR 0x003D +/** Luminous efficacy. */ +#define BT_MESH_PROP_ID_LUMINOUS_EFFICACY 0x003E +/** Luminous energy since turn on. */ +#define BT_MESH_PROP_ID_LUMINOUS_ENERGY_SINCE_TURN_ON 0x003F +/** Luminous exposure. */ +#define BT_MESH_PROP_ID_LUMINOUS_EXPOSURE 0x0040 +/** Luminous flux range. */ +#define BT_MESH_PROP_ID_LUMINOUS_FLUX_RANGE 0x0041 +/** Motion sensed. */ +#define BT_MESH_PROP_ID_MOTION_SENSED 0x0042 +/** Motion threshold. */ +#define BT_MESH_PROP_ID_MOTION_THRESHOLD 0x0043 +/** Open circuit event statistics. */ +#define BT_MESH_PROP_ID_OPEN_CIRCUIT_EVT_STAT 0x0044 +/** Outdoor statistical values. */ +#define BT_MESH_PROP_ID_OUTDOOR_STAT_VALUES 0x0045 +/** Output current range. */ +#define BT_MESH_PROP_ID_OUTPUT_CURRENT_RANGE 0x0046 +/** Output current statistics. */ +#define BT_MESH_PROP_ID_OUTPUT_CURRENT_STAT 0x0047 +/** Output ripple voltage specification. */ +#define BT_MESH_PROP_ID_OUTPUT_RIPPLE_VOLTAGE_SPEC 0x0048 +/** Output voltage range. */ +#define BT_MESH_PROP_ID_OUTPUT_VOLTAGE_RANGE 0x0049 +/** Output voltage statistics. */ +#define BT_MESH_PROP_ID_OUTPUT_VOLTAGE_STAT 0x004A +/** Over output ripple voltage event statistics. */ +#define BT_MESH_PROP_ID_OVER_OUTPUT_RIPPLE_VOLTAGE_EVT_STAT 0x004B +/** People count. */ +#define BT_MESH_PROP_ID_PEOPLE_COUNT 0x004C +/** Presence detected. */ +#define BT_MESH_PROP_ID_PRESENCE_DETECTED 0x004D +/** Present ambient light level. */ +#define BT_MESH_PROP_ID_PRESENT_AMB_LIGHT_LEVEL 0x004E +/** Present ambient temperature. */ +#define BT_MESH_PROP_ID_PRESENT_AMB_TEMP 0x004F +/** Present CIE-1931 chromaticity coordinates. */ +#define BT_MESH_PROP_ID_PRESENT_CIE_1931_CHROMATICITY_COORDS 0x0050 +/** Present correlated color temperature. */ +#define BT_MESH_PROP_ID_PRESENT_CORRELATED_COL_TEMP 0x0051 +/** Present device input power. */ +#define BT_MESH_PROP_ID_PRESENT_DEV_INPUT_POWER 0x0052 +/** Present device operating efficiency. */ +#define BT_MESH_PROP_ID_PRESENT_DEV_OP_EFFICIENCY 0x0053 +/** Present device operating temperature. */ +#define BT_MESH_PROP_ID_PRESENT_DEV_OP_TEMP 0x0054 +/** Present illuminance. */ +#define BT_MESH_PROP_ID_PRESENT_ILLUMINANCE 0x0055 +/** Present indoor ambient temperature. */ +#define BT_MESH_PROP_ID_PRESENT_INDOOR_AMB_TEMP 0x0056 +/** Present input current. */ +#define BT_MESH_PROP_ID_PRESENT_INPUT_CURRENT 0x0057 +/** Present input ripple voltage. */ +#define BT_MESH_PROP_ID_PRESENT_INPUT_RIPPLE_VOLTAGE 0x0058 +/** Present input voltage. */ +#define BT_MESH_PROP_ID_PRESENT_INPUT_VOLTAGE 0x0059 +/** Present luminous flux. */ +#define BT_MESH_PROP_ID_PRESENT_LUMINOUS_FLUX 0x005A +/** Present outdoor ambient temperature. */ +#define BT_MESH_PROP_ID_PRESENT_OUTDOOR_AMB_TEMP 0x005B +/** Present output current. */ +#define BT_MESH_PROP_ID_PRESENT_OUTPUT_CURRENT 0x005C +/** Present output voltage. */ +#define BT_MESH_PROP_ID_PRESENT_OUTPUT_VOLTAGE 0x005D +/** Present planckian distance. */ +#define BT_MESH_PROP_ID_PRESENT_PLANCKIAN_DISTANCE 0x005E +/** Present relative output ripple voltage. */ +#define BT_MESH_PROP_ID_PRESENT_REL_OUTPUT_RIPPLE_VOLTAGE 0x005F +/** Relative device energy use in a period of day. */ +#define BT_MESH_PROP_ID_REL_DEV_ENERGY_USE_IN_A_PERIOD_OF_DAY 0x0060 +/** Relative device runtime in a generic level range. */ +#define BT_MESH_PROP_ID_REL_DEV_RUNTIME_IN_A_GENERIC_LEVEL_RANGE 0x0061 +/** Relative exposure time in an illuminance range. */ +#define BT_MESH_PROP_ID_REL_EXPOSURE_TIME_IN_AN_ILLUMINANCE_RANGE 0x0062 +/** Relative runtime in a correlated color temperature range. */ +#define BT_MESH_PROP_ID_REL_RUNTIME_IN_A_CORRELATED_COL_TEMP_RANGE 0x0063 +/** Relative runtime in a device operating temperature range. */ +#define BT_MESH_PROP_ID_REL_RUNTIME_IN_A_DEV_OP_TEMP_RANGE 0x0064 +/** Relative runtime in an input current range. */ +#define BT_MESH_PROP_ID_REL_RUNTIME_IN_AN_INPUT_CURRENT_RANGE 0x0065 +/** Relative runtime in an input voltage range. */ +#define BT_MESH_PROP_ID_REL_RUNTIME_IN_AN_INPUT_VOLTAGE_RANGE 0x0066 +/** Short circuit event statistics. */ +#define BT_MESH_PROP_ID_SHORT_CIRCUIT_EVT_STAT 0x0067 +/** Time since motion sensed. */ +#define BT_MESH_PROP_ID_TIME_SINCE_MOTION_SENSED 0x0068 +/** Time since presence detected. */ +#define BT_MESH_PROP_ID_TIME_SINCE_PRESENCE_DETECTED 0x0069 +/** Total device energy use. */ +#define BT_MESH_PROP_ID_TOT_DEV_ENERGY_USE 0x006A +/** Total device off on cycles. */ +#define BT_MESH_PROP_ID_TOT_DEV_OFF_ON_CYCLES 0x006B +/** Total device power on cycles. */ +#define BT_MESH_PROP_ID_TOT_DEV_POWER_ON_CYCLES 0x006C +/** Total device power on time. */ +#define BT_MESH_PROP_ID_TOT_DEV_POWER_ON_TIME 0x006D +/** Total device runtime. */ +#define BT_MESH_PROP_ID_TOT_DEV_RUNTIME 0x006E +/** Total light exposure time. */ +#define BT_MESH_PROP_ID_TOT_LIGHT_EXPOSURE_TIME 0x006F +/** Total luminous energy. */ +#define BT_MESH_PROP_ID_TOT_LUMINOUS_ENERGY 0x0070 +/** Desired ambient temperature. */ +#define BT_MESH_PROP_ID_DESIRED_AMB_TEMP 0x0071 +/** @} */ + +#endif /* BT_MESH_PROPERTIES_H__ */ + +/** @} */ diff --git a/include/bluetooth/mesh/properties.rst b/include/bluetooth/mesh/properties.rst new file mode 100644 index 000000000000..9168b9ba321c --- /dev/null +++ b/include/bluetooth/mesh/properties.rst @@ -0,0 +1,23 @@ +.. _bt_mesh_properties_readme: + +Bluetooth Mesh Properties +########################## + +The Bluetooth SIG defines a list of Bluetooth Mesh properties in the Mesh +Device Properties Specification. Each property has an assigned ID and an +associated Bluetooth GATT characteristic. The properties all represent +values on a format defined by the associated characteristic. + +Only the list of Property IDs, as specified in the Bluetooth Mesh Device +Properties Specification v1.1 are defined in the API. It's up to the +application to implement property values that are compliant with the +specified format. + +API documentation +****************** + +| Header file: :file:`include/bluetooth/mesh/properties.h` + +.. doxygengroup:: bt_mesh_property_ids + :project: nrf + :members: diff --git a/include/bluetooth/services/latency.h b/include/bluetooth/services/latency.h new file mode 100644 index 000000000000..0fa06edf5e1a --- /dev/null +++ b/include/bluetooth/services/latency.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +/** + * @file + * @defgroup bt_gatt_latency BLE GATT Latency Service API + * @{ + * @brief API for the BLE GATT Latency Service. + */ + +#ifndef BT_GATT_LATENCY_H_ +#define BT_GATT_LATENCY_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Latency callback structure. */ +struct bt_gatt_latency_cb { + /** @brief Latency received callback. + * + * This function is called when a GATT write request has been received + * by the Latency characteristic. + * + * It is optional for an application to handle the write request when + * measuring the latency. + * + * @param[in] buf Latency data. + * @param[in] len Latency data length. + */ + void (*latency_request)(const void *buf, u16_t len); +}; + +/** @brief Latency structure. */ +struct bt_gatt_latency { + /** Characteristic handle. */ + u16_t handle; + + /** Connection object. */ + struct bt_conn *conn; + + /** Internal state. */ + atomic_t state; +}; + +/** @brief Latency Service UUID. */ + +#define LATENCY_UUID \ + UTIL_EXPAND(0x3A, 0x81, 0xC0, 0x58, 0xDE, 0xFD, 0x46, 0x34, \ + 0x9B, 0xF3, 0xDB, 0x58, 0x01, 0x6E, 0x13, 0x67) + +#define BT_UUID_LATENCY BT_UUID_DECLARE_128(LATENCY_UUID) + +/** @brief UUID of the Latency Characteristic. **/ +#define BT_UUID_LATENCY_CHAR \ + BT_UUID_DECLARE_128(0x3A, 0x81, 0xC0, 0x58, 0xDE, 0xFD, 0x46, 0x34, \ + 0x9B, 0xF3, 0xDB, 0x58, 0x02, 0x6E, 0x13, 0x67) + +/** @brief Initialize the GATT latency service. + * + * @param[in] latency Latency service instance. + * @param[in] cb Callbacks. + * + * @retval 0 If the operation was successful. + * Otherwise, a negative error code is returned. + * @retval (-EINVAL) Special error code used when the input + * parameters are invalid. + * @retval (-EALREADY) Special error code used when the latency + * service has been initialed. + */ +int bt_gatt_latency_init(struct bt_gatt_latency *latency, + const struct bt_gatt_latency_cb *cb); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* BT_GATT_LATENCY_H_ */ diff --git a/include/bluetooth/services/latency.rst b/include/bluetooth/services/latency.rst new file mode 100644 index 000000000000..7bb5e4ecc9ec --- /dev/null +++ b/include/bluetooth/services/latency.rst @@ -0,0 +1,34 @@ +.. _latency_readme: + +GATT Latency Service +#################### + +The GATT Latency Service is a custom service containing a single writable characteristic. +This characteristic can be used to calculate the round-trip time of a GATT Write operation. + +Service UUID +************ + +The 128-bit service UUID is 67136E01-58DB-F39B-3446-FDDE58C0813A. + +Characteristics +*************** + +This service has one characteristic. + +Latency Characteristic (67136E02-58DB-F39B-3446-FDDE58C0813A) +============================================================= + +Write + The server is waiting for a write request from the client, then it will automatically reply a write response back. + + +API documentation +***************** + +| Header file: :file:`include/bluetooth/services/latency.h` +| Source file: :file:`subsys/bluetooth/services/latency.c` + +.. doxygengroup:: bt_gatt_latency + :project: nrf + :members: diff --git a/include/bluetooth/services/latency_c.h b/include/bluetooth/services/latency_c.h new file mode 100644 index 000000000000..f12dc8123df3 --- /dev/null +++ b/include/bluetooth/services/latency_c.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +/** + * @file + * @defgroup bt_gatt_latency_c BLE GATT Latency Client API + * @{ + * @brief API for the BLE GATT Latency Client. + */ + +#ifndef BT_GATT_LATENCY_C_H_ +#define BT_GATT_LATENCY_C_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Latency client callback structure. */ +struct bt_gatt_latency_c_cb { + /** @brief Latency received callback. + * + * This function is called when a GATT write response has been received + * by the Latency characteristic. + * + * It is mandatory for an application to handle the write response when + * measuring the latency. + * + * @param[in] buf Latency data. + * @param[in] len Latency data length. + */ + void (*latency_response)(const void *buf, u16_t len); +}; + +/** @brief Latency client structure. */ +struct bt_gatt_latency_c { + /** Characteristic handle. */ + u16_t handle; + + /** Latency parameter. */ + struct bt_gatt_write_params latency_params; + + /** Connection object. */ + struct bt_conn *conn; + + /** Internal state. */ + atomic_t state; +}; + +/** @brief Initialize the GATT latency client. + * + * @param[in] latency Latency client instance. + * @param[in] cb Callbacks. + * + * @retval 0 If the operation was successful. + * Otherwise, a negative error code is returned. + * @retval (-EINVAL) Special error code used when the input + * parameters are invalid. + * @retval (-EALREADY) Special error code used when the latency + * client has been initialed. + */ +int bt_gatt_latency_c_init(struct bt_gatt_latency_c *latency, + const struct bt_gatt_latency_c_cb *cb); + +/** @brief Assign handles to the latency client instance. + * + * This function should be called when a link with a peer has been established, + * to associate the link to this instance of the module. This makes it + * possible to handle several links and associate each link to a particular + * instance of this module. The GATT attribute handles are provided by the + * GATT Discovery Manager. + * + * @param[in] dm Discovery object. + * @param[in,out] latency Latency client instance. + * + * @retval 0 If the operation was successful. + * Otherwise, a negative error code is returned. + * @retval (-ENOTSUP) Special error code used when the UUID + * of the service does not match the expected UUID. + * @retval (-EINVAL) Special error code used when the UUID + * characteristic or value descriptor not found. + */ +int bt_gatt_latency_c_handles_assign(struct bt_gatt_dm *dm, + struct bt_gatt_latency_c *latency); + +/** @brief Write data to the server. + * + * @param[in] latency Latency client instance. + * @param[in] data Data. + * @param[in] len Data length. + * + * @retval 0 If the operation was successful. + * Otherwise, a negative error code is returned. + * @retval (-EALREADY) Special error code used when the asynchronous + * request is waiting for a response. + */ +int bt_gatt_latency_c_request(struct bt_gatt_latency_c *latency, + const void *data, u16_t len); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* BT_GATT_LATENCY_C_H_ */ diff --git a/include/bluetooth/services/latency_c.rst b/include/bluetooth/services/latency_c.rst new file mode 100644 index 000000000000..19658ea042d1 --- /dev/null +++ b/include/bluetooth/services/latency_c.rst @@ -0,0 +1,23 @@ +.. _latency_c_readme: + +GATT Latency Client +################### + +The Latency Client can be used to interact with a connected peer that is running the Latency service with the :ref:`latency_readme`. +It writes data to the Latency characteristic and wait for a response to count the time spend of a GATT Write operation. + +Latency Characteristic +********************** + +To send data to the Latency Characteristic, use the send API of this module. +The sending procedure is asynchronous, so the data to be sent must remain valid until a dedicated callback notifies you that the Write Request has been completed. + +API documentation +***************** + +| Header file: :file:`include/bluetooth/services/latency_c.h` +| Source file: :file:`subsys/bluetooth/services/latency_c.c` + +.. doxygengroup:: bt_gatt_latency_c + :project: nrf + :members: diff --git a/include/dfu/dfu_target.h b/include/dfu/dfu_target.h new file mode 100644 index 000000000000..74737e7780d5 --- /dev/null +++ b/include/dfu/dfu_target.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +#ifndef DFU_TARGET_H__ +#define DFU_TARGET_H__ + +/** + * @defgroup dfu_target DFU Target + * @brief DFU Target + * + * @{ + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define DFU_TARGET_IMAGE_TYPE_MCUBOOT 1 +#define DFU_TARGET_IMAGE_TYPE_MODEM_DELTA 2 + +/** @brief Functions which needs to be supported by all DFU targets. + */ +struct dfu_target { + int (*init)(size_t file_size); + int (*offset_get)(size_t *offset); + int (*write)(const void *const buf, size_t len); + int (*done)(bool successful); +}; + +/** + * @brief Find the image type for the buffer of bytes recived. Used to determine + * what dfu target to initialize. + * + * @param[in] buf A buffer of bytes which are the start of an binary firmware + * image. + * @param[in] len The length of the provided buffer. + * + * @return Positive identifier for a supported image type or a negative error + * code identicating reason of failure. + **/ +int dfu_target_img_type(const void *const buf, size_t len); + +/** + * @brief Initialize the resources needed for the specific image type DFU + * target. + * + * If a target update is in progress, and the same target is + * given as input, then calling the 'init()' function of that target is + * skipped. + * + * To allow continuation of an aborted DFU procedure, call the + * 'dfu_target_offset_get' function after invoking this function. + * + * @param[in] img_type Image type identifier. + * @param[in] file_size Size of the current file being downloaded. + * + * @return 0 for a supported image type or a negative error + * code identicating reason of failure. + * + **/ +int dfu_target_init(int img_type, size_t file_size); + +/** + * @brief Get offset of the firmware upgrade + * + * @param[out] offset Returns the offset of the firmware upgrade. + * + * @return 0 if success, otherwise negative value if unable to get the offset + */ +int dfu_target_offset_get(size_t *offset); + +/** + * @brief Write the given buffer to the initialized DFU target. + * + * @param[in] buf A buffer of bytes which contains part of an binary firmware + * image. + * @param[in] len The length of the provided buffer. + * + * @return Positive identifier for a supported image type or a negative error + * code identicating reason of failure. + **/ +int dfu_target_write(const void *const buf, size_t len); + +/** + * @brief Deinitialize the resources that were needed for the current DFU + * target. + * + * @param[in] successful Indicate whether the process completed successfully or + * was aborted. + * + * @return 0 for an successful deinitialization or a negative error + * code identicating reason of failure. + **/ +int dfu_target_done(bool successful); + +#ifdef __cplusplus +} +#endif + +#endif /* DFU_TARGET_H__ */ diff --git a/include/dfu/dfu_target.rst b/include/dfu/dfu_target.rst new file mode 100644 index 000000000000..5fb2cdeec000 --- /dev/null +++ b/include/dfu/dfu_target.rst @@ -0,0 +1,67 @@ +.. _lib_dfu_target: + +DFU target +########## + +The DFU target library provides a common API for different types of firmware upgrades, for example, an MCUboot style upgrade or a modem firmware upgrade. +Use this library in your component to do different types of firmware upgrades against a single interface. + +When initializing the DFU target library, you must provide information about the type of firmware upgrade. +To do so automatically, send the first fragment of the firmware to the function :cpp:func:`dfu_target_img_type`. +This function can identify all supported firmware upgrade types. +The result of this call can then be given as input to the :cpp:func:`dfu_target_init` function. + + +.. note:: + After starting a DFU procedure for a given target, it is not supported to + initialize a new DFU procedure with a different firmware file for the same + target until either the DFU procedure has completed successfully, or the + device has been restarted. + + +Supported DFU targets +********************* + +Every supported DFU target must implement the set of functions defined in :file:`subsys/dfu/src/dfu_target.c`. + +The following sections describe the DFU targets that are currently supported. + +MCUboot style upgrades +====================== + +This type of firmware upgrade writes the data given to the :cpp:func:`dfu_target_write` function into the secondary slot specified by MCUboot's flash partitions. + +When the complete transfer is done, call the :cpp:func:`dfu_target_done` function to mark the firmware that is stored in the secondary slot as ready to be booted. +On the next reboot, the device will run the new firmware. + +Modem firmware upgrades +======================= + +This type of firmware upgrade opens a socket into the modem and passes the data given to the :cpp:func:`dfu_target_write` function through the socket. +The modem stores the data in the memory location for firmware patches. +If there is already a firmware patch stored in the modem, the library requests the modem to delete the old firmware patch, to make space for the new patch. + +When the complete transfer is done, call the :cpp:func:`dfu_target_done` function to request the modem to apply the patch, and to close the socket. +On the next reboot, the modem will to try to apply the patch. + + +Configuration +************* + +You can disable support for specific DFU targets with the following parameters: + +- :option:`CONFIG_DFU_TARGET_MCUBOOT` +- :option:`CONFIG_DFU_TARGET_MODEM` + +By default, all DFU targets are enabled, but you can only select the targets that are supported by your device and application. + + +API documentation +***************** + +| Header file: :file:`include/dfu/dfu_target.h` +| Source files: :file:`subsys/dfu/src/` + +.. doxygengroup:: dfu_target + :project: nrf + :members: diff --git a/subsys/bootloader/include/fprotect.h b/include/drivers/fprotect.h similarity index 73% rename from subsys/bootloader/include/fprotect.h rename to include/drivers/fprotect.h index b3839d2ec013..741a5e1a7d2b 100644 --- a/subsys/bootloader/include/fprotect.h +++ b/include/drivers/fprotect.h @@ -9,6 +9,19 @@ #include #include +#include + +/** + * @file + * @defgroup fprotect Hardware flash write protection. + * @{ + * + * @brief API for write protection of flash areas using Hardware peripheral + */ + +#ifdef __cplusplus +extern "C" { +#endif /** * @brief Protect flash area against writes. @@ -24,4 +37,12 @@ */ int fprotect_area(u32_t start, size_t length); +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + #endif /* FPROTECT_H_ */ diff --git a/include/drivers/fprotect.rst b/include/drivers/fprotect.rst new file mode 100644 index 000000000000..5fd01c55444b --- /dev/null +++ b/include/drivers/fprotect.rst @@ -0,0 +1,25 @@ +.. _fprotect_readme: + +Hardware flash write protection +############################### + +The hardware flash write protection driver (``fprotect``) can be used to protect flash areas from writing. +The driver uses a hardware peripheral (BPROT, ACL, or SPU, depending on the chip model) to protect the area. +The protection is irreversible until a reset occurs. + +The following example shows how to protect ``PM_B0_SIZE`` bytes of the flash area starting from ``PM_B0_ADDRESS``: + +.. code-block:: c + + int err = fprotect_area(PM_B0_ADDRESS, PM_B0_SIZE); + + +API documentation +***************** + +| Header file: :file:`include/fprotect.h` +| Source files: :file:`drivers/fprotect/` + +.. doxygengroup:: fprotect + :project: nrf + :members: diff --git a/include/fw_metadata.h b/include/fw_info.h similarity index 57% rename from include/fw_metadata.h rename to include/fw_info.h index 2448a15c54d6..c65039842b7d 100644 --- a/include/fw_metadata.h +++ b/include/fw_info.h @@ -4,32 +4,30 @@ * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic */ -#ifndef FW_METADATA_H__ -#define FW_METADATA_H__ +#ifndef FW_INFO_H__ +#define FW_INFO_H__ -/* - * The package will consist of (firmware | (padding) | validation_info), - * where the firmware contains the firmware_info at a predefined location. The - * padding is present if the validation_info needs alignment. The - * validation_info is not directly referenced from the firmware_info since the - * validation_info doesn't actually have to be placed after the firmware. - * - * Putting the firmware info inside the firmware instead of in front of it - * removes the need to consider the padding before the vector table of the - * firmware. It will also likely make it easier to add all the info at compile - * time. - */ +#ifdef __cplusplus +extern "C" { +#endif #include #include #include #include -#include +#include #include +#if USE_PARTITION_MANAGER +#include +#endif + +/** @defgroup fw_info Firmware info structure + * @{ + */ -#define MAGIC_LEN_WORDS (CONFIG_FW_MAGIC_LEN / sizeof(u32_t)) +#define MAGIC_LEN_WORDS (CONFIG_FW_INFO_MAGIC_LEN / sizeof(u32_t)) -struct fw_abi_info; +struct fw_info_abi; /**@brief Function that returns an ABI. * @@ -43,14 +41,20 @@ struct fw_abi_info; * @retval -EBADF index too large. * @retval -EFAULT abi was NULL. */ -typedef int (*fw_abi_getter)(u32_t id, u32_t index, - const struct fw_abi_info **abi); - -struct __packed fw_firmware_info { - /* Magic value to verify that the struct has the correct type. */ +typedef int (*fw_info_abi_getter)(u32_t id, u32_t index, + const struct fw_info_abi **abi); + +/** + * This is a data structure that is placed at a specific offset inside a + * firmware image so it can be consistently read by external parties. The + * specific offset makes it easy to find, and the magic value at the start + * guarantees that it contains data of a specific format. + */ +struct __packed fw_info { + /* Magic value to verify that the struct has the correct format. */ u32_t magic[MAGIC_LEN_WORDS]; - /* Size without validation_info pointer and padding. */ + /* Size of the firmware image code. */ u32_t firmware_size; /* Monotonically increasing version counter.*/ @@ -60,35 +64,41 @@ struct __packed fw_firmware_info { u32_t firmware_address; /* Where to place the getter for the ABI provided to this firmware. */ - fw_abi_getter *abi_in; + fw_info_abi_getter *abi_in; /* This firmware's ABI getter. */ - const fw_abi_getter abi_out; + const fw_info_abi_getter abi_out; }; - +/** @cond + * Remove from doc building. + */ #define OFFSET_CHECK(type, member, value) \ BUILD_ASSERT_MSG(offsetof(type, member) == value, \ #member " has wrong offset") /* Static asserts to ensure compatibility */ -OFFSET_CHECK(struct fw_firmware_info, magic, 0); -OFFSET_CHECK(struct fw_firmware_info, firmware_size, CONFIG_FW_MAGIC_LEN); -OFFSET_CHECK(struct fw_firmware_info, firmware_version, - (CONFIG_FW_MAGIC_LEN + 4)); -OFFSET_CHECK(struct fw_firmware_info, firmware_address, - (CONFIG_FW_MAGIC_LEN + 8)); +OFFSET_CHECK(struct fw_info, magic, 0); +OFFSET_CHECK(struct fw_info, firmware_size, CONFIG_FW_INFO_MAGIC_LEN); +OFFSET_CHECK(struct fw_info, firmware_version, + (CONFIG_FW_INFO_MAGIC_LEN + 4)); +OFFSET_CHECK(struct fw_info, firmware_address, + (CONFIG_FW_INFO_MAGIC_LEN + 8)); + +/** @endcond + */ /* For declaring this firmware's firmware info. */ #define __fw_info Z_GENERIC_SECTION(.firmware_info) __attribute__((used)) const -/* This struct is meant to serve as a header before a list of function pointers +/** + * This struct is meant to serve as a header before a list of function pointers * (or something else) that constitute the actual ABI. How to use the ABI, such * as the signatures of all the functions in the list must be unambiguous for an * ID/version combination. */ -struct __packed fw_abi_info { - /* Magic value to verify that the struct has the correct type. */ +struct __packed fw_info_abi { + /* Magic value to verify that the struct has the correct format. */ u32_t magic[MAGIC_LEN_WORDS]; /* The id of the ABI. */ @@ -113,10 +123,11 @@ struct __packed fw_abi_info { #define __ext_abi(type, name) \ OFFSET_CHECK_EXT_ABI(type, magic, 0); \ - OFFSET_CHECK_EXT_ABI(type, abi_id, CONFIG_FW_MAGIC_LEN); \ - OFFSET_CHECK_EXT_ABI(type, abi_flags, (CONFIG_FW_MAGIC_LEN + 4)); \ - OFFSET_CHECK_EXT_ABI(type, abi_version, (CONFIG_FW_MAGIC_LEN + 8)); \ - OFFSET_CHECK_EXT_ABI(type, abi_len, (CONFIG_FW_MAGIC_LEN + 12)); \ + OFFSET_CHECK_EXT_ABI(type, abi_id, CONFIG_FW_INFO_MAGIC_LEN); \ + OFFSET_CHECK_EXT_ABI(type, abi_flags, (CONFIG_FW_INFO_MAGIC_LEN + 4)); \ + OFFSET_CHECK_EXT_ABI(type, abi_version,\ + (CONFIG_FW_INFO_MAGIC_LEN + 8)); \ + OFFSET_CHECK_EXT_ABI(type, abi_len, (CONFIG_FW_INFO_MAGIC_LEN + 12)); \ BUILD_ASSERT_MSG((sizeof(type) % 4) == 0, \ "ext_abi " #type " is not word-aligned"); \ extern const type name; \ @@ -127,7 +138,7 @@ struct __packed fw_abi_info { -#define ABI_INFO_INIT(id, flags, version, total_size) \ +#define FW_INFO_ABI_INIT(id, flags, version, total_size) \ { \ .magic = {ABI_INFO_MAGIC}, \ .abi_id = id, \ @@ -189,14 +200,13 @@ static inline bool memeq(const void *expected, const void *actual, u32_t len) * * @return pointer if valid, NULL if not. */ -static inline const struct fw_firmware_info * -fw_check_firmware_info(u32_t fw_info_addr) +static inline const struct fw_info *fw_info_check(u32_t fw_info_addr) { - const struct fw_firmware_info *finfo; - const u32_t firmware_info_magic[] = {FIRMWARE_INFO_MAGIC}; + const struct fw_info *finfo; + const u32_t fw_info_magic[] = {FIRMWARE_INFO_MAGIC}; - finfo = (const struct fw_firmware_info *)(fw_info_addr); - if (memeq(finfo->magic, firmware_info_magic, CONFIG_FW_MAGIC_LEN)) { + finfo = (const struct fw_info *)(fw_info_addr); + if (memeq(finfo->magic, fw_info_magic, CONFIG_FW_INFO_MAGIC_LEN)) { return finfo; } return NULL; @@ -209,27 +219,46 @@ fw_check_firmware_info(u32_t fw_info_addr) #define FW_INFO_OFFSET2 0x800 #define FW_INFO_OFFSET_COUNT 3 +/* Find the difference between the start of the current image and the address + * from which the firmware info offset is calculated. + */ +#if defined(PM_S0_PAD_SIZE) && (PM_ADDRESS == PM_S0_IMAGE_ADDRESS) + #define VECTOR_OFFSET PM_S0_PAD_SIZE +#elif defined(PM_S1_PAD_SIZE) && (PM_ADDRESS == PM_S1_IMAGE_ADDRESS) + #define VECTOR_OFFSET PM_S1_PAD_SIZE +#elif defined(PM_MCUBOOT_PAD_SIZE) && \ + (PM_ADDRESS == PM_MCUBOOT_PRIMARY_APP_ADDRESS) + #define VECTOR_OFFSET PM_MCUBOOT_PAD_SIZE +#else + #define VECTOR_OFFSET 0 +#endif + +#define CURRENT_OFFSET (CONFIG_FW_INFO_OFFSET + VECTOR_OFFSET) + static const u32_t allowed_offsets[] = {FW_INFO_OFFSET0, FW_INFO_OFFSET1, FW_INFO_OFFSET2}; +/** @cond + * Remove from doc building. + */ BUILD_ASSERT_MSG(ARRAY_SIZE(allowed_offsets) == FW_INFO_OFFSET_COUNT, "Mismatch in the number of allowed offsets."); +/** @endcond + */ -#if (FW_INFO_OFFSET_COUNT != 3) || \ - ((CONFIG_FW_FIRMWARE_INFO_OFFSET) != (FW_INFO_OFFSET0) && \ - (CONFIG_FW_FIRMWARE_INFO_OFFSET) != (FW_INFO_OFFSET1) && \ - (CONFIG_FW_FIRMWARE_INFO_OFFSET) != (FW_INFO_OFFSET2)) - #error FW_FIRMWARE_INFO_OFFSET not set to one of the allowed values. +#if (FW_INFO_OFFSET_COUNT != 3) || ((CURRENT_OFFSET) != (FW_INFO_OFFSET0) && \ + (CURRENT_OFFSET) != (FW_INFO_OFFSET1) && \ + (CURRENT_OFFSET) != (FW_INFO_OFFSET2)) + #error FW_INFO_OFFSET not set to one of the allowed values. #endif /* Search for the firmware_info structure inside the firmware. */ -static inline const struct fw_firmware_info * -fw_find_firmware_info(u32_t firmware_address) +static inline const struct fw_info *fw_info_find(u32_t firmware_address) { - const struct fw_firmware_info *finfo; + const struct fw_info *finfo; for (u32_t i = 0; i < FW_INFO_OFFSET_COUNT; i++) { - finfo = fw_check_firmware_info(firmware_address + + finfo = fw_info_check(firmware_address + allowed_offsets[i]); if (finfo) { return finfo; @@ -239,21 +268,27 @@ fw_find_firmware_info(u32_t firmware_address) } -/* Check a fw_abi_info pointer. */ -static inline bool fw_abi_info_check(const struct fw_abi_info *abi_info) +/* Check a fw_info_abi pointer. */ +static inline bool fw_info_abi_check(const struct fw_info_abi *abi_info) { const u32_t abi_info_magic[] = {ABI_INFO_MAGIC}; - return(memeq(abi_info->magic, abi_info_magic, CONFIG_FW_MAGIC_LEN)); + return memeq(abi_info->magic, abi_info_magic, CONFIG_FW_INFO_MAGIC_LEN); } -/* Expose ABIs to the firmware at this address. This is meant to be called - * immediately before booting the aforementioned firmware since it will likely - * corrupt the memory of the running firmware. +/**Expose ABIs to another firmware + * + * Populate the other firmware's @c abi_in with a internal ABI getter function + * which serves all ABIs created with __ext_abi. + * + * @note This is should be called immediately before booting the other firmware + * since it will likely corrupt the memory of the running firmware. + * + * @param[in] fw_info Pointer to the other firmware's information structure. */ -void fw_abi_provide(const struct fw_firmware_info *fw_info); +void fw_info_abi_provide(const struct fw_info *fw_info); -/* Get a single ABI. +/**Get a single ABI. * * @param[in] id Which ABI to get. * @param[in] index If there are multiple ABIs available with the same ID, @@ -261,9 +296,9 @@ void fw_abi_provide(const struct fw_firmware_info *fw_info); * * @return The ABI, or NULL, if it wasn't found. */ -const struct fw_abi_info *fw_abi_get(u32_t id, u32_t index); +const struct fw_info_abi *fw_info_abi_get(u32_t id, u32_t index); -/* Find an ABI based on a version range. +/**Find an ABI based on a version range. * * @param[in] id The ID of the ABI to find. * @param[in] flags The required flags of the ABI to find. The returned @@ -273,7 +308,13 @@ const struct fw_abi_info *fw_abi_get(u32_t id, u32_t index); * * @return The ABI, or NULL if none was found. */ -const struct fw_abi_info *fw_abi_find(u32_t id, u32_t flags, u32_t min_version, - u32_t max_version); +const struct fw_info_abi *fw_info_abi_find(u32_t id, u32_t flags, + u32_t min_version, u32_t max_version); + + /** @} */ + +#ifdef __cplusplus +} +#endif #endif diff --git a/include/fw_info.rst b/include/fw_info.rst new file mode 100644 index 000000000000..a227a8b1cca2 --- /dev/null +++ b/include/fw_info.rst @@ -0,0 +1,127 @@ +.. _doc_fw_info: + +Firmware information +#################### + +The firmware information module (``fw_info``) provides externally readable metadata about a firmware image. +This information is located at a specific offset in the image. +In addition, the module provides code to read such information. + +Purpose +******* + +The purpose of the firmware information is to allow other images such as bootloaders, or infrastructure such as firmware servers, to gain information about the firmware image. + +The firmware information structure has a 12-byte magic header and a verified binary layout to ensure that the format is portable and identifiable. +It must be located at one of three offsets from the start of the image: 0x200, 0x400, or 0x800. +The reason that the structure is not located at 0x00 is that this can be problematic in some use cases, such as when the vector table must be located at 0x00. + +These rules make it simple to retrieve the information by checking for each possible offset (0x200, 0x400, 0x800) if the first 12 bytes match the magic value. +If they do, the information can be retrieved according to the definition in :c:type:`fw_info`. + +Information structure +********************* + +The information structure contains a minimal set of information that allows other code to reason about the firmware with no other information available. +It includes the following information: + +* The size of the firmware image. +* The single, monotonically increasing version number of the image. +* The address through which to boot into the firmware (the vector table address). + This address is not necessarily the start of the image. + +Additionally, there is information for exchanging arbitrary data: + +* Application Binary Interface (ABI) getter (:cpp:member:`abi_out`) +* Pointer to ABI getter (:cpp:member:`abi_in`) + +.. _doc_fw_info_abi: + +ABIs +**** + +The firmware information structure allows for exchange of arbitrary tagged and versioned interfaces called Application Binary Interfaces (ABIs). + +An ABI structure is a structure consisting of a header followed by arbitrary data. +The header consists of the following information: + +* ABI ID (uniquely identifies the ABI) +* ABI version (single, monotonically increasing version number for the ABI with this ID) +* ABI flags (32 individual bits for indicating the particulars of the ABI) +* ABI length (length of the following data) + +To retrieve an ABI, a firmware image calls another firmware image's ABI getter. +Every image must provide an ABI getter (:cpp:member:`abi_out`) that other images can use to retrieve its ABIs. +This ABI getter is a function pointer that retrieves ABI structs (or rather pointers to ABI structs). + +In addition, every image can access other ABIs through a second ABI getter (:cpp:member:`abi_in`). +This ABI getter must be provided when booting the image. +In other words, an image should expect its :cpp:member:`abi_in` ABI getter to be filled at the time it boots, and will not touch it during booting. +After booting, an image can call :cpp:member:`abi_in` to retrieve ABIs from the image or images that booted it without knowing where they are located. + +Each image can provide multiple ABIs. +An ABI getter function takes an index, and each index from 0 to *n* must return a different ABI, given that the image provides *n* ABIs with the same ID. + +Typically, the actual ABI will be a function pointer (or a list of function pointers), but the data can be anything, though it should be considered read-only. +The reason for making the ABI getters function pointers instead of pointing to a list of ABIs is to allow dynamic creation of ABI lists without using RAM. + +Usage +***** + +To locate and verify firmware info structures, use :cpp:func:`fw_info_find` and :cpp:func:`fw_info_check`, respectively. + +To find an ABI with a given version and flags, call :cpp:func:`fw_info_abi_find`. +This function calls :cpp:member:`abi_in` under the hood, checks the ABI's version against the allowed range, and checks that it has all the flags set. + +To populate an image's :cpp:member:`abi_in` (before booting the image), the booting image should call :cpp:func:`fw_info_abi_provide` with the other image's firmware information structure. +Note that if the booting (current) firmware image and the booted image's RAM overlap, :cpp:func:`fw_info_abi_provide` will corrupt the current firmware's RAM. +This is ok if it is done immediately before booting the other image, thus after it has performed its last RAM access. + +Creating ABIs +************* + +To create an ABI, complete the following steps: + +1. Declare a new struct type that starts with the :c:type:`fw_info_abi` struct: + + .. code-block:: c + + struct my_abi { + struct fw_info_abi header; + struct { + /* Actual ABI/data goes here. */ + } abi; + }; + +#. Use the :c:macro:`__ext_abi` macro to initialize the ABI struct in an arbitrary location. + :c:macro:`__ext_abi` will automatically include the ABI in the list provided via :cpp:func:`fw_info_abi_provide`. + + .. code-block:: c + + __ext_abi(struct my_abi, my_abi) = { + .header = FW_INFO_ABI_INIT(MY_ABI_ID, + CONFIG_MY_ABI_FLAGS, + CONFIG_MY_ABI_VER, + sizeof(struct my_abi)), + .abi = { + /* ABI initialization goes here. */ + } + }; + +#. To include function pointers in your ABI, call the :c:macro:`EXT_ABI_FUNCTION` macro to forward-declare the function and create a typedef for the function pointer: + + .. code-block:: c + + EXT_ABI_FUNCTION(int, my_abi_foo, bool arg1, int *arg2); + + + +API documentation +***************** + +| Header file: :file:`include/fw_info.h` +| Source files: :file:`subsys/fw_info/` + +.. doxygengroup:: fw_info + :project: nrf + :members: diff --git a/include/gps.h b/include/gps.h index 54a28556ec75..eb175f94d0d5 100644 --- a/include/gps.h +++ b/include/gps.h @@ -40,7 +40,7 @@ struct gps_datetime { u8_t hour; u8_t minute; u8_t seconds; - u8_t ms; + u16_t ms; }; struct gps_sv { diff --git a/include/modem_info.h b/include/modem_info.h index 8525a38366dd..12b1d2541dad 100644 --- a/include/modem_info.h +++ b/include/modem_info.h @@ -56,6 +56,7 @@ enum modem_info { MODEM_INFO_GPS_MODE, /**< GPS support mode. */ MODEM_INFO_IMSI, /**< Mobile subscriber identity. */ MODEM_INFO_IMEI, /**< Modem serial number. */ + MODEM_INFO_DATE_TIME, /**< Mobile network time and date */ MODEM_INFO_COUNT, /**< Number of legal elements in the enum. */ }; @@ -81,6 +82,7 @@ struct network_param { struct lte_param lte_mode; /**< LTE-M support mode. */ struct lte_param nbiot_mode; /**< NB-IoT support mode. */ struct lte_param gps_mode; /**< GPS support mode. */ + struct lte_param date_time; /**< Mobile network time and date */ double cellid_dec; /**< Cell ID of the device (in decimal format). */ char network_mode[MODEM_INFO_NETWORK_MODE_MAX_SIZE]; diff --git a/include/modem_info.rst b/include/modem_info.rst index ee3a7c8d65bb..9052bf30385a 100644 --- a/include/modem_info.rst +++ b/include/modem_info.rst @@ -16,6 +16,7 @@ It issues AT commands to retrieve the following data: * The modem firmware version * The modem serial number * The LTE-M, NB-IoT, and GPS support mode +* Mobile network time and date The modem information library uses the :ref:`at_cmd_parser_readme`. diff --git a/include/net/aws_fota.rst b/include/net/aws_fota.rst index b7c510a25b0d..b7f624ed5f8d 100644 --- a/include/net/aws_fota.rst +++ b/include/net/aws_fota.rst @@ -3,31 +3,84 @@ AWS FOTA ######## -This library combines the :ref:`lib_aws_jobs` and :ref:`lib_fota_download` libraries to create a user-friendly library that can perform firmware-over-the-air (FOTA) update using HTTP and MQTT TLS. +The Amazon Web Services firmware over-the-air (AWS FOTA) library combines the :ref:`lib_aws_jobs` and :ref:`lib_fota_download` libraries to create a user-friendly library that can perform an over-the-air firmware update using HTTP and MQTT TLS. -It connects to the specified broker using the existing or given certificates and uses a `TLS `_ -connection for the MQTT connection. +It connects to the specified broker using the existing or given certificates and uses `TLS`_ for the MQTT connection. This means that the data sent in each MQTT message is encrypted. -Note that a device connected to the AWS MQTT broker with a valid but different certificate, which has the same security attributes in AWS as the other device, and which is able to subscribe to the same MQTT topic as the other device, will also receive the message sent to that topic. +Note that other devices that are connected to the same AWS MQTT broker receive the same messages if: -MQTT is used to receive notification that an update is available, and to retrieve metadata about the update. -HTTP is used to download the update payload. +* The other device has valid (but different) certificates that use the same AWS IoT policy as the original device. +* The other device is subscribed to the same MQTT topic as the original device. -It is up to the application that uses the library to restart when the FOTA is complete. +The library uses MQTT to receive notification that an update is available, and to retrieve metadata about the update. +It uses HTTP to download the update payload. + +It is up to the application that uses the library to restart the device when the FOTA is complete. + +The AWS FOTA library is used in the :ref:`aws_fota_sample` sample. Configuration ************* -Use Kconfig to configure the MQTT payload buffer sizes and the buffers used to store the version string, host name, and file path. +Configure the following parameters when using this library: + +- :option:`CONFIG_AWS_FOTA_PAYLOAD_SIZE` +- :option:`CONFIG_AWS_FOTA_VERSION_STRING_MAX_LEN` +- :option:`CONFIG_AWS_FOTA_HOSTNAME_MAX_LEN` +- :option:`CONFIG_AWS_FOTA_FILE_PATH_MAX_LEN` Implementation ************** -The implementation uses the job document shown below for passing information from AWS Jobs to the device: +The following sequence diagram shows how a firmware over-the-air update is implemented through the use of `AWS IoT MQTT`_, `AWS IoT jobs`_, and `AWS Simple Storage Service (S3)`_. + +.. figure:: /images/aws_fota_dfu_sequence.svg + :alt: AWS FOTA sequence diagram for doing FOTA through AWS jobs + + +AWS S3 server +============= + +The firmware files for download must be stored in a bucket on an AWS S3 server. +You can use an AWS instance that is already set up, such as `nRF Cloud`_, or set up your own bucket. + +When setting up your own bucket, make sure to configure the permissions as shown in the following screenshot: + +.. figure:: /images/aws_s3_bucket_permissions.png + :alt: Bucket permissions in AWS S3 -.. code-block:: javascript +To update the permissions for an existing bucket, select your bucket and navigate to **Permissions** > **Block public access**. + +In addition to the permissions, you must configure a bucket policy. +To determine a suitable security scheme for your application, see `AWS S3 Developer Guide: Using Bucket Policies and User Policies`_ and `AWS S3 Developer Guide: Bucket Policy Examples`_. +To configure the policy, select your bucket and navigate to **Permissions** > **Bucket Policy**. + +For testing purposes, you can use the following, very permissive, bucket policy (replace *bucket_name* with the name of your bucket): + +.. parsed-literal:: + :class: highlight + + { "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": "*", + "Action": "s3:GetObject", + "Resource": "arn:aws:s3:::*bucket_name*/\*" + } + ] + } + + +AWS IoT jobs +============ + +The implementation uses a job document similar to the following (where *bucket_name* is the name of your bucket and *file_name* is the name of your file) for passing information from `AWS IoT jobs`_ to the device: + +.. parsed-literal:: + :class: highlight { "operation": "app_fw_update", @@ -35,26 +88,21 @@ The implementation uses the job document shown below for passing information fro "size": 181124, "location": { "protocol": "http:", - "host": "s3.amazonaws.com", - "path": "/nordic-firmware-files/0943dfbf-cb10-4eb7-8277-a8b179eaf4ff" + "host": "*bucket_name*.amazonaws.com", + "path": "*file_name*.bin" } } -The current implementation only uses information from `host` and `path` in this document. - -The following sequence diagram shows how a FOTA is implemented through the use of `AWS IoT Jobs `_, `AWS IoT MQTT `_, and `AWS S3 `_ in this library. +The current implementation uses information from the ``host`` and ``path`` fields only. -.. figure:: ../../doc/nrf/images/aws_fota_dfu_sequence.svg - :alt: AWS FOTA sequence diagram for doing FOTA through AWS Jobs Limitations *********** -* Currently, the library only uses HTTP for downloading the firmware. - It is, however, possible to have it work with HTTPS - see :ref:`lib_download_client`. - These changes need to be applied to :ref:`lib_fota_download` to enable downloading firmware through HTTPS. -* The library requires content-range header to be present in the HTTP response from the server. - This limitation is inherited from :ref:`lib_download_client` which is used by :ref:`lib_fota_download`. +* Currently, the library uses HTTP for downloading the firmware. + To use HTTPS instead, apply the changes described in :ref:`the HTTPS section of the download client documentation ` to the :ref:`lib_fota_download` library. +* The library requires a Content-Range header to be present in the HTTP response from the server. + This limitation is inherited from the :ref:`lib_download_client` library. API documentation ***************** diff --git a/include/net/aws_jobs.rst b/include/net/aws_jobs.rst index 8a374c013660..790988bdee5b 100644 --- a/include/net/aws_jobs.rst +++ b/include/net/aws_jobs.rst @@ -1,35 +1,32 @@ .. _lib_aws_jobs: -AWS Jobs +AWS jobs ######## -This library provides functions for working with the `AWS IoT jobs -`_ -service. +The Amazon Web Services (AWS) jobs library provides functions for working with the `AWS IoT jobs`_ service. -The AWS Jobs library provides APIs that can be used to: - -- report status, -- subscribe to job topics. +You can use the library to report the status of AWS IoT jobs and to subscribe to job topics. The module also contains the following elements: -- string templates for topics, -- defines for lengths of topics, status, and job IDs, -- defines for Subscribe message IDs. +- String templates that can be used for generating MQTT topics +- Defines for lengths of topics, status, and job IDs +- Defines for subscribe message IDs -This library assumes that all strings can be UTF-8 formatted. +This library assumes that all strings can be formatted in UTF-8. Configuration ************* -Use Kconfig to configure the MQTT message buffer size. +Configure the following parameters when using this library: + +- :option:`CONFIG_UPDATE_JOB_PAYLOAD_LEN` API documentation ***************** -| Header file: :file:`include/aws_jobs.h` +| Header file: :file:`include/net/aws_jobs.h` | Source files: :file:`subsys/net/lib/aws_jobs/` .. doxygengroup:: aws_jobs diff --git a/include/net/bsdlib.h b/include/net/bsdlib.h index 952a01eae19d..1c88c675a2e3 100644 --- a/include/net/bsdlib.h +++ b/include/net/bsdlib.h @@ -18,6 +18,17 @@ */ int bsdlib_init(void); +/** + * @brief Get the last return value of bsdlib_init. + * + * This function can be used to access the last return value of + * bsdlib_init. This can be used to check the state of a modem + * firmware exchange when bsdlib was initialized at boot-time. + * + * @return int The last return value of bsdlib_init. + */ +int bsdlib_get_init_ret(void); + /** * @brief Shutdown bsdlib, releasing its resources. * diff --git a/include/net/cloud.h b/include/net/cloud.h index 1732c97b8e97..cba3e76df88e 100644 --- a/include/net/cloud.h +++ b/include/net/cloud.h @@ -36,6 +36,7 @@ enum cloud_event_type { CLOUD_EVT_DATA_RECEIVED, CLOUD_EVT_PAIR_REQUEST, CLOUD_EVT_PAIR_DONE, + CLOUD_EVT_FOTA_DONE, CLOUD_EVT_COUNT }; @@ -53,6 +54,7 @@ enum cloud_endpoint { CLOUD_EP_TOPIC_STATE, CLOUD_EP_TOPIC_CONFIG, CLOUD_EP_TOPIC_PAIR, + CLOUD_EP_TOPIC_BATCH, CLOUD_EP_URI, CLOUD_EP_COUNT }; diff --git a/include/net/download_client.h b/include/net/download_client.h index a45d3e0e18d2..79e60de0f8ca 100644 --- a/include/net/download_client.h +++ b/include/net/download_client.h @@ -31,25 +31,33 @@ extern "C" { * @brief Download client event IDs. */ enum download_client_evt_id { - /** Event contains a fragment. */ + /** + * Event contains a fragment. + * The application may return any non-zero value to stop the download. + */ DOWNLOAD_CLIENT_EVT_FRAGMENT, - /** Download complete. */ - DOWNLOAD_CLIENT_EVT_DONE, /** * An error has occurred during download and * the connection to the server has been lost. - * - ENOTCONN: error reading from socket + * + * Error reason may be one of the following: + * - ENOTCONN: socket error during send() or recv() * - ECONNRESET: peer closed connection - * - EBADMSG: HTTP response header not - * as expected + * - EBADMSG: HTTP response header not as expected + * + * In case of network-related errors (ENOTCONN or ECONNRESET), + * returning zero from the callback will let the library attempt + * to reconnect to the server and download the last fragment again. + * Otherwise, the application may return any non-zero value + * to stop the download. * - * In both cases, the application should - * disconnect (@ref download_client_disconnect) - * and connect (@ref download_client_connect) - * before reattempting the download, to - * reinitialize the network socket. + * In case the download is stopped, the application should manually + * disconnect (@ref download_client_disconnect) to clean up the + * network socket as necessary before re-attempting the download. */ DOWNLOAD_CLIENT_EVT_ERROR, + /** Download complete. */ + DOWNLOAD_CLIENT_EVT_DONE, }; /** diff --git a/include/net/download_client.rst b/include/net/download_client.rst index acf91c185251..a694089046aa 100644 --- a/include/net/download_client.rst +++ b/include/net/download_client.rst @@ -18,7 +18,7 @@ The download happens in a separate thread which can be paused and resumed. Make sure to configure :option:`CONFIG_DOWNLOAD_CLIENT_MAX_FRAGMENT_SIZE` in a way that suits your application. A large fragment size requires more RAM, while a small fragment size results in more download requests, and thus a higher protocol overhead. If the size of the file being downloaded is larger than a hundred times the size of one fragment, the server might close the HTTP connection -after the hundreth fragment has been transfered. The library is able to detect when the server has closed the HTTP connection +after the hundredth fragment has been transferred. The library is able to detect when the server has closed the HTTP connection and reconnect automatically. Increasing the fragment size prevents having to establish several HTTP connections and thus helps in keeping protocol overhead to a minimum. @@ -38,14 +38,16 @@ For HTTP, the following requirements must be met: * IETF RFC 7233 is supported by the HTTP Server. * :option:`CONFIG_DOWNLOAD_CLIENT_MAX_RESPONSE_SIZE` is configured so that it can contain the entire HTTP response. +.. _download_client_https: + HTTPS ===== The library uses TLS version 1.2. When using HTTPS, the application must provision the TLS credentials and pass the security tag to the library when calling :cpp:func:`download_client_connect`. -To provision a TLS certificate to the modem, the application can use the nrf_inbuilt_key APIs (see the :file:`nrf_inbuilt_key.h` file in the `nrfxlib`_ repository). -The following snippet illustrates how to provision a TLS certificate, associate it to TLS security tag, and pass that tag to the library. +To provision a TLS certificate to the modem, use :cpp:func:`nrf_inbuilt_key_write` and other nrf_inbuilt_key APIs from the :file:`bdslib/include/nrf_inbuilt_key.h` file in the `nrfxlib`_ repository. +The following snippet illustrates how to provision a TLS certificate, associate it to a TLS security tag, and pass that tag to the library. .. code:: @@ -132,6 +134,16 @@ The following snippet illustrates how to provision a TLS certificate, associate } +Limitations +*********** + +The library requires the host server to provide a Content-Range field in the HTTP GET header. +If this header field is missing, the library logs the following error:: + + download_client: Server did not send "Content-Range" in response + +To debug your application when getting such an error, set the log level of the library to debug (:option:`CONFIG_DOWNLOAD_CLIENT_LOG_LEVEL_DBG`) and select :option:`CONFIG_DOWNLOAD_CLIENT_LOG_HEADERS`. + API documentation ***************** diff --git a/include/net/fota_download.rst b/include/net/fota_download.rst index 0910335988b6..5560a9790893 100644 --- a/include/net/fota_download.rst +++ b/include/net/fota_download.rst @@ -1,22 +1,28 @@ .. _lib_fota_download: -FOTA Download +FOTA download ############# -The firmware over-the-air (FOTA) download library provides functions to download a firmware file as an upgrade candidate to the secondary slot of MCUboot. +The firmware over-the-air (FOTA) download library provides functions for downloading a firmware file as an upgrade candidate to the DFU target that is used in the :ref:`lib_dfu_target` library. -This is done using the :ref:`lib_download_client` and ``flash_img`` libraries. +The library uses the :ref:`lib_download_client` to download the image. +Once the download has been started, all received data fragments are passed to the :ref:`lib_dfu_target` library. +The :ref:`lib_dfu_target` library takes care of where the upgrade candidate is stored, depending on the image type that is being downloaded. -Once the download has been started, all received data fragments are passed to the ``flash_img`` library, which stores them in the appropriate memory address for an firmware upgrade candidate. +When the download client sends the event indicating that the download has completed, the received firmware is tagged as an upgrade candidate, and the download client is instructed to disconnect from the server. +The library then sends a :cpp:enumerator:`FOTA_DOWNLOAD_EVT_FINISHED` callback event. +When the consumer of the library receives this event, it should issue a reboot command to apply the upgrade. -When the download client sends the event indicating that the download has completed, the last data fragments are flushed to persistent memory, and the received firmware is tagged as an upgrade candidate. -Lastly the download client is told to disconnect from the server. +By default, the FOTA download library uses HTTP for downloading the firmware file. +To use HTTPS instead, apply the changes described in :ref:`the HTTPS section of the download client documentation ` to the library. + +The FOTA download library is used in the :ref:`http_application_update_sample` sample. API documentation ***************** -| Header file: :file:`include/fota_download.h` +| Header file: :file:`include/net/fota_download.h` | Source files: :file:`subsys/net/lib/fota_download/src/` .. doxygengroup:: fota_download diff --git a/include/net/mqtt_socket.h b/include/net/mqtt_socket.h index 8a44c48cf128..0073cf9ffaab 100644 --- a/include/net/mqtt_socket.h +++ b/include/net/mqtt_socket.h @@ -320,7 +320,7 @@ struct mqtt_sec_config { u32_t sec_tag_count; /** Indicates the list of security tags to be used for the session. */ - sec_tag_t *seg_tag_list; + sec_tag_t *sec_tag_list; /** Peer hostname for ceritificate verification. * May be NULL to skip hostname verification. diff --git a/include/nrf_cloud.h b/include/nrf_cloud.h index d6f60ed59f63..d358ec127bb0 100644 --- a/include/nrf_cloud.h +++ b/include/nrf_cloud.h @@ -42,6 +42,8 @@ enum nrf_cloud_evt_type { NRF_CLOUD_EVT_SENSOR_DATA_ACK, /** The transport was disconnected. */ NRF_CLOUD_EVT_TRANSPORT_DISCONNECTED, + /** The device should be restarted to apply a firmware upgrade */ + NRF_CLOUD_EVT_FOTA_DONE, /** There was an error communicating with the cloud. */ NRF_CLOUD_EVT_ERROR = 0xFF }; diff --git a/include/profiler.h b/include/profiler.h index e64433a9cb72..2af0fdded0ee 100644 --- a/include/profiler.h +++ b/include/profiler.h @@ -16,6 +16,8 @@ #include +#include +#include #ifndef CONFIG_MAX_NUMBER_OF_CUSTOM_EVENTS /** Maximum number of custom events. */ diff --git a/include/secure_services.h b/include/secure_services.h index 08a5b21c94c4..cedd1a786bdc 100644 --- a/include/secure_services.h +++ b/include/secure_services.h @@ -22,6 +22,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -65,6 +66,16 @@ int spm_request_random_number(u8_t *output, size_t len, size_t *olen); */ int spm_request_read(void *destination, u32_t addr, size_t len); +/** Search for the firmware_info structure in firmware image located at address. + * + * @param[in] firmware_address Address where firmware image is stored. + * @param[out] info Pointer to where found info is stored. + * + * @retval 0 If successful. + * @retval -EINVAL If info is NULL. + * @retval -EFAULT If no info is found. + */ +int spm_firmware_info(u32_t fw_address, struct fw_info *info); #ifdef __cplusplus } diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 7b37880ec0b8..f57d8fcbd86f 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -7,6 +7,7 @@ add_subdirectory_ifdef(CONFIG_BSD_LIBRARY bsdlib) add_subdirectory_ifdef(CONFIG_LWM2M_CARRIER lwm2m_carrier) add_subdirectory_ifdef(CONFIG_DK_LIBRARY dk_buttons_and_leds) +add_subdirectory_ifdef(CONFIG_AT_NOTIF at_notif) add_subdirectory_ifdef(CONFIG_AT_HOST_LIBRARY at_host) add_subdirectory_ifdef(CONFIG_AT_CMD_PARSER at_cmd_parser) add_subdirectory_ifdef(CONFIG_MODEM_INFO modem_info) diff --git a/lib/Kconfig b/lib/Kconfig index c6e46be42c2b..30e7f174a29e 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -10,6 +10,8 @@ rsource "bsdlib/Kconfig" rsource "lwm2m_carrier/Kconfig" +rsource "at_notif/Kconfig" + rsource "at_host/Kconfig" rsource "dk_buttons_and_leds/Kconfig" diff --git a/lib/at_cmd_parser/at_cmd_parser.c b/lib/at_cmd_parser/at_cmd_parser.c index 6c3b12ade1e3..57263675a53c 100644 --- a/lib/at_cmd_parser/at_cmd_parser.c +++ b/lib/at_cmd_parser/at_cmd_parser.c @@ -24,6 +24,7 @@ enum at_parser_state { NUMBER, SMS_PDU, NOTIFICATION, + COMMAND, OPTIONAL, }; @@ -49,6 +50,15 @@ static int at_parse_detect_type(const char **str, int index) * notification ID, (eg +CEREG:) */ set_new_state(NOTIFICATION); + } else if ((index == 0) && is_command(tmpstr)) { + /* Next, check if we deal with command (eg AT+CCLK) */ + set_new_state(COMMAND); + } else if (index == 0) { + /* If the string start without an notification + * ID, we treat the whole string as one string + * parameter + */ + set_new_state(STRING); } else if ((index > 0) && is_notification(*tmpstr)) { /* If notifications is detected later in the @@ -81,13 +91,6 @@ static int at_parse_detect_type(const char **str, int index) } else if (is_lfcr(*tmpstr) && (state == OPTIONAL)) { set_new_state(OPTIONAL); - } else if ((index == 0) && - !is_notification(*tmpstr)) { - /* If the string start without an notification - * ID, we treat the whole string as one string - * parameter - */ - set_new_state(STRING); } else if (is_separator(*tmpstr)) { /* If a separator is detected we have detected * and empty optional parameter @@ -125,7 +128,17 @@ static int at_parse_process_element(const char **str, at_params_string_put(list, index, start_ptr, tmpstr - start_ptr); + } else if (state == COMMAND) { + const char *start_ptr = tmpstr; + tmpstr += sizeof("AT+") - 1; + + while (is_valid_notification_char(*tmpstr)) { + tmpstr++; + } + + at_params_string_put(list, index, start_ptr, + tmpstr - start_ptr); } else if (state == OPTIONAL) { at_params_empty_put(list, index); diff --git a/lib/at_cmd_parser/at_utils.h b/lib/at_cmd_parser/at_utils.h index 9d726e9ab39f..81137333ac2a 100644 --- a/lib/at_cmd_parser/at_utils.h +++ b/lib/at_cmd_parser/at_utils.h @@ -20,11 +20,13 @@ #include #define AT_PARAM_SEPARATOR ',' -#define AT_CMD_SEPARATOR ':' +#define AT_RSP_SEPARATOR ':' +#define AT_CMD_SEPARATOR '=' #define AT_CMD_BUFFER_TERMINATOR 0 #define AT_CMD_STRING_IDENTIFIER '\"' #define AT_STANDARD_NOTIFICATION_PREFIX '+' #define AT_PROP_NOTIFICATION_PREFX '%' +#define AT_CUSTOM_COMMAND_PREFX '#' /** * @brief Check if character is a notification start character @@ -47,6 +49,33 @@ static inline bool is_notification(char chr) return false; } +/** + * @brief Check if a string is a beginning of an AT command + * + * This function will check if the character is a "AT+" or "AT%" which + * identifies an AT command. + * + * @param[in] str String to examine + * + * @retval true If the string is an AT command + * @retval false Otherwise + */ +static inline bool is_command(const char *str) +{ + if (strlen(str) < 3) { + return false; + } + + if ((toupper(str[0]) == 'A') && (toupper(str[1]) == 'T') && + ((str[2] == AT_STANDARD_NOTIFICATION_PREFIX) || + (str[2] == AT_PROP_NOTIFICATION_PREFX) || + (str[2] == AT_CUSTOM_COMMAND_PREFX))) { + return true; + } + + return false; +} + /** * @brief Verify that the character is a valid character * @@ -102,6 +131,7 @@ static inline bool is_terminated(char chr) static inline bool is_separator(char chr) { if ((chr == AT_PARAM_SEPARATOR) || + (chr == AT_RSP_SEPARATOR) || (chr == AT_CMD_SEPARATOR)) { return true; } diff --git a/lib/at_host/Kconfig b/lib/at_host/Kconfig index 5c742549765a..1cd8d8de737e 100644 --- a/lib/at_host/Kconfig +++ b/lib/at_host/Kconfig @@ -9,6 +9,7 @@ menu "AT Host Library for nrf91" config AT_HOST_LIBRARY bool "AT Host Library for nrf91" select AT_CMD + select AT_NOTIF if AT_HOST_LIBRARY diff --git a/lib/at_host/at_host.c b/lib/at_host/at_host.c index d3d567c29a52..fb27d4b88815 100644 --- a/lib/at_host/at_host.c +++ b/lib/at_host/at_host.c @@ -12,6 +12,7 @@ #include #include #include +#include LOG_MODULE_REGISTER(at_host, CONFIG_AT_HOST_LOG_LEVEL); @@ -67,8 +68,10 @@ static inline void write_uart_string(char *str) } } -static void response_handler(char *response) +static void response_handler(void *context, char *response) { + ARG_UNUSED(context); + /* Forward the data over UART */ write_uart_string(response); } @@ -279,7 +282,11 @@ static int at_host_init(struct device *arg) return -EINVAL; } - at_cmd_set_notification_handler(response_handler); + err = at_notif_register_handler(NULL, response_handler); + if (err != 0) { + LOG_ERR("Can't register handler err=%d", err); + return err; + } /* Initialize the UART module */ err = at_uart_init(uart_dev_name); diff --git a/lib/at_notif/CMakeLists.txt b/lib/at_notif/CMakeLists.txt new file mode 100644 index 000000000000..86eaabc81ff1 --- /dev/null +++ b/lib/at_notif/CMakeLists.txt @@ -0,0 +1,9 @@ +# +# Copyright (c) 2019 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic +# + +zephyr_include_directories(.) +zephyr_library() +zephyr_library_sources(at_notif.c) diff --git a/lib/at_notif/Kconfig b/lib/at_notif/Kconfig new file mode 100644 index 000000000000..03d01a269d8c --- /dev/null +++ b/lib/at_notif/Kconfig @@ -0,0 +1,24 @@ +# +# Copyright (c) 2019 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic +# + +menuconfig AT_NOTIF + bool "AT-command notification manager" + depends on AT_CMD + help + A library for managing AT-command notifications. + +if AT_NOTIF + +config AT_NOTIF_SYS_INIT + bool "Initialize the AT-command notification manager during system init" + default y if AT_CMD_SYS_INIT + +module=AT_NOTIF +module-dep=LOG +module-str= AT-command notification management library +source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config" + +endif # AT_NOTIF diff --git a/lib/at_notif/at_notif.c b/lib/at_notif/at_notif.c new file mode 100644 index 000000000000..8b59ae0a071f --- /dev/null +++ b/lib/at_notif/at_notif.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(at_notif, CONFIG_AT_NOTIF_LOG_LEVEL); + +static K_MUTEX_DEFINE(list_mtx); + +/**@brief Link list element for notification handler. */ +struct notif_handler { + sys_snode_t node; + void *ctx; + at_notif_handler_t handler; +}; + +static sys_slist_t handler_list; + + +/**@brief Find notification handler. + * + * @note Returns next and writes previous to prev. + */ +static struct notif_handler *find_node(struct notif_handler **prev_out, + void *ctx, at_notif_handler_t handler) +{ + struct notif_handler *prev = NULL, *curr, *tmp; + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&handler_list, curr, tmp, node) { + if (curr->ctx == ctx && curr->handler == handler) { + *prev_out = prev; + return curr; + } + prev = curr; + } + return NULL; +} + +/**@brief Append notification handler. */ +static int append_notif_handler(void *ctx, at_notif_handler_t handler) +{ + struct notif_handler *to_ins; + + k_mutex_lock(&list_mtx, K_FOREVER); + + /* Check if it exists */ + if (find_node(&to_ins, ctx, handler) != NULL) { + LOG_DBG("Already in the list.\n"); + k_mutex_unlock(&list_mtx); + return 0; + } + + /* Allocate memory and fill. */ + to_ins = (struct notif_handler *)k_malloc(sizeof(struct notif_handler)); + if (to_ins == NULL) { + k_mutex_unlock(&list_mtx); + return -ENOBUFS; + } + memset(to_ins, 0, sizeof(struct notif_handler)); + to_ins->ctx = ctx; + to_ins->handler = handler; + + /* Append. */ + sys_slist_append(&handler_list, &to_ins->node); + + k_mutex_unlock(&list_mtx); + return 0; +} + +/**@brief Remove notification handler. */ +static int remove_notif_handler(void *ctx, at_notif_handler_t handler) +{ + struct notif_handler *curr, *prev = NULL; + + k_mutex_lock(&list_mtx, K_FOREVER); + + /* Check if it exists */ + curr = find_node(&prev, ctx, handler); + if (curr == NULL) { + LOG_DBG("Not found.\n"); + k_mutex_unlock(&list_mtx); + return -ENXIO; + } + + /* Remove */ + sys_slist_remove(&handler_list, &prev->node, &curr->node); + k_free(curr); + + k_mutex_unlock(&list_mtx); + return 0; +} + +/**@brief Dispatcher for notifications. */ +static void notif_dispatch(char *response) +{ + struct notif_handler *curr, *tmp; + + k_mutex_lock(&list_mtx, K_FOREVER); + + LOG_DBG("Dispatching events.\n"); + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&handler_list, curr, tmp, node) { + LOG_DBG("ctx=0x%08X, handler=0x%08X", (u32_t)curr->ctx, + (u32_t)curr->handler); + curr->handler(curr->ctx, response); + } + + k_mutex_unlock(&list_mtx); +} + +static int module_init(struct device *dev) +{ + ARG_UNUSED(dev); + + LOG_DBG("Initialization"); + sys_slist_init(&handler_list); + at_cmd_set_notification_handler(notif_dispatch); + return 0; +} + +int at_notif_init(void) +{ + return module_init(NULL); +} + +int at_notif_register_handler(void *context, at_notif_handler_t handler) +{ + if (handler == NULL) { + LOG_DBG("context=0x%08X, handler=0x%08X", (u32_t)context, + (u32_t)handler); + return -EINVAL; + } + return append_notif_handler(context, handler); +} + +int at_notif_deregister_handler(void *context, at_notif_handler_t handler) +{ + if (handler == NULL) { + LOG_DBG("context=0x%08X, handler=0x%08X", (u32_t)context, + (u32_t)handler); + return -EINVAL; + } + return remove_notif_handler(context, handler); +} + +#ifdef CONFIG_AT_NOTIF_SYS_INIT +SYS_INIT(module_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); +#endif diff --git a/lib/bsdlib/Kconfig b/lib/bsdlib/Kconfig index d0cbaa241e7f..6a5ae37ab7fa 100644 --- a/lib/bsdlib/Kconfig +++ b/lib/bsdlib/Kconfig @@ -10,7 +10,7 @@ config BSD_LIBRARY prompt "Use BSD Socket library for IP/TLS/DTLS" select FLOAT select FP_SHARING - select NET_SOCKETS_OFFLOAD + imply NET_SOCKETS_OFFLOAD depends on TRUSTED_EXECUTION_NONSECURE help Use Nordic BSD Socket library. diff --git a/lib/bsdlib/bsdlib.c b/lib/bsdlib/bsdlib.c index bc3b856b6d2c..a3ae6f3f6902 100644 --- a/lib/bsdlib/bsdlib.c +++ b/lib/bsdlib/bsdlib.c @@ -18,17 +18,17 @@ extern void ipc_proxy_irq_handler(void); +static int init_ret; + static int _bsdlib_init(struct device *unused) { - int err; - /* Setup the network IRQ used by the BSD library. * Note: No call to irq_enable() here, that is done through bsd_init(). */ IRQ_DIRECT_CONNECT(BSD_NETWORK_IRQ, BSD_NETWORK_IRQ_PRIORITY, ipc_proxy_irq_handler, 0); - err = bsd_init(); + init_ret = bsd_init(); if (IS_ENABLED(CONFIG_BSD_LIBRARY_SYS_INIT)) { /* bsd_init() returns values from a different namespace @@ -39,7 +39,7 @@ static int _bsdlib_init(struct device *unused) return 0; } - return err; + return init_ret; } int bsdlib_init(void) @@ -47,6 +47,11 @@ int bsdlib_init(void) return _bsdlib_init(NULL); } +int bsdlib_get_init_ret(void) +{ + return init_ret; +} + int bsdlib_shutdown(void) { bsd_shutdown(); diff --git a/lib/bsdlib/nrf91_sockets.c b/lib/bsdlib/nrf91_sockets.c index 33aaecdeb0aa..ea87c3dfd09c 100644 --- a/lib/bsdlib/nrf91_sockets.c +++ b/lib/bsdlib/nrf91_sockets.c @@ -266,6 +266,8 @@ static int z_to_nrf_family(sa_family_t z_family) return NRF_AF_LTE; case AF_LOCAL: return NRF_AF_LOCAL; + case AF_PACKET: + return NRF_AF_PACKET; case AF_UNSPEC: /* No NRF_AF_UNSPEC defined. */ default: @@ -284,6 +286,8 @@ static int nrf_to_z_family(nrf_socket_family_t nrf_family) return AF_LTE; case NRF_AF_LOCAL: return AF_LOCAL; + case NRF_AF_PACKET: + return AF_PACKET; default: return -EAFNOSUPPORT; } @@ -323,6 +327,8 @@ static int z_to_nrf_socktype(int socktype) switch (socktype) { case SOCK_MGMT: return NRF_SOCK_MGMT; + case SOCK_RAW: + return NRF_SOCK_RAW; default: return socktype; } @@ -934,18 +940,13 @@ static int nrf91_bsdlib_socket_offload_init(struct device *arg) static struct nrf91_socket_iface_data { struct net_if *iface; - u8_t dummy_link_addr; } nrf91_socket_iface_data; static void nrf91_socket_iface_init(struct net_if *iface) { nrf91_socket_iface_data.iface = iface; - /* FIXME Zephyr's interface initialization function checks for link - * address presence, set dummy placeholder for now. - */ - net_if_set_link_addr(iface, &nrf91_socket_iface_data.dummy_link_addr, 1, - NET_LINK_UNKNOWN); + iface->if_dev->offloaded = true; socket_offload_register(&nrf91_socket_offload_ops); } diff --git a/lib/lwm2m_carrier/Kconfig b/lib/lwm2m_carrier/Kconfig index 71722c6d411e..2bed21589056 100644 --- a/lib/lwm2m_carrier/Kconfig +++ b/lib/lwm2m_carrier/Kconfig @@ -9,7 +9,7 @@ menuconfig LWM2M_CARRIER depends on NEWLIB_LIBC depends on BSD_LIBRARY && !BSD_LIBRARY_SYS_INIT # Need a source of entropy - depends on TEST_RANDOM_GENERATOR + depends on ENTROPY_HAS_DRIVER # Flash settings depends on FLASH && FLASH_PAGE_LAYOUT depends on MPU_ALLOW_FLASH_WRITE diff --git a/lib/modem_info/modem_info.c b/lib/modem_info/modem_info.c index 94891a79c6b8..4ce196ab6f3a 100644 --- a/lib/modem_info/modem_info.c +++ b/lib/modem_info/modem_info.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -40,6 +41,7 @@ LOG_MODULE_REGISTER(modem_info); #define AT_CMD_SYSTEMMODE "AT%XSYSTEMMODE?" #define AT_CMD_IMSI "AT+CIMI" #define AT_CMD_IMEI "AT+CGSN" +#define AT_CMD_DATE_TIME "AT+CCLK?" #define AT_CMD_SUCCESS_SIZE 5 #define RSRP_DATA_NAME "rsrp" @@ -62,9 +64,10 @@ LOG_MODULE_REGISTER(modem_info); #define GPS_MODE_DATA_NAME "gpsMode" #define IMSI_DATA_NAME "imsi" #define MODEM_IMEI_DATA_NAME "imei" +#define DATE_TIME_DATA_NAME "dateTime" #define RSRP_PARAM_INDEX 1 -#define RSRP_PARAM_COUNT 3 +#define RSRP_PARAM_COUNT 5 #define RSRP_OFFSET_VAL 141 #define BAND_PARAM_INDEX 1 /* Index of desired parameter */ @@ -105,12 +108,15 @@ LOG_MODULE_REGISTER(modem_info); #define GPS_MODE_PARAM_INDEX 3 #define SYSTEMMODE_PARAM_COUNT 5 -#define IMSI_PARAM_INDEX 0 -#define IMSI_PARAM_COUNT 1 +#define IMSI_PARAM_INDEX 0 +#define IMSI_PARAM_COUNT 1 #define MODEM_IMEI_PARAM_INDEX 0 #define MODEM_IMEI_PARAM_COUNT 1 +#define DATE_TIME_PARAM_INDEX 1 +#define DATE_TIME_PARAM_COUNT 2 + struct modem_info_data { const char *cmd; const char *data_name; @@ -279,6 +285,14 @@ static const struct modem_info_data imei_data = { .data_type = AT_PARAM_TYPE_STRING, }; +static const struct modem_info_data date_time_data = { + .cmd = AT_CMD_DATE_TIME, + .data_name = DATE_TIME_DATA_NAME, + .param_index = DATE_TIME_PARAM_INDEX, + .param_count = DATE_TIME_PARAM_COUNT, + .data_type = AT_PARAM_TYPE_STRING, +}; + static const struct modem_info_data *const modem_data[] = { [MODEM_INFO_RSRP] = &rsrp_data, [MODEM_INFO_CUR_BAND] = &band_data, @@ -300,6 +314,7 @@ static const struct modem_info_data *const modem_data[] = { [MODEM_INFO_GPS_MODE] = &gps_mode_data, [MODEM_INFO_IMSI] = &imsi_data, [MODEM_INFO_IMEI] = &imei_data, + [MODEM_INFO_DATE_TIME] = &date_time_data, }; static rsrp_cb_t modem_info_rsrp_cb; @@ -338,7 +353,7 @@ static int modem_info_parse(const struct modem_info_data *modem_data, } param_index = at_params_valid_count_get(&m_param_list); - if (param_index != modem_data->param_count) { + if (param_index > modem_data->param_count) { return -EAGAIN; } @@ -469,8 +484,10 @@ int modem_info_string_get(enum modem_info info, char *buf) return len <= 0 ? -ENOTSUP : len; } -static void modem_info_rsrp_subscribe_handler(char *response) +static void modem_info_rsrp_subscribe_handler(void *context, char *response) { + ARG_UNUSED(context); + u16_t param_value; int err; @@ -501,7 +518,12 @@ int modem_info_rsrp_register(rsrp_cb_t cb) { modem_info_rsrp_cb = cb; - at_cmd_set_notification_handler(modem_info_rsrp_subscribe_handler); + int rc = at_notif_register_handler(NULL, + modem_info_rsrp_subscribe_handler); + if (rc != 0) { + LOG_ERR("Can't register handler rc=%d", rc); + return rc; + } if (at_cmd_write(AT_CMD_CESQ_ON, NULL, 0, NULL) != 0) { return -EIO; diff --git a/lib/modem_info/modem_info_params.c b/lib/modem_info/modem_info_params.c index 733dedf8ce9a..288138a06afd 100644 --- a/lib/modem_info/modem_info_params.c +++ b/lib/modem_info/modem_info_params.c @@ -31,6 +31,7 @@ int modem_info_params_init(struct modem_param_info *modem) modem->network.lte_mode.type = MODEM_INFO_LTE_MODE; modem->network.nbiot_mode.type = MODEM_INFO_NBIOT_MODE; modem->network.gps_mode.type = MODEM_INFO_GPS_MODE; + modem->network.date_time.type = MODEM_INFO_DATE_TIME; modem->sim.uicc.type = MODEM_INFO_UICC; modem->sim.iccid.type = MODEM_INFO_ICCID; @@ -52,10 +53,10 @@ static int area_code_parse(struct lte_param *area_code) return -EINVAL; } - area_code->value_string[2] = '\0'; + area_code->value_string[4] = '\0'; /* Parses the string, interpreting its content as an */ /* integral number with base 16. (Hexadecimal) */ - area_code->value = (double)strtol(area_code->value_string, NULL, 16); + area_code->value = strtol(area_code->value_string, NULL, 16); return 0; } @@ -143,6 +144,7 @@ int modem_info_params_get(struct modem_param_info *modem) ret += modem_data_get(&modem->network.lte_mode); ret += modem_data_get(&modem->network.nbiot_mode); ret += modem_data_get(&modem->network.gps_mode); + ret += modem_data_get(&modem->network.date_time); ret += mcc_mnc_parse(&modem->network.current_operator, &modem->network.mcc, diff --git a/samples/bluetooth/llpm/CMakeLists.txt b/samples/bluetooth/llpm/CMakeLists.txt new file mode 100644 index 000000000000..627484f84b1a --- /dev/null +++ b/samples/bluetooth/llpm/CMakeLists.txt @@ -0,0 +1,16 @@ +# +# Copyright (c) 2019 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic +# + +cmake_minimum_required(VERSION 3.8.2) +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(NONE) + +FILE(GLOB app_sources src/*.c) +# NORDIC SDK APP START +target_sources(app PRIVATE + ${app_sources} +) +# NORDIC SDK APP END diff --git a/samples/bluetooth/llpm/README.rst b/samples/bluetooth/llpm/README.rst new file mode 100644 index 000000000000..9fb27c1c0960 --- /dev/null +++ b/samples/bluetooth/llpm/README.rst @@ -0,0 +1,306 @@ +.. _ble_llpm: + +Bluetooth: LLPM +############### + +The Bluetooth Low Latency Packet Mode (LLPM) sample uses the :ref:`latency_readme` and the :ref:`latency_c_readme` to showcase the LLPM proprietary Bluetooth extension from Nordic Semiconductor. +You can use it to determine the transmission latency of LLPM-enabled connections, or to compare with different connection parameters and check their influence on the results. + + +Overview +******** + +The LLPM is designed for applications in which the interface response time is critical for the user. +For example, for virtual reality headsets or gaming mouse and keyboard. + +See the following subsections for a description of the key LLPM elements. + +LLPM connection interval (1 ms) + The connection interval defines how often the devices must listen on the radio. + The LLPM introduces the possibility to reduce the connection interval below what is supported in BLE. + The lowest supported connection interval is 1 ms for one link. + +Physical layer (PHY) + Starting with Bluetooth 5, the over-the-air data rate in Bluetooth Low Energy supports 2 Ms/s (mega symbol per second), which allows for faster transmission. + The LLPM connection interval is only supported on *LE 2M PHY*. + Otherwise, the BLE controller will deny the request command. + +QoS connection event reports + When reports are enabled, one report will be generated on every connection event. + The report gives information about the quality of service of the connection event. + The values in the report are used to describe the quality of links. + For parameter descriptions, see :cpp:enum:`hci_vs_evt_qos_conn_event_report_t` (in :file:`ble_controller_hci_vs.h`). + +Transmission latency + The definition of the latency used in this example counts the time interval from the sender's application to the GATT service of the receiver. + It demonstrates the performance of an LLPM-enabled connection that the receiver will receive the data approximately every 1 ms. + +GATT Latency Service + To measure the transmission latency from application layers, a GATT Latency service `BT_UUID_LATENCY` is included to compute the time spent. + When the sender writes its timestamp to the `BT_UUID_LATENC_CHAR` characteristic of the receiver, the Latency service of the receiver will automatically reply back. + Whenever the sender receives a response, it will use its current time and the corresponding timestamp written before to estimate the round-trip time (RTT) of a writing characteristic procedure (see `Bluetooth Core Specification`_: Vol 3, Part F, 3.4.5 Writing attributes). + +.. list-table:: GATT Attributes + :header-rows: 1 + + * - Type + - UUID + - Property + * - Primary service + - BT_UUID_LATENCY + - Read only + * - Characteristic + - BT_UUID_LATENCY_CHAR + - BT_GATT_CHRC_WRITE, BT_GATT_PERM_WRITE + +This sample transmits data between two boards to measure the transmission latency in between. +One of the devices is connected as a *master* and another is connected as a *slave*. +The performance is evaluated with the transmission latency dividing the estimated round-trip time in half (RTT / 2). + +By default, the following values are used to demonstrates the interaction of the connection parameters: + +.. list-table:: Default parameter values + :header-rows: 1 + + * - Parameter + - Value + * - Connection interval + - 80 units (100 ms) + * - LLPM connection interval + - Lowest interval (1 ms) + * - Physical layer (PHY) + - LE 2M PHY + + +Requirements +************ + +* Two of the following nRF52-series development kit boards: + + * PCA10040 + * PCA10056 + * Other boards running BLE Controller variants that support LLPM (see :ref:`nrfxlib:ble_controller` Proprietary feature support) + + You can mix different boards. +* Connection to a computer with a serial terminal for each of the boards. + + +Building and running +******************** +.. |sample path| replace:: :file:`samples/bluetooth/llpm` + +.. include:: /includes/build_and_run.txt + + +Testing +======= + +After programming the sample to both boards, test it by performing the following steps: + +1. Connect to both boards with a terminal emulator (for example, PuTTY). + See :ref:`putty` for the required settings. +#. Reset both boards. +#. Observe that the boards establish a connection. + When they are connected, one of them serves as *master* and the other one as *slave*. + + - The master outputs the following information:: + + Press any key to set LLPM short connection connection interval (1 ms) + + - The slave outputs the following information:: + + Press any key to start measuring transmission latency + +#. Press a key in the terminal that is connected to the slave. +#. Observe the terminal connected to the slave. The latency measurements are printed in the terminal. + The latency is expected to be shorter than the default connection interval:: + + Transmission Latency: 80917 (us) + +#. Press a key in the terminal that is connected to the master. +#. Observe the connection gets updated to LLPM connection interval (1 ms) on both sides:: + + Connection interval updated: LLPM (1 ms) + +#. Observe the terminal connected to the slave. + The measured latency on the slave becomes approximate 1 ms:: + + Transmission Latency: 1098 (us) + +#. Press a key in the terminal that is connected to the master. +#. Observe the terminal connected to the master. + The measured latency on the master remains approximate 1 ms:: + + Transmission Latency: 1235 (us) + +.. msc:: + hscale = "1.3"; + Master,Slave; + Master<<=>>Slave [label="Connected"]; + Master<<=>>Slave [label="Discovered GATT Latency Service"]; + Slave note Slave [label="Press any key to start measuring transmission latency"]; + Slave note Slave [label="Read current timestamp: s1"]; + Slave=>Master [label="Write Request (timestamp: s1)"]; + Master>>Slave [label="Write Response"]; + Slave note Slave [label="Read current timestamp: s2"]; + Slave note Slave [label="Latency = (s2 - s1) / 2"]; + Master note Master [label="Press any key to set LLPM short connection connection interval (1 ms)"]; + Master<<=>>Slave [label="Switched to LLPM connection interval"]; + Master note Master [label="Press any key to start measuring transmission latency"]; + Master note Master [label="Read current timestamp: m1"]; + Master=>Slave [label="Write Request (timestamp: m1)"]; + Slave>>Master [label="Write Response"]; + Master note Master [label="Read current timestamp: m2"]; + Master note Master [label="Latency = (m2 - m1) / 2"]; + + +Sample output +============= + +The result should look similar to the following output. + +- For the master:: + + ***** Booting Zephyr OS build v1.14.99-ncs3-snapshot2-2647-gd6e67554cfeb ***** + Bluetooth initialized + LLPM mode enabled + Advertising successfully started + Scanning successfully started + Connection event reports enabled + Filter not match. Address: 08:c6:a4:e0:72:e9 (random) connectable: 0 + Filter not match. Address: 13:04:eb:f1:0b:46 (random) connectable: 0 + Filter not match. Address: 2b:74:72:c3:8f:a8 (random) connectable: 0 + Filter not match. Address: 02:ec:f9:bb:ec:27 (random) connectable: 0 + Filter not match. Address: 01:54:cf:d4:31:cd (random) connectable: 0 + Filter not match. Address: 3e:21:91:91:52:82 (random) connectable: 0 + Filter not match. Address: 08:c6:a4:e0:72:e9 (random) connectable: 0 + Filter not match. Address: 37:63:6a:ed:38:e2 (random) connectable: 0 + Filter not match. Address: 56:c6:75:17:80:d8 (random) connectable: 1 + Filters matched. Address: f9:3c:9c:d1:f6:07 (random) connectable: 1 + Connected as master + Conn. interval is 80 units (1.25 ms/unit) + QoS conn event reports: channel index 0x1f, CRC errors 0x00 + Service discovery completed + Press any key to set LLPM short connection interval (1 ms) + QoS conn event reports: channel index 0x07, CRC errors 0x00 + Press any key to start measuring transmission latency + Connection interval updated: LLPM (1 ms) + Transmission Latency: 1235 (us) + Transmission Latency: 1007 (us) + QoS conn event reports: channel index 0x22, CRC errors 0x00 + Transmission Latency: 1434 (us) + Transmission Latency: 1312 (us) + Transmission Latency: 1220 (us) + Transmission Latency: 991 (us) + Transmission Latency: 1419 (us) + QoS conn event reports: channel index 0x1a, CRC errors 0x00 + Transmission Latency: 1281 (us) + Transmission Latency: 1052 (us) + Transmission Latency: 991 (us) + Transmission Latency: 1403 (us) + Transmission Latency: 1296 (us) + Transmission Latency: 1052 (us) + Transmission Latency: 976 (us) + Transmission Latency: 1358 (us) + Transmission Latency: 1281 (us) + Transmission Latency: 1052 (us) + Transmission Latency: 976 (us) + QoS conn event reports: channel index 0x1d, CRC errors 0x00 + Transmission Latency: 1358 (us) + Transmission Latency: 1281 (us) + Transmission Latency: 1052 (us) + Transmission Latency: 976 (us) + Transmission Latency: 1358 (us) + QoS conn event reports: channel index 0x10, CRC errors 0x00 + Transmission Latency: 1281 (us) + +- For the slave:: + + ***** Booting Zephyr OS build v1.14.99-ncs3-snapshot2-2647-gd6e67554cfeb ***** + Bluetooth initialized + LLPM mode enabled + Advertising successfully started + Scanning successfully started + Connection event reports enabled + Filter not match. Address: 1d:18:b1:84:fd:05 (random) connectable: 0 + Filter not match. Address: 00:92:3f:a6:3f:48 (random) connectable: 0 + Filter not match. Address: 02:ec:f9:bb:ec:27 (random) connectable: 0 + Filter not match. Address: 3e:21:91:91:52:82 (random) connectable: 0 + Filter not match. Address: 08:c6:a4:e0:72:e9 (random) connectable: 0 + Filter not match. Address: 13:04:eb:f1:0b:46 (random) connectable: 0 + Filter not match. Address: cb:01:1a:2d:6e:ae (random) connectable: 1 + Filter not match. Address: 5c:f2:70:c2:3f:9f (random) connectable: 1 + Connected as slave + Conn. interval is 80 units (1.25 ms/unit) + QoS conn event reports: channel index 0x1f, CRC errors 0x00 + Service discovery completed + Press any key to start measuring transmission latency + QoS conn event reports: channel index 0x07, CRC errors 0x00 + Transmission Latency: 80917 (us) + Transmission Latency: 80841 (us) + Transmission Latency: 80749 (us) + Transmission Latency: 80673 (us) + Transmission Latency: 80596 (us) + Transmission Latency: 80505 (us) + Transmission Latency: 80429 (us) + Transmission Latency: 80337 (us) + Transmission Latency: 80261 (us) + Transmission Latency: 80184 (us) + Transmission Latency: 80093 (us) + Transmission Latency: 80017 (us) + Transmission Latency: 79940 (us) + Transmission Latency: 79849 (us) + Connection interval updated: LLPM (1 ms) + Transmission Latency: 81604 (us) + Transmission Latency: 30181 (us) + Transmission Latency: 1098 (us) + QoS conn event reports: channel index 0x22, CRC errors 0x00 + Transmission Latency: 1129 (us) + Transmission Latency: 1037 (us) + Transmission Latency: 930 (us) + Transmission Latency: 1312 (us) + Transmission Latency: 1083 (us) + Transmission Latency: 1007 (us) + + +Dependencies +************* + +This sample uses the following |NCS| libraries: + +* :ref:`latency_readme` +* :ref:`latency_c_readme` + +This sample uses the following `nrfxlib`_ libraries: + +* :ref:`nrfxlib:ble_controller` + +In addition, it uses the following Zephyr libraries: + +* :file:`include/console.h` +* :ref:`zephyr:kernel`: + + * :file:`include/kernel.h` + +* :file:`include/misc/printk.h` +* :file:`include/zephyr/types.h` +* :ref:`zephyr:bluetooth_api`: + + * :file:`include/bluetooth/bluetooth.h` + * :file:`include/bluetooth/conn.h` + * :file:`include/bluetooth/gatt.h` + * :file:`include/bluetooth/hci.h` + * :file:`include/bluetooth/uuid.h` + * :file:`include/bluetooth/scan.h` + * :file:`include/bluetooth/gatt_dm.h` + + +References +*********** + +For more information about the connection parameters that are used in this sample, see the following chapters of the `Bluetooth Core Specification`_: + +* Vol 3, Part F, 3.4.5 Writing attributes +* Vol 3, Part G, 4.9.3 Write Characteristic Value +* Vol 6, Part B, 5.1.1 Connection Update Procedure +* Vol 6, Part B, 5.1.10 PHY Update Procedure diff --git a/samples/bluetooth/llpm/prj.conf b/samples/bluetooth/llpm/prj.conf new file mode 100644 index 000000000000..dbf2fb12404a --- /dev/null +++ b/samples/bluetooth/llpm/prj.conf @@ -0,0 +1,29 @@ +# +# Copyright (c) 2019 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic +# + +CONFIG_CONSOLE_SUBSYS=y +CONFIG_CONSOLE_HANDLER=y +CONFIG_CONSOLE_GETCHAR=y + +CONFIG_BT_DEVICE_NAME="Nordic_LLPM" +CONFIG_BT=y +CONFIG_BT_DEBUG_LOG=y +CONFIG_BT_SMP=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_MAX_CONN=2 + +CONFIG_BT_SCAN=y +CONFIG_BT_SCAN_FILTER_ENABLE=y +CONFIG_BT_SCAN_UUID_CNT=1 + +CONFIG_BT_GATT_LATENCY=y +CONFIG_BT_GATT_LATENCY_C=y + +CONFIG_BT_LL_NRFXLIB=y +CONFIG_BT_HCI_VS_EVT_USER=y +CONFIG_HEAP_MEM_POOL_SIZE=2048 +CONFIG_BT_GAP_PERIPHERAL_PREF_PARAMS=n diff --git a/samples/bluetooth/llpm/sample.yaml b/samples/bluetooth/llpm/sample.yaml new file mode 100644 index 000000000000..1c15620d6fad --- /dev/null +++ b/samples/bluetooth/llpm/sample.yaml @@ -0,0 +1,9 @@ +sample: + description: BLE LLPM + name: BLE llpm +tests: + test_build: + build_only: true + build_on_all: true + platform_whitelist: nrf52_pca10040 nrf52840_pca10056 + tags: bluetooth ci_build diff --git a/samples/bluetooth/llpm/src/main.c b/samples/bluetooth/llpm/src/main.c new file mode 100644 index 000000000000..694c514ce829 --- /dev/null +++ b/samples/bluetooth/llpm/src/main.c @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVICE_NAME CONFIG_BT_DEVICE_NAME +#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1) +#define INTERVAL_MIN 0x50 /* 80 units, 100 ms */ +#define INTERVAL_MAX 0x50 /* 80 units, 100 ms */ +#define INTERVAL_LLPM 0x0D01 /* Proprietary 1 ms */ + +static volatile bool test_ready; +static struct bt_conn *default_conn; +static struct bt_gatt_latency gatt_latency; +static struct bt_gatt_latency_c gatt_latency_client; +static struct bt_le_conn_param *conn_param = + BT_LE_CONN_PARAM(INTERVAL_MIN, INTERVAL_MAX, 0, 400); +static struct bt_conn_info conn_info = {0}; + +static const struct bt_data ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA_BYTES(BT_DATA_UUID128_ALL, LATENCY_UUID), +}; + +static const struct bt_data sd[] = { + BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN), +}; + +void scan_filter_match(struct bt_scan_device_info *device_info, + struct bt_scan_filter_match *filter_match, + bool connectable) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(device_info->addr, addr, sizeof(addr)); + + printk("Filters matched. Address: %s connectable: %d\n", + addr, connectable); +} + +void scan_filter_no_match(struct bt_scan_device_info *device_info, + bool connectable) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(device_info->addr, addr, sizeof(addr)); + + printk("Filter does not match. Address: %s connectable: %d\n", + addr, connectable); +} + +void scan_connecting_error(struct bt_scan_device_info *device_info) +{ + printk("Connecting failed\n"); +} + +BT_SCAN_CB_INIT(scan_cb, scan_filter_match, scan_filter_no_match, + scan_connecting_error, NULL); + +static void scan_init(void) +{ + int err; + struct bt_le_scan_param scan_param = { + .type = BT_HCI_LE_SCAN_PASSIVE, + .filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_ENABLE, + .interval = 0x0010, + .window = 0x0010, + }; + + struct bt_scan_init_param scan_init = { + .connect_if_match = true, + .scan_param = &scan_param, + .conn_param = conn_param + }; + + bt_scan_init(&scan_init); + bt_scan_cb_register(&scan_cb); + + err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, BT_UUID_LATENCY); + if (err) { + printk("Scanning filters cannot be set\n"); + return; + } + + err = bt_scan_filter_enable(BT_SCAN_UUID_FILTER, false); + if (err) { + printk("Filters cannot be turned on\n"); + } +} + +static void discovery_complete(struct bt_gatt_dm *dm, void *context) +{ + struct bt_gatt_latency_c *latency = context; + + printk("Service discovery completed\n"); + + bt_gatt_dm_data_print(dm); + bt_gatt_latency_c_handles_assign(dm, latency); + bt_gatt_dm_data_release(dm); + + /* Start testing when the GATT service is discovered */ + test_ready = true; +} + +static void discovery_service_not_found(struct bt_conn *conn, void *context) +{ + printk("Service not found\n"); +} + +static void discovery_error(struct bt_conn *conn, int err, void *context) +{ + printk("Error while discovering GATT database: (%d)\n", err); +} + +struct bt_gatt_dm_cb discovery_cb = { + .completed = discovery_complete, + .service_not_found = discovery_service_not_found, + .error_found = discovery_error, +}; + +static void advertise_and_scan(void) +{ + int err; + + err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), + sd, ARRAY_SIZE(sd)); + if (err) { + printk("Advertising failed to start (err %d)\n", err); + return; + } + + printk("Advertising successfully started\n"); + + err = bt_scan_start(BT_SCAN_TYPE_SCAN_PASSIVE); + if (err) { + printk("Starting scanning failed (err %d)\n", err); + } + + printk("Scanning successfully started\n"); +} + +static void connected(struct bt_conn *conn, u8_t err) +{ + if (err) { + printk("Connection failed (err %u)\n", err); + return; + } + + default_conn = bt_conn_ref(conn); + err = bt_conn_get_info(default_conn, &conn_info); + if (err) { + printk("Error %d while getting bt conn info\n", err); + } + + printk("Connected as %s\n", + conn_info.role == BT_CONN_ROLE_MASTER ? "master" : "slave"); + printk("Conn. interval is %u units (1.25 ms/unit)\n", + conn_info.le.interval); + + /* make sure we're not scanning or advertising */ + bt_le_adv_stop(); + bt_scan_stop(); + + err = bt_gatt_dm_start(default_conn, BT_UUID_LATENCY, &discovery_cb, + &gatt_latency_client); + if (err) { + printk("Discover failed (err %d)\n", err); + } +} + +static void disconnected(struct bt_conn *conn, u8_t reason) +{ + printk("Disconnected (reason %u)\n", reason); + + test_ready = false; + + if (default_conn) { + bt_conn_unref(default_conn); + default_conn = NULL; + } + + advertise_and_scan(); +} + +static void le_param_updated(struct bt_conn *conn, u16_t interval, + u16_t latency, u16_t timeout) +{ + if (interval == INTERVAL_LLPM) { + printk("Connection interval updated: LLPM (1 ms)\n"); + } +} + +static int enable_llpm_mode(void) +{ + int err; + struct net_buf *buf; + hci_vs_cmd_llpm_mode_set_t *cmd_enable; + + buf = bt_hci_cmd_create(HCI_VS_OPCODE_CMD_LLPM_MODE_SET, + sizeof(*cmd_enable)); + if (!buf) { + printk("Could not allocate LLPM command buffer\n"); + } + + cmd_enable = net_buf_add(buf, sizeof(*cmd_enable)); + cmd_enable->enable = true; + + err = bt_hci_cmd_send(HCI_VS_OPCODE_CMD_LLPM_MODE_SET, buf); + if (err) { + printk("Error enabling LLPM %d\n", err); + } else { + printk("LLPM mode enabled\n"); + } + + return err; +} + +static int enable_llpm_short_connection_interval(void) +{ + int err; + struct bt_le_conn_param conn_param = { + .interval_min = INTERVAL_LLPM, + .interval_max = INTERVAL_LLPM, + .latency = 0, + .timeout = 300 + }; + + err = bt_conn_le_param_update(default_conn, &conn_param); + if (err) { + printk("Update connection parameters failed (err %d)\n", err); + } + + return err; +} + +static bool on_vs_evt(struct net_buf_simple *buf) +{ + u8_t code; + hci_vs_evt_qos_conn_event_report_t *evt; + + code = net_buf_simple_pull_u8(buf); + if (code != HCI_VS_SUBEVENT_CODE_QOS_CONN_EVENT_REPORT) { + return false; + } + + evt = (void *)buf->data; + if ((evt->event_counter & 0x3FF) == 0) { + printk("QoS conn event reports: " + "channel index 0x%02x, CRC errors 0x%02x\n", + evt->channel_index, evt->crc_error_count); + } + + return true; +} + +static int enable_qos_conn_evt_report(void) +{ + int err; + struct net_buf *buf; + + err = bt_hci_register_vnd_evt_cb(on_vs_evt); + if (err) { + printk("Failed registering vendor specific callback\n"); + } + + hci_vs_cmd_qos_conn_event_report_enable_t *cmd_enable; + + buf = bt_hci_cmd_create(HCI_VS_OPCODE_CMD_QOS_CONN_EVENT_REPORT_ENABLE, + sizeof(*cmd_enable)); + if (!buf) { + printk("Could not allocate command buffer\n"); + } + + cmd_enable = net_buf_add(buf, sizeof(*cmd_enable)); + cmd_enable->enable = true; + + err = bt_hci_cmd_send(HCI_VS_OPCODE_CMD_QOS_CONN_EVENT_REPORT_ENABLE, + buf); + if (err) { + printk("Could not send command buffer\n"); + } else { + printk("Connection event reports enabled\n"); + } + + return err; +} + +static void latency_response_handler(const void *buf, u16_t len) +{ + u32_t latency_time; + + if (len == sizeof(latency_time)) { + /* compute how long the time spent */ + latency_time = *((u32_t *)buf); + u32_t cycles_spent = k_cycle_get_32() - latency_time; + u32_t us_spent = SYS_CLOCK_HW_CYCLES_TO_NS(cycles_spent) / 2000; + + printk("Transmission Latency: %u (us)\n", us_spent); + } +} + +static const struct bt_gatt_latency_c_cb latency_client_cb = { + .latency_response = latency_response_handler +}; + +static void bt_ready(int err) +{ + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return; + } + + printk("Bluetooth initialized\n"); + + scan_init(); + + err = bt_gatt_latency_init(&gatt_latency, NULL); + if (err) { + printk("Latency service initialization failed (err %d)\n", err); + return; + } + + err = bt_gatt_latency_c_init(&gatt_latency_client, &latency_client_cb); + if (err) { + printk("Latency client initialization failed (err %d)\n", err); + return; + } + + if (enable_llpm_mode()) { + printk("Enable LLPM mode failed.\n"); + return; + } + + advertise_and_scan(); +} + +static void test_run(void) +{ + int err; + + if (!test_ready) { + /* disconnected while blocking inside _getchar() */ + return; + } + + test_ready = false; + + /* Switch to LLPM short connection interval */ + if (conn_info.role == BT_CONN_ROLE_MASTER) { + printk("Press any key to set LLPM short connection interval (1 ms)\n"); + console_getchar(); + + if (enable_llpm_short_connection_interval()) { + printk("Enable LLPM short connection interval failed\n"); + return; + } + } + + printk("Press any key to start measuring transmission latency\n"); + console_getchar(); + + /* Start sending the timestamp to its peer */ + while (default_conn) { + u32_t time = k_cycle_get_32(); + + err = bt_gatt_latency_c_request(&gatt_latency_client, &time, + sizeof(time)); + if (err && err != -EALREADY) { + printk("Latency failed (err %d)\n", err); + } + + k_sleep(200); /* sleep 200 ms*/ + } +} + +void main(void) +{ + int err; + static struct bt_conn_cb conn_callbacks = { + .connected = connected, + .disconnected = disconnected, + .le_param_updated = le_param_updated, + }; + + console_init(); + + err = bt_enable(bt_ready); + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return; + } + + if (enable_qos_conn_evt_report()) { + printk("Enable LLPM QoS failed.\n"); + return; + } + + bt_conn_cb_register(&conn_callbacks); + + for (;;) { + if (test_ready) { + test_run(); + } + } +} diff --git a/samples/bluetooth/peripheral_lbs/src/main.c b/samples/bluetooth/peripheral_lbs/src/main.c index 4db1e0cd1019..d25ae86a40c3 100644 --- a/samples/bluetooth/peripheral_lbs/src/main.c +++ b/samples/bluetooth/peripheral_lbs/src/main.c @@ -34,9 +34,6 @@ #define CON_STATUS_LED DK_LED2 #define RUN_LED_BLINK_INTERVAL 1000 -#define LED_ON 1 -#define LED_OFF 0 - #define USER_LED DK_LED3 #define USER_BUTTON DK_BTN1_MSK @@ -63,14 +60,14 @@ static void connected(struct bt_conn *conn, u8_t err) printk("Connected\n"); - dk_set_led(CON_STATUS_LED, LED_ON); + dk_set_led_on(CON_STATUS_LED); } static void disconnected(struct bt_conn *conn, u8_t reason) { printk("Disconnected (reason %u)\n", reason); - dk_set_led(CON_STATUS_LED, LED_OFF); + dk_set_led_off(CON_STATUS_LED); } #ifdef CONFIG_BT_GATT_LBS_SECURITY_ENABLED diff --git a/samples/bluetooth/peripheral_uart/prj.conf b/samples/bluetooth/peripheral_uart/prj.conf index fc6e3140c986..5f33548ef032 100644 --- a/samples/bluetooth/peripheral_uart/prj.conf +++ b/samples/bluetooth/peripheral_uart/prj.conf @@ -20,8 +20,8 @@ CONFIG_BT=y CONFIG_BT_PERIPHERAL=y CONFIG_BT_DEVICE_NAME="Nordic_UART_Service" CONFIG_BT_DEVICE_APPEARANCE=833 -CONFIG_BT_MAX_CONN=2 -CONFIG_BT_MAX_PAIRED=2 +CONFIG_BT_MAX_CONN=1 +CONFIG_BT_MAX_PAIRED=1 # Enable the NUS service CONFIG_BT_GATT_NUS=y diff --git a/samples/bluetooth/peripheral_uart/src/main.c b/samples/bluetooth/peripheral_uart/src/main.c index 512117355f78..d848b56eb4ee 100644 --- a/samples/bluetooth/peripheral_uart/src/main.c +++ b/samples/bluetooth/peripheral_uart/src/main.c @@ -14,7 +14,6 @@ #include #include -#include #include #include @@ -35,16 +34,10 @@ #define DEVICE_NAME CONFIG_BT_DEVICE_NAME #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1) -/* Change this if you have an LED connected to a custom port */ -#define LED_PORT DT_ALIAS_LED0_GPIOS_CONTROLLER - -#define RUN_STATUS_LED DT_ALIAS_LED0_GPIOS_PIN +#define RUN_STATUS_LED DK_LED1 #define RUN_LED_BLINK_INTERVAL 1000 -#define CON_STATUS_LED DT_ALIAS_LED0_GPIOS_PIN - -#define LED_ON 0 -#define LED_OFF 1 +#define CON_STATUS_LED DK_LED2 #define KEY_PASSKEY_ACCEPT DK_BTN1_MSK #define KEY_PASSKEY_REJECT DK_BTN2_MSK @@ -56,14 +49,8 @@ static K_SEM_DEFINE(ble_init_ok, 0, 2); static struct bt_conn *current_conn; static struct bt_conn *auth_conn; -static struct device *led_port; static struct device *uart; -static u32_t led_pins[] = {DT_ALIAS_LED0_GPIOS_PIN, - DT_ALIAS_LED1_GPIOS_PIN, - DT_ALIAS_LED2_GPIOS_PIN, - DT_ALIAS_LED3_GPIOS_PIN}; - struct uart_data_t { void *fifo_reserved; u8_t data[UART_BUF_SIZE]; @@ -82,13 +69,6 @@ static const struct bt_data sd[] = { BT_DATA_BYTES(BT_DATA_UUID128_ALL, NUS_UUID_SERVICE), }; -static void set_led_state(int led, bool state) -{ - if (led_port) { - gpio_pin_write(led_port, led, state); - } -} - static void uart_cb(struct device *uart) { static struct uart_data_t *rx; @@ -180,20 +160,28 @@ static int init_uart(void) static void connected(struct bt_conn *conn, u8_t err) { + char addr[BT_ADDR_LE_STR_LEN]; + if (err) { printk("Connection failed (err %u)\n", err); return; } - printk("Connected\n"); + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + printk("Connected %s\n", addr); + current_conn = bt_conn_ref(conn); - set_led_state(CON_STATUS_LED, LED_ON); + dk_set_led_on(CON_STATUS_LED); } static void disconnected(struct bt_conn *conn, u8_t reason) { - printk("Disconnected (reason %u)\n", reason); + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + printk("Disconnected: %s (reason %u)\n", addr, reason); if (auth_conn) { bt_conn_unref(auth_conn); @@ -203,7 +191,7 @@ static void disconnected(struct bt_conn *conn, u8_t reason) if (current_conn) { bt_conn_unref(current_conn); current_conn = NULL; - set_led_state(CON_STATUS_LED, LED_OFF); + dk_set_led_off(CON_STATUS_LED); } } @@ -388,64 +376,9 @@ static void bt_ready(int err) k_sem_give(&ble_init_ok); } -static int init_leds(void) -{ - int err = 0; - - led_port = device_get_binding(LED_PORT); - - if (!led_port) { - printk("Could not bind to LED port\n"); - return -ENXIO; - } - - err = gpio_pin_configure(led_port, RUN_STATUS_LED, - GPIO_DIR_OUT); - if (!err) { - err = gpio_pin_configure(led_port, CON_STATUS_LED, - GPIO_DIR_OUT); - } - - if (!err) { - err = gpio_pin_write(led_port, RUN_STATUS_LED, LED_OFF); - } - - if (!err) { - err = gpio_pin_write(led_port, CON_STATUS_LED, LED_OFF); - } - - if (err) { - printk("Not able to correctly initialize LED pins (err:%d)", - err); - led_port = NULL; - } - - return err; -} - void error(void) { - int err = -1; - - led_port = device_get_binding(LED_PORT); - if (led_port) { - for (size_t i = 0; i < ARRAY_SIZE(led_pins); i++) { - err = gpio_pin_configure(led_port, led_pins[i], - GPIO_DIR_OUT); - if (err) { - break; - } - } - } - - if (!err) { - for (size_t i = 0; i < ARRAY_SIZE(led_pins); i++) { - err = gpio_pin_write(led_port, led_pins[i], LED_ON); - if (err) { - break; - } - } - } + dk_set_leds_state(DK_ALL_LEDS_MSK, DK_NO_LEDS_MSK); while (true) { /* Spin for ever */ @@ -482,13 +415,19 @@ void button_changed(u32_t button_state, u32_t has_changed) } } -void configure_buttons(void) +static void configure_gpio(void) { - int err = dk_buttons_init(button_changed); + int err; + err = dk_buttons_init(button_changed); if (err) { printk("Cannot init buttons (err: %d)\n", err); } + + err = dk_leds_init(); + if (err) { + printk("Cannot init LEDs (err: %d)\n", err); + } } static void led_blink_thread(void) @@ -503,7 +442,7 @@ static void led_blink_thread(void) err = bt_enable(bt_ready); } - configure_buttons(); + configure_gpio(); if (!err) { bt_conn_cb_register(&conn_callbacks); @@ -526,10 +465,8 @@ static void led_blink_thread(void) error(); } - init_leds(); - for (;;) { - set_led_state(RUN_STATUS_LED, (++blink_status) % 2); + dk_set_led(RUN_STATUS_LED, (++blink_status) % 2); k_sleep(RUN_LED_BLINK_INTERVAL); } } diff --git a/samples/bootloader/prj.conf b/samples/bootloader/prj.conf index b43f57d473aa..8c70c513fe36 100644 --- a/samples/bootloader/prj.conf +++ b/samples/bootloader/prj.conf @@ -14,9 +14,10 @@ CONFIG_ERRNO=n CONFIG_SYS_CLOCK_EXISTS=n CONFIG_NRF_RTC_TIMER=n CONFIG_CLOCK_CONTROL=n +CONFIG_FPROTECT=y CONFIG_SECURE_BOOT_DEBUG=y -# To reduce size, turn off debug printing +# To reduce size, turn off debug printing: # CONFIG_BOOT_BANNER=n # CONFIG_PRINTK=n # CONFIG_CONSOLE=n diff --git a/samples/bootloader/src/main.c b/samples/bootloader/src/main.c index 19d56d7211bb..404a747f1a15 100644 --- a/samples/bootloader/src/main.c +++ b/samples/bootloader/src/main.c @@ -11,10 +11,10 @@ #include #include #include -#include "bootloader.h" +#include #include -#include -#include +#include +#include #include #ifdef CONFIG_UART_NRFX #ifdef CONFIG_UART_0_NRF_UART @@ -24,69 +24,6 @@ #endif #endif - -static bool verify_firmware(u32_t address) -{ - /* Some key data storage backends require word sized reads, hence - * we need to ensure word alignment for 'key_data' - */ - u32_t key_data[CONFIG_SB_PUBLIC_KEY_HASH_LEN/4]; - int retval = -EFAULT; - int err; - const struct fw_firmware_info *fw_info; - const struct fw_validation_info *fw_ver_info; - - fw_info = fw_find_firmware_info(address); - - if (!fw_info) { - printk("Could not find valid firmware info inside " - "firmware. Aborting boot!\n\r"); - return false; - } - - fw_ver_info = validation_info_find(fw_info, 4); - - if (!fw_ver_info) { - printk("Could not find valid firmware validation " - "info trailing firmware. Aborting boot!\n\r"); - return false; - } - - err = bl_crypto_init(); - if (err) { - printk("bl_crypto_init() returned %d. Aborting boot!\n\r", err); - return false; - } - - u32_t num_public_keys = num_public_keys_read(); - - for (u32_t key_data_idx = 0; key_data_idx < num_public_keys; - key_data_idx++) { - if (public_key_data_read(key_data_idx, &key_data[0], - CONFIG_SB_PUBLIC_KEY_HASH_LEN) < 0) { - retval = -EFAULT; - break; - } - retval = bl_root_of_trust_verify(fw_ver_info->public_key, - (u8_t *)key_data, - fw_ver_info->signature, - (u8_t *)address, - fw_info->firmware_size); - if (retval != -ESIGINV) { - break; - } - } - - if (retval != 0) { - printk("Firmware validation failed with error %d. " - "Aborting boot!\n\r", - retval); - return false; - } - - return true; -} - static void uninit_used_peripherals(void) { #ifdef CONFIG_UART_0_NRF_UART @@ -107,14 +44,15 @@ extern u32_t _vector_table_pointer; #define VTOR SCB->VTOR #endif -static void boot_from(const struct fw_firmware_info *fw_info) +static void boot_from(const struct fw_info *fw_info) { u32_t *vector_table = (u32_t *)fw_info->firmware_address; printk("Attempting to boot from address 0x%x.\n\r", fw_info->firmware_address); - if (!verify_firmware(fw_info->firmware_address)) { + if (!bl_validate_firmware_local(fw_info->firmware_address, + fw_info)) { printk("Failed to validate!\n\r"); return; } @@ -161,7 +99,7 @@ static void boot_from(const struct fw_firmware_info *fw_info) VTOR = fw_info->firmware_address; - fw_abi_provide(fw_info); + fw_info_abi_provide(fw_info); /* Set MSP to the new address and clear any information from PSP */ __set_MSP(vector_table[0]); @@ -183,8 +121,8 @@ void main(void) u32_t s0_addr = s0_address_read(); u32_t s1_addr = s1_address_read(); - const struct fw_firmware_info *s0_info = fw_find_firmware_info(s0_addr); - const struct fw_firmware_info *s1_info = fw_find_firmware_info(s1_addr); + const struct fw_info *s0_info = fw_info_find(s0_addr); + const struct fw_info *s1_info = fw_info_find(s1_addr); if (!s1_info || (s0_info->firmware_version >= s1_info->firmware_version)) { diff --git a/samples/nrf9160/at_client/README.rst b/samples/nrf9160/at_client/README.rst new file mode 100644 index 000000000000..df5e95112d56 --- /dev/null +++ b/samples/nrf9160/at_client/README.rst @@ -0,0 +1,134 @@ +.. _at_client_sample: + +nRF9160: AT Client +################## + +The AT Client sample demonstrates the asynchronous serial communication taking place over UART to the nRF9160 modem. +The sample enables you to use an external computer or MCU to send AT commands to the LTE-M/NB-IoT modem of your nRF9160 device. + +Overview +******** + +The AT Client sample acts as a proxy for sending directives to the nRF9160 modem via AT commands. +This facilitates the reading of responses or analyzing of events related to the nRF9160 modem. +The commands can be initiated from a terminal or the `LTE Link Monitor`_, which is an application implemented as part of `nRF Connect for Desktop`_. + +For more information on the AT commands, see the `AT Commands Reference Guide`_. + + + +Requirements +************ + +* The following development board: + + * nRF9160 DK board (PCA10090) + + +Building and running +******************** + +.. |sample path| replace:: :file:`samples/nrf9160/at_client` + +.. include:: /includes/build_and_run_nrf9160.txt + + +Connecting to the nRF9160 DK board with LTE Link Monitor +======================================================== + +To connect to the nRF9160 DK board with LTE Link Monitor, perform the following steps: + +#. Launch the LTE Link Monitor after programming the AT Client sample onto the nRF9160 DK board. +#. Make sure that **Automatic requests** is enabled in LTE Link Monitor. +#. Connect the nRF9160 DK board to the PC with a USB cable. +#. Power on the nRF9160 DK board. +#. Click **Select Device** and select the particular board device entry from the drop-down list in the LTE Link Monitor. +#. Observe that initially the command :command:`AT+CFUN?` is automatically sent to the modem, which returns a value 4, indicating that the modem is in the offline mode. +#. Observe that the LTE Link Monitor terminal display also shows :command:`AT+CFUN=1` indicating that the modem has changed to the normal mode. +#. Press the reset button on the nRF9160 DK to reboot the board and start the AT Client sample. + + +Testing +======= + +After programming the sample to your board, test the sample by performing the following steps: + +1. `Connect to the nRF9160 DK board with LTE Link Monitor `_. +#. Run the following commands from the LTE Link Monitor terminal: + + a. Enter the command: :command:`AT+CFUN?` + + This command reads the current functional mode of the modem and triggers the command :command:`AT+CFUN=1` which sets the functional mode of the modem to normal. + + #. Enter the command :command:`AT+CFUN?` into the LTE Link Monitor terminal again. + + The UART/Modem/UICC/LTE/PDN indicators in the LTE Link Monitor side panel turn green. + This command also automatically launches a series of commands like: + + * :command:`AT+CGSN=1` which displays the product serial identification number (IMEI). + * :command:`AT+CGMI` which displays the manufacturer name. + * :command:`AT+CGMM` which displays the model identification name. + * :command:`AT+CGMR` which displays the revision identification. + * :command:`AT+CEMODE` which displays the current mode of operation. + + c. Enter the command: :command:`AT%XOPERID` + + This command returns the network operator ID. + + + #. Enter the command: :command:`AT%XMONITOR` + + This command returns the modem parameters. + + + #. Enter the command: :command:`AT%XTEMP?` + + This command displays the current modem temperature. + + + #. Enter the command: :command:`AT%CMNG=1` + + This command displays a list of all certificates that are stored on your device. + If the device has been added to nRF Cloud, a CA certificate, a client certificate, and a private key with security tag 16842753 (which is the security tag for nRF Cloud credentials) are displayed. + + + +Sample output +============= + +The following is a sample output of the command: :command:`AT%XMONITOR` + +.. code-block:: console + + AT%XMONITOR + %XMONITOR: 5,"","","24201","76C1",7,20,"0102DA03",105,6400,53,24,"","11100000","11100000" + OK + + +Dependencies +************ + +This sample uses the following libraries: + +From |NCS| + * ``lib/at_host`` which includes: + * :ref:`at_cmd_readme` + * :ref:`at_notif_readme` + +From nrfxlib + * :ref:`nrfxlib:bsdlib` + +In addition, it uses the following samples: + +From |NCS| + * :ref:`secure_partition_manager` + + + + + + +References +********** + +`AT Commands Reference Guide`_ \ No newline at end of file diff --git a/samples/nrf9160/at_client/src/main.c b/samples/nrf9160/at_client/src/main.c index 9d366255a7f0..3cd23a158ce5 100644 --- a/samples/nrf9160/at_client/src/main.c +++ b/samples/nrf9160/at_client/src/main.c @@ -15,14 +15,6 @@ void bsd_recoverable_error_handler(uint32_t err) printk("bsdlib recoverable error: %u\n", err); } -/**@brief Irrecoverable BSD library error. */ -void bsd_irrecoverable_error_handler(uint32_t err) -{ - printk("bsdlib irrecoverable error: %u\n", err); - - __ASSERT_NO_MSG(false); -} - void main(void) { printk("The AT host sample started\n"); diff --git a/samples/nrf9160/aws_fota/CMakeLists.txt b/samples/nrf9160/aws_fota/CMakeLists.txt index b27d9ac080f2..fda956a16c58 100644 --- a/samples/nrf9160/aws_fota/CMakeLists.txt +++ b/samples/nrf9160/aws_fota/CMakeLists.txt @@ -8,6 +8,19 @@ cmake_minimum_required(VERSION 3.8.2) include(../../../cmake/boilerplate.cmake NO_POLICY_SCOPE) include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +if(CONFIG_PROVISION_CERTIFICATES) + message(WARNING " + ------------------------------------------------------------ + --- WARNING: Provisioning certificates is ENABLED. Do --- + --- not use this binary in production or share it with --- + --- anyone. It has certificates stored in readable flash,--- + --- the binary, and the modem traces. Only use this --- + --- binary once to provision certificates for development--- + --- to reduce flash tear. After the certificates are --- + --- provisioned, disable this option and rebuild the --- + --- sample. --- + ------------------------------------------------------------") +endif() project(mqtt-aws-jobs-fota) # NORDIC SDK APP START diff --git a/samples/nrf9160/aws_fota/Kconfig b/samples/nrf9160/aws_fota/Kconfig index 459242fa3e9f..a42edc5d940a 100644 --- a/samples/nrf9160/aws_fota/Kconfig +++ b/samples/nrf9160/aws_fota/Kconfig @@ -10,38 +10,60 @@ config APP_VERSION string "Application version" default "v1.0.0" +config USE_NRF_CLOUD + bool "Use nRF Cloud" + default n + +if !USE_NRF_CLOUD config CLOUD_CERT_SEC_TAG - int "Secure tag fo TLS credentials" - default 16842753 + int "Security tag for TLS credentials" + default 12345678 -config MQTT_BROKER_HOSTNAME - string "AWS IoT MQTT broker hostname" - default "a2n7tk1kp18wix-ats.iot.us-east-1.amazonaws.com" +config PROVISION_CERTIFICATES + bool "Provision certificates from the certificates.h file" + default n help - Default is set to be the NRF Cloud MQTT broker - -config MQTT_BROKER_PORT - int "AWS IoT MQTT broker port" - default 8883 + If enabled, the sample provisions server certificates into + the modem by storing the certificates defined in the + certificates.h file in the modem under the given security tag. + Use this option only once to provision the device. + The certificates are stored in the application binary and are + therefore shown in the modem trace information. This is a + security risk. After provisioning the certificates, turn off + this option and compile and program the sample again. + Also, do not share the binary that includes the compiled + certificates with anyone. config USE_CLOUD_CLIENT_ID - bool "Custom MQTT Client Id" + bool "Custom MQTT client ID" + default y if USE_CLOUD_CLIENT_ID - config CLOUD_CLIENT_ID string "Client ID" default "your_client_id" endif -config USE_PROVISIONED_CERTIFICATES - bool "Use provisioned certificates" - default y +config MQTT_BROKER_HOSTNAME + string "AWS IoT MQTT broker hostname" + default "your_aws_mqtt_broker_hostname.amazonaws.com" + help + Default is set to be the nRF Cloud MQTT broker. +config MQTT_BROKER_PORT + int "AWS IoT MQTT broker port" + default 8883 +endif #!USE_NRF_CLOUD + +if USE_NRF_CLOUD +config MQTT_BROKER_HOSTNAME + string + default "a2n7tk1kp18wix-ats.iot.us-east-1.amazonaws.com" help - If enabled, the application will not provide server certificates into - the modem and use whatever certificates that are stored in the modem - under the default secure tag (e. g. certificates provisioned by - another sample, for instance, `asset_tracker`). Otherwise, a user - should provide a valid server certificate in `certificates.h` file. + Default is set to be the nRF Cloud MQTT broker. + +config MQTT_BROKER_PORT + int + default 8883 +endif #USE_NRF_CLOUD config MQTT_MESSAGE_BUFFER_SIZE int "MQTT message buffer size" @@ -51,6 +73,10 @@ config MQTT_PAYLOAD_BUFFER_SIZE int "MQTT payload buffer size" default 128 +config DEVICE_SHADOW_PAYLOAD_SIZE + int "MQTT payload transmission buffer size for AWS IoT device shadow updates" + default 255 + endmenu menu "Zephyr Kernel" diff --git a/samples/nrf9160/aws_fota/README.rst b/samples/nrf9160/aws_fota/README.rst index dea2cab6c41d..08d9a9a1ec49 100644 --- a/samples/nrf9160/aws_fota/README.rst +++ b/samples/nrf9160/aws_fota/README.rst @@ -1,17 +1,105 @@ .. _aws_fota_sample: -nRF9160: AWS FOTA Sample -######################## +nRF9160: AWS FOTA +################# -The AWS FOTA sample shows how to perform over-the-air firmware updates of an nRF9160 via MQTT and HTTP. +The Amazon Web Services firmware over-the-air (AWS FOTA) sample shows how to perform an over-the-air firmware update of an nRF9160 device via MQTT and HTTP. It is similar to the :ref:`http_application_update_sample`, except that the firmware download is triggered through an AWS IoT job. Overview ********* -The sample connects to an AWS MQTT broker and subscribes to related AWS Job topics. -When it receives notification that an update job is available, it retrieves metadata over MQTT, then retrieves the payload over HTTP, and reports back the status when it completes. +The sample connects to the configured `AWS IoT MQTT`_ broker and subscribes to several topics related to AWS IoT jobs. +When an update job is created on the AWS IoT service, the sample receives a notification through MQTT. + +Triggered by the notification, the sample retrieves the metadata for the update job over MQTT. +This metadata contains the location of the new firmware image (which is generated when building the sample, but you must upload it to a server). +The sample then retrieves the firmware image over HTTP and replaces the current firmware with the downloaded firmware. + +See :ref:`lib_aws_fota` for information about the download procedure. +The `AWS IoT Developer Guide`_ contains all required information about the Amazon Web Services IoT service. + +.. note:: + For simplicity, the testing instructions for this sample use `nRF Cloud`_ as the AWS S3 server instance that hosts the new firmware image. + You can set up and use your own AWS S3 server bucket instead. + See the documentation for the :ref:`lib_aws_fota` library for more information. + +Creating a thing in AWS IoT +=========================== + +Before you can run this sample, you must create a *thing* for your board in AWS IoT so that AWS knows about your board. +This thing must be connected to a security policy. +For testing, you can use a permissive policy, but make sure to update the policy to be more restrictive before you go into production. +See `AWS IoT Developer Guide: Basic Policy Variables`_ and `AWS IoT Developer Guide: Security Best Practices`_ for more information about policies. + +To create a thing for your board: + +1. Log on to the `AWS IoT console`_. +#. Go to **Secure** > **Policies** and select **Create a policy**. +#. Enter a name and define your policy. + For testing purposes, you can use the following policy (switch to **Advanced mode** to copy and paste it): + + .. code-block:: javascript + + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "iot:*", + "Resource": "*" + } + ] + } +#. Go to **Manage** > **Things** and select **Register a thing** or **Create** (depending on whether you already have a thing registered). +#. Select **Create a single thing**. +#. Enter a name. + The default name used by the sample is ``nrf-IMEI``, where *IMEI* is the IMEI number of your board. + If you choose a different name, make sure to :ref:`configure a custom client ID ` in the sample before you build it. +#. Accept the defaults and continue to the next step. +#. Select **Create certificate** to generate new certificates. + Alternatively, you can use existing certificates. + In this case, follow the instructions in AWS IoT. +#. Download the certificates for later use. + You need the thing certificate (``*-certificate.pem.crt``), the private key (``*.private.pem.key``), and the root CA (choose the Amazon Root CA 1, ``AmazonRootCA1.pem``). +#. Click **Activate** to activate the certificates. +#. Click **Attach a policy** to continue to the next step. +#. Select the policy that you created in step 3 and click **Register Thing**. + +Updating the certificates +========================= + +The certificates that you created or added for your thing in AWS IoT must be stored on your board so that it can connect to AWS IoT. +There are two different ways of doing this: + +Add the certificates to the sample code: + If you add the certificates to the sample code, the sample will store them on your board automatically. + + .. warning_start + + .. warning:: + * The sample will overwrite the certificates stored with the configured :option:`security tag `. + By default, these are the preprogrammed certificates for nRF Cloud. + Make sure to update the security tag to prevent overwriting the nRF Cloud certificates. + * You should provision the certificates only once and then update the sample configuration to use the existing certificates. + When provisioning the certificates, they are stored in the application binary and visible in the modem trace information, which is a security risk. + + .. warning_end + + 1. Open the ``certificates.h`` file in the ``src`` folder of the sample. + #. Add the three certificates in the given format. + Make sure to not add whitespace except for the ``\n`` line breaks. + #. Before programming the sample, configure it to provision the certificates from the certificates.h file (:option:`PROVISION_CERTIFICATES`) and to use a different security tag (:option:`CLOUD_CERT_SEC_TAG`). + +Use LTE Link Monitor to write the certificates to the board: + The nRF Connect `LTE Link Monitor`_ provides a certificate manager that you can use to store the certificates on your board: + + 1. Make sure that you have the AT client sample programmed on your board. + #. Put the modem in offline state. + #. Paste the three certificates into the respective fields. + #. Choose a security tag. + #. Click **Update certificates**. + #. Before programming the sample, make sure to configure the :option:`security tag ` to the one that you chose. -See :ref:`lib_aws_fota` for more information. Requirements ************ @@ -20,9 +108,12 @@ Requirements * nRF9160 DK board (PCA10090) +* An `AWS account`_ with access to Simple Storage Service (S3) and the IoT Core service + +* An `nRF Cloud`_ account + * .. include:: /includes/spm.txt -* AWS IoT account -* Certificates with correct permissions to the AWS IoT MQTT broker written/flashed into the modem + Building and running ******************** @@ -31,56 +122,148 @@ Building and running .. include:: /includes/build_and_run_nrf9160.txt -Prerequisites -************* +.. _configuring: -The connected device must have the same MQTT Client ID as the thing name created on AWS IoT. -The device needs to have certificates from AWS IoT provisioned onto the device and it needs to have a policy attached to the certificates, and that the certificates are activate in AWS IoT. The process for doing so is described below. +Sample configuration +==================== -Below is an example of a very permissive policy which should be used for **testing only**. -You can attach it to the certificates if you are unsure what policies are needed for your device. -To create a policy, go to `AWS IoT Console `_ and select **Secure->Policies->Create**. +Before you build the sample, check and update the following configuration options: -.. code-block:: javascript +.. option:: APP_VERSION - Application version - { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": "iot:*", - "Resource": "*" - } - ] - } + The version string is printed when the sample starts. + Use this information to verify that the FOTA update worked. + +.. option:: CLOUD_CERT_SEC_TAG - Security tag for TLS credentials -To create a thing, log in to the `AWS IoT Console `_ and select **Manage->Things->Create->Create a single thing**. -Provide it with a name which corresponds to your device's MQTT Client ID. -Next, choose to either create certificates or add your own certificates and make sure to **Activate** them. -Also, make sure to download one of the `root CA certificates `_ and the generated certificates if you choose to let AWS generate some for you. + By default, the sample uses the certificates that are stored with the security tag for nRF Cloud. + To use different certificates, configure a different security tag. + If you used LTE Link Monitor to store the certificates, make sure to configure the security tag to the same that you used to store them. + +.. option:: MQTT_BROKER_HOSTNAME - AWT IoT MQTT broker hostname + + By default, the sample uses nRF Cloud's MQTT broker. + Change this value to AWS IoT's MQTT broker. + To find the address of the AWS IoT MQTT broker, open the AWS IoT console, go to **Test** and select **View endpoint** from the **Connected as XXX** drop-down menu. + +.. option:: USE_CLOUD_CLIENT_ID - Custom MQTT client ID + + The client ID links your board to the thing in AWS IoT. + By default, the client ID is ``nrf-IMEI``, where *IMEI* is the IMEI number of your board. + If you chose a different name for your thing in AWS IoT, check this option and specify the AWS IoT thing name as client ID. -Then, attach the policy you created earlier to the certificates. You must also add the information from the Root CA, Public Certificate, and Private Key into :file:`certificates.h`. +.. option:: USE_NRF_CLOUD - Use nRF Cloud + + If this option is checked, the sample uses the certificates that are stored in the modem with the nRF Cloud security tag. -When these steps are done, deselect ``CONFIG_USE_PROVISIONED_CERTIFICATES`` in menuconfig. -This will write your new certificates to the secure tag :c:type:`sec_tag_t` defined in menuconfig ``CONFIG_CLOUD_CERT_SEC_TAG`` into the modem. + Uncheck this option if you want to use the certificates added to the ``certificates.h`` file. + +.. option:: PROVISION_CERTIFICATES - Provision certificates from the certificates.h file + If this option is checked, the sample stores the certificates from the ``certificates.h`` file with the security tag that is defined as :option:`Security tag for TLS credentials `. + + .. include:: README.rst + :start-after: warning_start + :end-before: warning_end -Usage -***** +For all other values, use the default values unless you are using a custom MQTT server. -1. Configure the host name to be the AWS IoT MQTT Broker endpoint you want to use in menuconfig. -#. Make sure you have provisioned certificates onto your device. See the steps in `prerequisites`_. -#. Flash the sample onto the board. -#. When the sample has successfully connected to the AWS IoT broker, log in to your `AWS IoT Console `_ and go to **Manage->Jobs->Create**. -#. Then, choose **Create custom job**, provide it with a unique **Job ID**, and select the **Device to update**. -#. Add a **Job file**. +Testing +======= -.. note:: - * An example of a **job file** can be found in the documentation of the :ref:`lib_aws_fota` library. - You can upload it to an `S3 bucket `_ on your AWS account and select it from there. If you use the AWS IoT Console, this is the only way to add a job file to a custom job. +After programming the sample to the board, test it by performing the following steps: + +1. |connect_terminal| +#. Reset the board. +#. Confirm that the sample prints the configured application version and connects to AWS IoT. + You should see output like the following:: + + ***** Booting Zephyr OS build v1.14.99-ncs3-snapshot2-1281-g40b430ba977c ***** + + The MQTT AWS Jobs FOTA Sample, version: v1.0.0 + nrf_inbuilt_key_delete(42, 0) => result=0 + nrf_inbuilt_key_delete(42, 1) => result=0 + nrf_inbuilt_key_delete(42, 2) => result=0 + nrf_inbuilt_key_write => result=0 + nrf_inbuilt_key_write => result=0 + nrf_inbuilt_key_write => result=0 + LTE Link Connecting ... + LTE Link Connected! + IPv4 Address 127.0.0.1 + client_id: nrf-aws-fota + [mqtt_evt_handler:129] MQTT client connected! + [00:00:14.106,140] aws_jobs: Subscribe: $aws/things/nrf-aws-fota/jobs/notify-next + +#. Log on to the `AWS IoT console`_, go to **Manage** > **Things**, and select your thing. +#. Go to **Shadow** and confirm that the application version (``nrfcloud__dfu_v1__app_v``) is the one that you configured for the sample. +#. In the :ref:`configuring`, change the application version. + Then rebuild the application, but do not program it. +#. Go to `firmware.nrfcloud.com`_ and sign in. + If you already have an nRF Cloud account, you can use the same credentials. + Otherwise, create an account and sign in. +#. In the menu on the left-hand side, select **Firmware**. +#. Click **Generate new** to create a URL for the firmware image. +#. Click **Upload** for the URL that you created and select the file ``app_update.bin`` (located in the ``zephyr`` subfolder of your build directory). +#. Create a job document (a text file) with the following content, replacing *host_url* with the server part of the URL that you created (for example, ``s3.amazonaws.com``) and *file_path* with the path and file name (for example, ``nordic-firmware-files/XXXXXXXXX``): + + .. parsed-literal:: + :class: highlight + + { + "operation": "app_fw_update", + "fwversion": "v1.0.2", + "size": 181124, + "location": { + "protocol": "http:", + "host": "*host_url*", + "path": "*file_path*" + } + } + See `AWS IoT Developer Guide: Jobs`_ for more information about AWS jobs. +#. Log on to the `AWS S3 console`_. +#. If you do not have a bucket yet, create one with the default settings. +#. Select the bucket, click **Upload**, and select your job document. + Use the default settings when uploading the file. +#. Log on to the `AWS IoT console`_, go to **Manage** > **Jobs**, and select **Create a job**. +#. Click **Create custom job** and enter a unique job ID. + Select your device and the job file that you uploaded to AWS S3. + Use the default settings for all other options. +#. Since the sample is configured to subscribe to the ``app_fw_update`` job topic, it picks up the job automatically. + This can take several minutes. + Select the job in AWS IoT to confirm that it is in progress. + Note that it might show as in progress on the overview, while it is actually queued. +#. In the terminal emulator, observe that the new firmware image is downloaded and installed. + You should see output similar to the following:: + + ... + [00:02:03.748,931] download_client: Downloaded 135168/201232 bytes (67%) + [00:02:03.794,494] fota_flash_block: Erasing sector at offset 0x00021000 + [00:02:04.893,188] download_client: Downloaded 139264/201232 bytes (69%) + [00:02:04.938,720] fota_flash_block: Erasing sector at offset 0x00022000 + [00:02:05.933,013] download_client: Downloaded 143360/201232 bytes (71%) + [00:02:05.978,546] fota_flash_block: Erasing sector at offset 0x00023000 + ... + [00:05:20.585,266] aws_fota: Firmware download completed + + +#. When the board resets, observe that the sample prints the new application version. +#. Log on to the `AWS IoT console`_, go to **Manage** > **Things**, and select your thing. +#. Go to **Shadow** and confirm that the application version has updated. + + +Troubleshooting +=============== + +ERROR: mqtt_connect -45: + Error -45 ("operation is not supported on socket") indicates an error with the configured certificates. + Check that you added the certificates correctly in ``certificates.h`` and that you did not mix up the different certificates. + Certificates must be formatted correctly, without extra whitespace. + +Content range is not defined: + If you host the firmware image on a different server than nRF Cloud, this error indicates that the Content-Range field is missing in the HTTP GET header. + To fix this problem, configure the host server to provide this field. -7. Choose **Snapshot** as job type and press **Next**. Dependencies ************ diff --git a/samples/nrf9160/aws_fota/prj.conf b/samples/nrf9160/aws_fota/prj.conf index 6f2a1469d137..fba957f5be64 100644 --- a/samples/nrf9160/aws_fota/prj.conf +++ b/samples/nrf9160/aws_fota/prj.conf @@ -6,19 +6,12 @@ # General config CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_HW_STACK_PROTECTION=y - -CONFIG_IMG_MANAGER=y -CONFIG_BOOTLOADER_MCUBOOT=y - -# Enable DOWNLOAD libs -CONFIG_DOWNLOAD_CLIENT=y +CONFIG_ASSERT=y +CONFIG_REBOOT=y # Networking CONFIG_NETWORKING=y CONFIG_NET_NATIVE=n -CONFIG_NET_SOCKETS_SOCKOPT_TLS=y -CONFIG_NET_SOCKETS=y -CONFIG_NET_SOCKETS_POSIX_NAMES=y # LTE link control CONFIG_LTE_LINK_CONTROL=y @@ -26,6 +19,7 @@ CONFIG_LTE_AUTO_INIT_AND_CONNECT=n # BSD library CONFIG_BSD_LIBRARY=y +CONFIG_BSD_LIBRARY_SYS_INIT=n # AT Host CONFIG_UART_INTERRUPT_DRIVEN=y @@ -35,21 +29,25 @@ CONFIG_AT_HOST_LIBRARY=y CONFIG_MQTT_LIB=y CONFIG_MQTT_LIB_TLS=y -# Allow writing to flash -CONFIG_FLASH=y -CONFIG_FLASH_PAGE_LAYOUT=y -CONFIG_MPU_ALLOW_FLASH_WRITE=y - # Main thread CONFIG_MAIN_THREAD_PRIORITY=7 CONFIG_MAIN_STACK_SIZE=4096 -# FOTA +# AWS FOTA CONFIG_AWS_FOTA=y CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=4096 CONFIG_HEAP_MEM_POOL_SIZE=4096 -CONFIG_FLOAT=y - CONFIG_CLIENT_ID_MAX_LEN=64 -CONFIG_REBOOT=y +CONFIG_DOWNLOAD_CLIENT=y +CONFIG_DFU_TARGET=y +CONFIG_FOTA_DOWNLOAD=y + +# Application update support +CONFIG_BOOTLOADER_MCUBOOT=y + +# Image manager +CONFIG_IMG_MANAGER=y +CONFIG_FLASH=y + +# Enable Log CONFIG_LOG=y diff --git a/samples/nrf9160/aws_fota/src/main.c b/samples/nrf9160/aws_fota/src/main.c index 6102c5b76632..9dffdc67d4b2 100644 --- a/samples/nrf9160/aws_fota/src/main.c +++ b/samples/nrf9160/aws_fota/src/main.c @@ -6,23 +6,26 @@ #include #include -#include -#include - -#include +#include #include +#include +#include #include #include +#include #include - #include #include +#if defined(CONFIG_USE_NRF_CLOUD) +#define NRF_CLOUD_SECURITY_TAG 16842753 +#endif + #if defined(CONFIG_BSD_LIBRARY) #include "nrf_inbuilt_key.h" #endif -#if !defined(CONFIG_USE_PROVISIONED_CERTIFICATES) +#if defined(CONFIG_PROVISION_CERTIFICATES) #include "certificates.h" #endif @@ -55,15 +58,56 @@ void bsd_recoverable_error_handler(uint32_t err) printk("bsdlib recoverable error: %u\n", err); } -/**@brief Irrecoverable BSD library error. */ -void bsd_irrecoverable_error_handler(uint32_t err) +#endif /* defined(CONFIG_BSD_LIBRARY) */ + +/* Topic for updating shadow topic with version number */ +#define AWS "$aws/things/" +#define UPDATE_DELTA_TOPIC AWS "%s/shadow/update" +#define SHADOW_STATE_UPDATE \ +"{\"state\":{\"reported\":{\"nrfcloud__dfu_v1__app_v\":\"%s\"}}}" + +static int update_device_shadow_version(struct mqtt_client *const client) { - printk("bsdlib irrecoverable error: %u\n", err); + struct mqtt_publish_param param; + char update_delta_topic[strlen(AWS) + strlen("/shadow/update") + + CLIENT_ID_LEN]; + u8_t shadow_update_payload[CONFIG_DEVICE_SHADOW_PAYLOAD_SIZE]; + + int ret = snprintf(update_delta_topic, + sizeof(update_delta_topic), + UPDATE_DELTA_TOPIC, + client->client_id.utf8); + u32_t update_delta_topic_len = ret; + + if (ret >= sizeof(update_delta_topic)) { + return -ENOMEM; + } else if (ret < 0) { + return ret; + } - __ASSERT_NO_MSG(false); -} + ret = snprintf(shadow_update_payload, + sizeof(shadow_update_payload), + SHADOW_STATE_UPDATE, + CONFIG_APP_VERSION); + u32_t shadow_update_payload_len = ret; -#endif /* defined(CONFIG_BSD_LIBRARY) */ + if (ret >= sizeof(shadow_update_payload)) { + return -ENOMEM; + } else if (ret < 0) { + return ret; + } + + param.message.topic.qos = MQTT_QOS_1_AT_LEAST_ONCE; + param.message.topic.topic.utf8 = update_delta_topic; + param.message.topic.topic.size = update_delta_topic_len; + param.message.payload.data = shadow_update_payload; + param.message.payload.len = shadow_update_payload_len; + param.message_id = sys_rand32_get(); + param.dup_flag = 0; + param.retain_flag = 0; + + return mqtt_publish(client, ¶m); +} /**@brief Function to print strings without null-termination. */ static void data_print(u8_t *prefix, u8_t *data, size_t len) @@ -112,11 +156,6 @@ void mqtt_evt_handler(struct mqtt_client * const c, return; } else if (err < 0) { printk("aws_fota_mqtt_evt_handler: Failed! %d\n", err); - printk("Disconnecting MQTT client...\n"); - err = mqtt_disconnect(c); - if (err) { - printk("Could not disconnect: %d\n", err); - } } switch (evt->type) { @@ -127,14 +166,12 @@ void mqtt_evt_handler(struct mqtt_client * const c, } printk("[%s:%d] MQTT client connected!\n", __func__, __LINE__); +#if !defined(CONFIG_USE_NRF_CLOUD) + err = update_device_shadow_version(c); if (err) { - printk("Unable to initialize AWS jobs upon " - "connection\n"); - err = mqtt_disconnect(c); - if (err) { - printk("Could not disconnect: %d\n", err); - } + printk("Unable to update device shadow err: %d\n", err); } +#endif break; case MQTT_EVT_DISCONNECT: @@ -209,6 +246,7 @@ void mqtt_evt_handler(struct mqtt_client * const c, static void broker_init(const char *hostname) { int err; + char addr_str[INET6_ADDRSTRLEN]; struct addrinfo *result; struct addrinfo *addr; struct addrinfo hints = { @@ -238,7 +276,9 @@ static void broker_init(const char *hostname) broker->sin_family = AF_INET; broker->sin_port = htons(CONFIG_MQTT_BROKER_PORT); - printk("IPv4 Address 0x%08x\n", broker->sin_addr.s_addr); + inet_ntop(AF_INET, &broker->sin_addr, addr_str, + sizeof(addr_str)); + printk("IPv4 Address %s\n", addr_str); break; } else if (addr->ai_addrlen == sizeof(struct sockaddr_in6)) { /* IPv6 Address. */ @@ -252,7 +292,9 @@ static void broker_init(const char *hostname) broker->sin6_family = AF_INET6; broker->sin6_port = htons(CONFIG_MQTT_BROKER_PORT); - printk("IPv6 Address"); + inet_ntop(AF_INET6, &broker->sin6_addr, addr_str, + sizeof(addr_str)); + printk("IPv6 Address %s\n", addr_str); break; } else { printk("error: ai_addrlen = %u should be %u or %u\n", @@ -268,57 +310,48 @@ static void broker_init(const char *hostname) /* Free the address. */ freeaddrinfo(result); } - -#if !defined(CONFIG_USE_PROVISIONED_CERTIFICATES) +#if defined(CONFIG_PROVISION_CERTIFICATES) +#warning Not for prodcution use. This should only be used once to provisioning the certificates please deselect the provision certificates configuration and compile again. +#define MAX_OF_2 MAX(sizeof(CLOUD_CA_CERTIFICATE),\ + sizeof(CLOUD_CLIENT_PRIVATE_KEY)) +#define MAX_LEN MAX(MAX_OF_2, sizeof(CLOUD_CLIENT_PUBLIC_CERTIFICATE)) +static u8_t certificates[][MAX_LEN] = {{CLOUD_CA_CERTIFICATE}, + {CLOUD_CLIENT_PRIVATE_KEY}, + {CLOUD_CLIENT_PUBLIC_CERTIFICATE} }; +static const size_t cert_len[] = { + sizeof(CLOUD_CA_CERTIFICATE) - 1, sizeof(CLOUD_CLIENT_PRIVATE_KEY) - 1, + sizeof(CLOUD_CLIENT_PUBLIC_CERTIFICATE) - 1 +}; static int provision_certificates(void) { - { - int err; + int err; - /* Delete certificates */ - nrf_sec_tag_t sec_tag = CONFIG_CLOUD_CERT_SEC_TAG; + printk("************************* WARNING *************************\n"); + printk("%s called do not use this in production!\n", __func__); + printk("This will store the certificates in readable flash and leave\n"); + printk("them exposed on modem_traces. Only use this once for\n"); + printk("provisioning certificates for development to reduce flash tear." + "\n"); + printk("************************* WARNING *************************\n"); + nrf_sec_tag_t sec_tag = CONFIG_CLOUD_CERT_SEC_TAG; + nrf_key_mgnt_cred_type_t cred[] = { + NRF_KEY_MGMT_CRED_TYPE_CA_CHAIN, + NRF_KEY_MGMT_CRED_TYPE_PRIVATE_CERT, + NRF_KEY_MGMT_CRED_TYPE_PUBLIC_CERT, + }; - for (nrf_key_mgnt_cred_type_t type = 0; type < 3; type++) { - err = nrf_inbuilt_key_delete(sec_tag, type); - printk("nrf_inbuilt_key_delete(%u, %d) => result=%d\n", + /* Delete certificates */ + for (nrf_key_mgnt_cred_type_t type = 0; type < 3; type++) { + err = nrf_inbuilt_key_delete(sec_tag, type); + printk("nrf_inbuilt_key_delete(%u, %d) => result=%d\n", sec_tag, type, err); - } - - /* Provision CA Certificate. */ - err = nrf_inbuilt_key_write(CONFIG_CLOUD_CERT_SEC_TAG, - NRF_KEY_MGMT_CRED_TYPE_CA_CHAIN, - CLOUD_CA_CERTIFICATE, - strlen(CLOUD_CA_CERTIFICATE)); - printk("nrf_inbuilt_key_write => result=%d\n", err); - if (err) { - printk("CLOUD_CA_CERTIFICATE err: %d", err); - return err; - } - - /* Provision Private Certificate. */ - err = nrf_inbuilt_key_write( - CONFIG_CLOUD_CERT_SEC_TAG, - NRF_KEY_MGMT_CRED_TYPE_PRIVATE_CERT, - CLOUD_CLIENT_PRIVATE_KEY, - strlen(CLOUD_CLIENT_PRIVATE_KEY)); - printk("nrf_inbuilt_key_write => result=%d\n", err); - if (err) { - printk("NRF_CLOUD_CLIENT_PRIVATE_KEY err: %d", err); - return err; - } + } - /* Provision Public Certificate. */ - err = nrf_inbuilt_key_write( - CONFIG_CLOUD_CERT_SEC_TAG, - NRF_KEY_MGMT_CRED_TYPE_PUBLIC_CERT, - CLOUD_CLIENT_PUBLIC_CERTIFICATE, - strlen(CLOUD_CLIENT_PUBLIC_CERTIFICATE)); + /* Write certificates */ + for (nrf_key_mgnt_cred_type_t type = 0; type < 3; type++) { + err = nrf_inbuilt_key_write(sec_tag, cred[type], + certificates[type], cert_len[type]); printk("nrf_inbuilt_key_write => result=%d\n", err); - if (err) { - printk("CLOUD_CLIENT_PUBLIC_CERTIFICATE err: %d", - err); - return err; - } } return 0; } @@ -329,10 +362,11 @@ static int client_id_get(char *id_buf) #if !defined(CONFIG_CLOUD_CLIENT_ID) enum at_cmd_state at_state; char imei_buf[IMEI_LEN + 5]; - int err = at_cmd_write("AT+CGSN", imei_buf, (IMEI_LEN + 5), &at_state); + if (err) { - printk("Error when trying to do at_cmd_write: %d, at_state: %d", err, at_state); + printk("Error when trying to do at_cmd_write: %d, at_state: %d", + err, at_state); } snprintf(id_buf, CLIENT_ID_LEN + 1, "nrf-%s", imei_buf); @@ -347,10 +381,9 @@ static int client_init(struct mqtt_client *client, char *hostname) { mqtt_client_init(client); broker_init(hostname); - int ret = client_id_get(client_id_buf); - printk("client_id: %s\n", client_id_buf); + printk("client_id: %s\n", client_id_buf); if (ret != 0) { return ret; } @@ -373,7 +406,13 @@ static int client_init(struct mqtt_client *client, char *hostname) /* MQTT transport configuration */ client->transport.type = MQTT_TRANSPORT_SECURE; - static sec_tag_t sec_tag_list[] = {CONFIG_CLOUD_CERT_SEC_TAG}; + static sec_tag_t sec_tag_list[] = { +#ifdef CONFIG_USE_NRF_CLOUD + NRF_CLOUD_SECURITY_TAG +#else + CONFIG_CLOUD_CERT_SEC_TAG +#endif + }; struct mqtt_sec_config *tls_config = &(client->transport).tls.config; tls_config->peer_verify = 2; @@ -401,18 +440,18 @@ static int fds_init(struct mqtt_client *c) static void modem_configure(void) { #if defined(CONFIG_LTE_LINK_CONTROL) - if (IS_ENABLED(CONFIG_LTE_AUTO_INIT_AND_CONNECT)) { - /* Do nothing, modem is already turned on - * and connected. - */ - } else { - int err; - - printk("LTE Link Connecting ...\n"); - err = lte_lc_init_and_connect(); - __ASSERT(err == 0, "LTE link could not be established."); - printk("LTE Link Connected!\n"); - } + BUILD_ASSERT_MSG(!IS_ENABLED(CONFIG_LTE_AUTO_INIT_AND_CONNECT), + "This sample does not support auto init and connect"); + int err; + + err = at_notif_init(); + __ASSERT(err == 0, "AT Notify could not be initialized."); + err = at_cmd_init(); + __ASSERT(err == 0, "AT CMD could not be established."); + printk("LTE Link Connecting ...\n"); + err = lte_lc_init_and_connect(); + __ASSERT(err == 0, "LTE link could not be established."); + printk("LTE Link Connected!\n"); #endif } @@ -438,10 +477,36 @@ void main(void) struct mqtt_client client; printk("MQTT AWS Jobs FOTA Sample, version: %s\n", CONFIG_APP_VERSION); + printk("Initializing bsdlib\n"); + err = bsdlib_init(); + switch (err) { + case MODEM_DFU_RESULT_OK: + printk("Modem firmware update successful!\n"); + printk("Modem will run the new firmware after reboot\n"); + k_thread_suspend(k_current_get()); + break; + case MODEM_DFU_RESULT_UUID_ERROR: + case MODEM_DFU_RESULT_AUTH_ERROR: + printk("Modem firmware update failed\n"); + printk("Modem will run non-updated firmware on reboot.\n"); + break; + case MODEM_DFU_RESULT_HARDWARE_ERROR: + case MODEM_DFU_RESULT_INTERNAL_ERROR: + printk("Modem firmware update failed\n"); + printk("Fatal error.\n"); + break; + case -1: + printk("Could not initialize bsdlib.\n"); + printk("Fatal error.\n"); + return; + default: + break; + } + printk("Initialized bsdlib\n"); -#if !defined(CONFIG_USE_PROVISIONED_CERTIFICATES) +#if defined(CONFIG_PROVISION_CERTIFICATES) provision_certificates(); -#endif /* CONFIG_USE_PROVISIONED_CERTIFICATES */ +#endif /* CONFIG_PROVISION_CERTIFICATES */ modem_configure(); client_init(&client, CONFIG_MQTT_BROKER_HOSTNAME); @@ -465,7 +530,8 @@ void main(void) } /* All initializations were successful mark image as working so that we - * will not revert upon reboot. */ + * will not revert upon reboot. + */ boot_write_img_confirmed(); while (1) { diff --git a/samples/nrf9160/coap_client/overlay-carrier.conf b/samples/nrf9160/coap_client/overlay-carrier.conf index adf6096d01e5..f21161c993d7 100644 --- a/samples/nrf9160/coap_client/overlay-carrier.conf +++ b/samples/nrf9160/coap_client/overlay-carrier.conf @@ -9,9 +9,6 @@ CONFIG_LWM2M_CARRIER=y # The library requires newlibc CONFIG_NEWLIB_LIBC=y -# The library needs a source of entropy -CONFIG_TEST_RANDOM_GENERATOR=y - CONFIG_BSD_LIBRARY=y CONFIG_BSD_LIBRARY_SYS_INIT=n diff --git a/samples/nrf9160/coap_client/src/main.c b/samples/nrf9160/coap_client/src/main.c index da764728e07c..695d6f8bda17 100644 --- a/samples/nrf9160/coap_client/src/main.c +++ b/samples/nrf9160/coap_client/src/main.c @@ -34,14 +34,6 @@ void bsd_recoverable_error_handler(uint32_t err) printk("bsdlib recoverable error: %u\n", (unsigned int)err); } -/**@brief Irrecoverable BSD library error. */ -void bsd_irrecoverable_error_handler(uint32_t err) -{ - printk("bsdlib irrecoverable error: %u\n", (unsigned int)err); - - __ASSERT_NO_MSG(false); -} - #endif /* defined(CONFIG_BSD_LIBRARY) */ /**@brief Resolves the configured hostname. */ diff --git a/samples/nrf9160/gps/src/main.c b/samples/nrf9160/gps/src/main.c index e3c9e41cca57..77fccad7a873 100644 --- a/samples/nrf9160/gps/src/main.c +++ b/samples/nrf9160/gps/src/main.c @@ -40,11 +40,6 @@ void bsd_recoverable_error_handler(uint32_t error) printf("Err: %lu\n", (unsigned long)error); } -void bsd_irrecoverable_error_handler(uint32_t error) -{ - printf("Irrecoverable: %lu\n", (unsigned long)error); -} - static int enable_gps(void) { int at_sock; diff --git a/samples/nrf9160/http_application_update/prj.conf b/samples/nrf9160/http_application_update/prj.conf index 2c04f7c98a6f..43a154ab0da3 100644 --- a/samples/nrf9160/http_application_update/prj.conf +++ b/samples/nrf9160/http_application_update/prj.conf @@ -5,15 +5,14 @@ # # General config CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_HW_STACK_PROTECTION=y CONFIG_ASSERT=y CONFIG_REBOOT=y -CONFIG_FP_SHARING=y # Network CONFIG_NETWORKING=y -CONFIG_NET_NATIVE=n CONFIG_NET_SOCKETS=y -CONFIG_NET_SOCKETS_OFFLOAD=y +CONFIG_NET_NATIVE=n # LTE link control CONFIG_LTE_LINK_CONTROL=y @@ -21,6 +20,7 @@ CONFIG_LTE_AUTO_INIT_AND_CONNECT=n # BSD library CONFIG_BSD_LIBRARY=y +CONFIG_BSD_LIBRARY_SYS_INIT=n # Library for buttons and LEDs CONFIG_DK_LIBRARY=y @@ -37,23 +37,28 @@ CONFIG_MAIN_THREAD_PRIORITY=7 # Heap and stacks CONFIG_HEAP_MEM_POOL_SIZE=2048 CONFIG_MAIN_STACK_SIZE=8192 -CONFIG_HW_STACK_PROTECTION=y -# Enable DOWNLOAD libs +# Image manager +CONFIG_IMG_MANAGER=y +CONFIG_FLASH=y + +# GPIO +CONFIG_GPIO=y + +# FOTA library CONFIG_DOWNLOAD_CLIENT=y +CONFIG_DFU_TARGET=y +CONFIG_FOTA_DOWNLOAD=y + +# Application Upgrade support +CONFIG_BOOTLOADER_MCUBOOT=y # Enable Log, with debug messages and in place processing CONFIG_LOG=y CONFIG_LOG_DEFAULT_LEVEL=3 CONFIG_LOG_IMMEDIATE=y -CONFIG_FOTA_DOWNLOAD=y - -# GPIO -CONFIG_GPIO=y - +# Sample configuration CONFIG_DOWNLOAD_HOST="s3.amazonaws.com" -# Prebuilt version 2: -CONFIG_DOWNLOAD_FILE="/nordic-firmware-files/c1838e44-9d6f-4f41-990e-70b4e60770ce" +CONFIG_DOWNLOAD_FILE="nordic-firmware-files/c1838e44-9d6f-4f41-990e-70b4e60770ce" CONFIG_APPLICATION_VERSION=1 -CONFIG_BOOTLOADER_MCUBOOT=y diff --git a/samples/nrf9160/http_application_update/src/main.c b/samples/nrf9160/http_application_update/src/main.c index 3ef4a75b283a..221b775e328a 100644 --- a/samples/nrf9160/http_application_update/src/main.c +++ b/samples/nrf9160/http_application_update/src/main.c @@ -3,15 +3,16 @@ * * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic */ -#include #include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include #include #include -#include #define LED_PORT DT_ALIAS_LED0_GPIOS_CONTROLLER @@ -26,14 +27,6 @@ void bsd_recoverable_error_handler(uint32_t err) printk("bsdlib recoverable error: %u\n", err); } -/**@brief Irrecoverable BSD library error. */ -void bsd_irrecoverable_error_handler(uint32_t err) -{ - printk("bsdlib irrecoverable error: %u\n", err); - - __ASSERT_NO_MSG(false); -} - /**@brief Start transfer of the file. */ static void app_dfu_transfer_start(struct k_work *unused) { @@ -42,12 +35,14 @@ static void app_dfu_transfer_start(struct k_work *unused) retval = fota_download_start(CONFIG_DOWNLOAD_HOST, CONFIG_DOWNLOAD_FILE); if (retval != 0) { + /* Re-enable button callback */ + gpio_pin_enable_callback(gpiob, DT_ALIAS_SW0_GPIOS_PIN); + printk("fota_download_start() failed, err %d\n", retval); } - - return; } + /**@brief Turn on LED0 and LED1 if CONFIG_APPLICATION_VERSION * is 2 and LED0 otherwise. */ @@ -109,13 +104,13 @@ static int dfu_button_init(void) void fota_dl_handler(enum fota_download_evt_id evt_id) { switch (evt_id) { + case FOTA_DOWNLOAD_EVT_ERROR: + printk("Received error from fota_download\n"); + /* Fallthrough */ case FOTA_DOWNLOAD_EVT_FINISHED: /* Re-enable button callback */ gpio_pin_enable_callback(gpiob, DT_ALIAS_SW0_GPIOS_PIN); break; - case FOTA_DOWNLOAD_EVT_ERROR: - printk("Received error from fota_download\n"); - break; default: break; @@ -129,18 +124,18 @@ void fota_dl_handler(enum fota_download_evt_id evt_id) static void modem_configure(void) { #if defined(CONFIG_LTE_LINK_CONTROL) - if (IS_ENABLED(CONFIG_LTE_AUTO_INIT_AND_CONNECT)) { - /* Do nothing, modem is already turned on - * and connected. - */ - } else { - int err; - - printk("LTE Link Connecting ...\n"); - err = lte_lc_init_and_connect(); - __ASSERT(err == 0, "LTE link could not be established."); - printk("LTE Link Connected!\n"); - } + BUILD_ASSERT_MSG(!IS_ENABLED(CONFIG_LTE_AUTO_INIT_AND_CONNECT), + "This sample does not support auto init and connect"); + int err; + + err = at_notif_init(); + __ASSERT(err == 0, "AT Notify could not be initialized."); + err = at_cmd_init(); + __ASSERT(err == 0, "AT CMD could not be established."); + printk("LTE Link Connecting ...\n"); + err = lte_lc_init_and_connect(); + __ASSERT(err == 0, "LTE link could not be established."); + printk("LTE Link Connected!\n"); #endif } @@ -171,6 +166,32 @@ static int application_init(void) void main(void) { int err; + printk("Initializing bsdlib\n"); + err = bsdlib_init(); + switch (err) { + case MODEM_DFU_RESULT_OK: + printk("Modem firmware update successful!\n"); + printk("Modem will run the new firmware after reboot\n"); + k_thread_suspend(k_current_get()); + break; + case MODEM_DFU_RESULT_UUID_ERROR: + case MODEM_DFU_RESULT_AUTH_ERROR: + printk("Modem firmware update failed\n"); + printk("Modem will run non-updated firmware on reboot.\n"); + break; + case MODEM_DFU_RESULT_HARDWARE_ERROR: + case MODEM_DFU_RESULT_INTERNAL_ERROR: + printk("Modem firmware update failed\n"); + printk("Fatal error.\n"); + break; + case -1: + printk("Could not initialize bsdlib.\n"); + printk("Fatal error.\n"); + return; + default: + break; + } + printk("Initialized bsdlib\n"); modem_configure(); @@ -182,6 +203,4 @@ void main(void) } printk("Press Button 1 to start the FOTA download\n"); - - return; } diff --git a/samples/nrf9160/lte_ble_gateway/prj.conf b/samples/nrf9160/lte_ble_gateway/prj.conf index 03a2c1c5899e..25f8d5924f2e 100644 --- a/samples/nrf9160/lte_ble_gateway/prj.conf +++ b/samples/nrf9160/lte_ble_gateway/prj.conf @@ -15,11 +15,6 @@ CONFIG_NET_NATIVE=n CONFIG_NET_SOCKETS=y CONFIG_NET_SOCKETS_OFFLOAD=y -# MQTT -CONFIG_MQTT_SOCKET_LIB=y -CONFIG_MQTT_LIB_TLS=y -CONFIG_MQTT_MAX_PACKET_LENGTH=2048 - # LTE link control CONFIG_LTE_LINK_CONTROL=y CONFIG_LTE_AUTO_INIT_AND_CONNECT=n diff --git a/samples/nrf9160/lte_ble_gateway/src/main.c b/samples/nrf9160/lte_ble_gateway/src/main.c index 1bf68013ae6e..aa680f544ee1 100644 --- a/samples/nrf9160/lte_ble_gateway/src/main.c +++ b/samples/nrf9160/lte_ble_gateway/src/main.c @@ -73,7 +73,6 @@ static struct k_work connect_work; enum error_type { ERROR_NRF_CLOUD, ERROR_BSD_RECOVERABLE, - ERROR_BSD_IRRECOVERABLE }; /* Forward declaration of functions */ @@ -116,13 +115,6 @@ void error_handler(enum error_type err_type, int err) led_pattern = DK_LED1_MSK | DK_LED3_MSK; printk("Error of type ERROR_BSD_RECOVERABLE: %d\n", err); break; - case ERROR_BSD_IRRECOVERABLE: - /* Blinking all LEDs ON/OFF if there is an - * irrecoverable error. - */ - led_pattern = DK_ALL_LEDS_MSK; - printk("Error of type ERROR_BSD_IRRECOVERABLE: %d\n", err); - break; default: /* Blinking all LEDs ON/OFF in pairs (1 and 2, 3 and 4) * undefined error. @@ -151,12 +143,6 @@ void bsd_recoverable_error_handler(uint32_t err) error_handler(ERROR_BSD_RECOVERABLE, (int)err); } -/**@brief Irrecoverable BSD library error. */ -void bsd_irrecoverable_error_handler(uint32_t err) -{ - error_handler(ERROR_BSD_IRRECOVERABLE, (int)err); -} - /**@brief Callback for GPS trigger events */ static void gps_trigger_handler(struct device *dev, struct gps_trigger *trigger) { diff --git a/samples/nrf9160/lwm2m_carrier/prj.conf b/samples/nrf9160/lwm2m_carrier/prj.conf index 6891ea9900c7..d5b0992dcffc 100644 --- a/samples/nrf9160/lwm2m_carrier/prj.conf +++ b/samples/nrf9160/lwm2m_carrier/prj.conf @@ -9,9 +9,6 @@ CONFIG_LWM2M_CARRIER=y # The library requires newlibc CONFIG_NEWLIB_LIBC=y -# The library needs a source of entropy -CONFIG_TEST_RANDOM_GENERATOR=y - CONFIG_BSD_LIBRARY=y CONFIG_BSD_LIBRARY_SYS_INIT=n diff --git a/samples/nrf9160/lwm2m_carrier/src/main.c b/samples/nrf9160/lwm2m_carrier/src/main.c index 5ddfe22ce1f9..4d4ceab7d0c7 100644 --- a/samples/nrf9160/lwm2m_carrier/src/main.c +++ b/samples/nrf9160/lwm2m_carrier/src/main.c @@ -11,12 +11,6 @@ void bsd_recoverable_error_handler(uint32_t err) printk("bsdlib recoverable error: %u\n", (unsigned int)err); } -void bsd_irrecoverable_error_handler(uint32_t err) -{ - printk("bsdlib irrecoverable error: %u\n", (unsigned int)err); - __ASSERT_NO_MSG(false); -} - void lwm2m_carrier_event_handler(const lwm2m_carrier_event_t *event) { switch (event->type) { diff --git a/samples/nrf9160/lwm2m_client/prj.conf b/samples/nrf9160/lwm2m_client/prj.conf index 2381c9e9b94a..f28eb382680e 100644 --- a/samples/nrf9160/lwm2m_client/prj.conf +++ b/samples/nrf9160/lwm2m_client/prj.conf @@ -6,12 +6,15 @@ CONFIG_REBOOT=y # Network CONFIG_NETWORKING=y +CONFIG_NET_NATIVE=n +CONFIG_NET_IPV6=n +CONFIG_NET_IPV4=y CONFIG_NET_SOCKETS=y CONFIG_NET_SOCKETS_OFFLOAD=y -CONFIG_DNS_RESOLVER=y # Needed to enable DNS handling in LWM2M library. # LwM2M CONFIG_LWM2M=y +CONFIG_LWM2M_DNS_SUPPORT=y CONFIG_LWM2M_RW_JSON_SUPPORT=n CONFIG_LWM2M_SERVER_DEFAULT_PMIN=1 CONFIG_LWM2M_SERVER_DEFAULT_PMAX=300 @@ -101,13 +104,5 @@ CONFIG_GPS_SIM_THREAD_STACK_SIZE=1024 CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 #CONFIG_HW_STACK_PROTECTION=y -# Disable native network stack to save some memory -CONFIG_NET_IPV4=n -CONFIG_NET_IPV6=n -CONFIG_NET_UDP=n -CONFIG_NET_TCP=n -CONFIG_NET_RX_STACK_SIZE=256 -CONFIG_NET_TX_STACK_SIZE=256 - # Set LwM2M Server IP address here CONFIG_APP_LWM2M_SERVER="" diff --git a/samples/nrf9160/mqtt_simple/overlay-carrier.conf b/samples/nrf9160/mqtt_simple/overlay-carrier.conf index adf6096d01e5..f21161c993d7 100644 --- a/samples/nrf9160/mqtt_simple/overlay-carrier.conf +++ b/samples/nrf9160/mqtt_simple/overlay-carrier.conf @@ -9,9 +9,6 @@ CONFIG_LWM2M_CARRIER=y # The library requires newlibc CONFIG_NEWLIB_LIBC=y -# The library needs a source of entropy -CONFIG_TEST_RANDOM_GENERATOR=y - CONFIG_BSD_LIBRARY=y CONFIG_BSD_LIBRARY_SYS_INIT=n diff --git a/samples/nrf9160/mqtt_simple/src/main.c b/samples/nrf9160/mqtt_simple/src/main.c index 6970f4cc1838..2489dba25450 100644 --- a/samples/nrf9160/mqtt_simple/src/main.c +++ b/samples/nrf9160/mqtt_simple/src/main.c @@ -41,14 +41,6 @@ void bsd_recoverable_error_handler(uint32_t err) printk("bsdlib recoverable error: %u\n", (unsigned int)err); } -/**@brief Irrecoverable BSD library error. */ -void bsd_irrecoverable_error_handler(uint32_t err) -{ - printk("bsdlib irrecoverable error: %u\n", (unsigned int)err); - - __ASSERT_NO_MSG(false); -} - #endif /* defined(CONFIG_BSD_LIBRARY) */ #if defined(CONFIG_LWM2M_CARRIER) diff --git a/samples/nrf9160/secure_services/src/main.c b/samples/nrf9160/secure_services/src/main.c index 6b7fde60aed9..3df6c4aa3316 100644 --- a/samples/nrf9160/secure_services/src/main.c +++ b/samples/nrf9160/secure_services/src/main.c @@ -10,6 +10,7 @@ #include #include #include +#include void print_hex_number(u8_t *num, size_t len) { @@ -28,6 +29,7 @@ void print_random_number(u8_t *num, size_t len) void main(void) { + struct fw_info info_app; const int sleep_time_s = 5; const int random_number_count = 16; const int random_number_len = 144; @@ -50,6 +52,13 @@ void main(void) print_random_number(random_number, olen); } + ret = spm_firmware_info(PM_APP_ADDRESS, &info_app); + if (ret != 0) { + printk("Could find firmware info (err: %d)\n", ret); + } + + printk("App FW version: %d\n", info_app.firmware_version); + #ifdef CONFIG_BOOTLOADER_MCUBOOT const int num_bytes_to_read = PM_MCUBOOT_PAD_SIZE; const int read_address = PM_MCUBOOT_PAD_ADDRESS; @@ -65,6 +74,17 @@ void main(void) print_hex_number(buf, num_bytes_to_read); #endif + u32_t info_part = 0; + u32_t ficr_addr = (u32_t)&NRF_FICR_S->INFO.PART; + + printk("\nRead FICR, offset 0x20C (address 0x%08x):\n", ficr_addr); + ret = spm_request_read(&info_part, ficr_addr, sizeof(u32_t)); + if (ret != 0) { + printk("Could not read FICR (err: %d)\n", ret); + } else { + printk("FICR.INFO.PART (+0x20C) = 0x%08X\n", info_part); + } + printk("\nReboot in %d seconds.\n", sleep_time_s); k_sleep(K_SECONDS(5)); diff --git a/samples/nrf9160/serial_lte_modem/CMakeLists.txt b/samples/nrf9160/serial_lte_modem/CMakeLists.txt new file mode 100644 index 000000000000..7d35e9466d29 --- /dev/null +++ b/samples/nrf9160/serial_lte_modem/CMakeLists.txt @@ -0,0 +1,28 @@ +# +# Copyright (c) 2019 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic +# + +cmake_minimum_required(VERSION 3.8.2) + +# This sample runs as a non-secure application on nRF91. Therefore, it +# requires the secure_partition_manager that prepares the required +# peripherals to be available for the application. +# +# Configure the SPM image to enable the peripherals that this sample +# needs. +set(spm_CONF_FILE + prj.conf + ${CMAKE_CURRENT_LIST_DIR}/child_secure_partition_manager.conf + ) + +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(serial_lte_modem) + +target_sources(app PRIVATE src/main.c) +target_sources_ifdef(CONFIG_SLM_AT_MODE app PRIVATE src/slm_at_host.c) +target_sources_ifdef(CONFIG_SLM_TCPIP_AT_MODE app PRIVATE src/slm_at_tcpip.c) +target_sources_ifdef(CONFIG_SLM_GPS_AT_MODE app PRIVATE src/slm_at_gps.c) + +zephyr_include_directories(src) diff --git a/samples/nrf9160/serial_lte_modem/Kconfig b/samples/nrf9160/serial_lte_modem/Kconfig new file mode 100644 index 000000000000..708e265e5128 --- /dev/null +++ b/samples/nrf9160/serial_lte_modem/Kconfig @@ -0,0 +1,105 @@ +# +# Copyright (c) 2019 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic +# + +source "$ZEPHYR_BASE/Kconfig.zephyr" + +menu "Nordic Serial LTE Modem" + +config SLM_AT_MODE + bool "Serial LTE Modem by raw AT mode" + default y + select AT_CMD + select AT_CMD_PARSER + +config SLM_AT_MAX_PARAM + int "Max number of parameters in AT command" + default 8 + +# +# TCP/IP +# +config SLM_TCPIP_AT_MODE + bool "Support TCP/IP by raw AT mode" + default y + +# +# GPS +# +config SLM_GPS_AT_MODE + bool "Support GPS by raw AT mode" + default y + +# +# Inter-Connect +# +choice + prompt "UART for Interconnect" + default SLM_CONNECT_UART_0 + help + Sets the UART to use for interconnect + - UART 0 + - UART 1 + - UART 2 + config SLM_CONNECT_UART_0 + bool "UART 0" + config SLM_CONNECT_UART_1 + bool "UART 1" + config SLM_CONNECT_UART_2 + bool "UART 2" +endchoice + +config SLM_CONNECT_UART + int + default 0 if SLM_CONNECT_UART_0 + default 1 if SLM_CONNECT_UART_1 + default 2 if SLM_CONNECT_UART_2 + +choice + prompt "Termination Mode" + default SLM_CR_LF_TERMINATION + help + Sets the termination ending from the serial terminal + Levels are: + - NULL Termination + - CR Termination + - LF Termination + - CR+LF Termination + config SLM_NULL_TERMINATION + bool "NULL Termination" + config SLM_CR_TERMINATION + bool "CR Termination" + config SLM_LF_TERMINATION + bool "LF Termination" + config SLM_CR_LF_TERMINATION + bool "CR+LF Termination" +endchoice + +config SLM_AT_HOST_TERMINATION + int + default 0 if SLM_NULL_TERMINATION + default 1 if SLM_CR_TERMINATION + default 2 if SLM_LF_TERMINATION + default 3 if SLM_CR_LF_TERMINATION + +# +# GPIO wakeup +# +config SLM_GPIO_WAKEUP + bool "Support of GPIO wakeup" + help + Enable GPIO wakeup on nRF9160 side + +config SLM_MODEM_WAKEUP_PIN + int "GPIO for wakeup" + depends on SLM_GPIO_WAKEUP + default 31 + +module = SLM +module-str = serial modem +source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config" + +endmenu + diff --git a/samples/nrf9160/serial_lte_modem/README.rst b/samples/nrf9160/serial_lte_modem/README.rst new file mode 100644 index 000000000000..a9a054d19bc7 --- /dev/null +++ b/samples/nrf9160/serial_lte_modem/README.rst @@ -0,0 +1,160 @@ +.. _serial_lte_modem: + +nRF9160: Serial LTE Modem +######################### + +The Serial LTE Modem (SLM) sample demostrates sending AT commands between a host and a client device. +An nRF9160 DK is used as the host, while the client can be simulated using either a PC or an nRF52 DK. + +This sample is an enhancement to the at_client sample. It provides the following features: + + * Support for TCP/IP proprietary AT commands. + * Support for GPS proprietary AT commands. + * Support for communication to external MCU over UART. + +All nRF91 modem AT commands are also supported. + +Requirements +************ + +* The following development board: + + * nRF9160 DK board (PCA10090) + +* If the client is a PC: + + * Any terminal software, such as TeraTerm. + +* If the client is an nRF52 device: + + * nRF52840 DK board (PCA10056) + * nRF52832 DK board (PCA10040) + +.. terminal_config + +Terminal connection +=================== + +By default, configuration option ``CONFIG_SLM_CONNECT_UART_0`` is defined. +This means that you can use the J-Link COM port on the PC side to connect with nRF9160 DK, and send or receive AT commands there. + +Terminal serial configuration: + + * Hardware flow control: disabled + * Baud rate: 115200 + * Parity bit: no + +.. note:: + * The default AT command terminator is Carrier Return and Line Feed, i.e. ``\r\n``. + * nRF91 logs are output to the same terminal port. + +External MCU configuration +========================== + +To work with external MCU (nRF52), you must set the configuration option ``CONFIG_SLM_CONNECT_UART_2``. +The pin interconnection between nRF91 and nRF52 is presented in the following table: + +.. list-table:: + :align: center + :header-rows: 1 + + * - nRF52 DK + - nRF91 DK + * - UART TX P0.6 + - UART RX P0.11 + * - UART RX P0.8 + - UART TX P0.10 + * - UART CTS P0.7 + - UART RTS P0.12 + * - UART RTS P0.5 + - UART RTS P0.13 + * - GPIO OUT P0.27 + - GPIO IN P0.31 + +UART instance in use: + + * nRF52840 and nRF52832 (UART0) + * nRF9160 (UART2) + +UART configuration: + + * Hardware flow control: enabled + * Baud rate: 115200 + * Parity bit: no + * Operation mode: IRQ + +Note that the GPIO output level on nRF91 side should be 3 V. + +TCP/IP AT commands +****************** + +The following proprietary TCP/IP AT commands are used in this sample: + +* AT#XSOCKET=, +* AT#XSOCKET? +* AT#XBIND=, +* AT#XTCPCONN=, +* AT#XTCPCONN? +* AT#XTCPSEND= +* AT#XTCPRECV=,