diff --git a/CMakeLists.txt b/CMakeLists.txt index 5af971f0..f803cecc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.10) -project(pinetime VERSION 0.4.0 LANGUAGES C CXX ASM) +project(pinetime VERSION 0.5.0 LANGUAGES C CXX ASM) set(NRF_TARGET "nrf52") diff --git a/README.md b/README.md index 790e3de1..4c970f8e 100644 --- a/README.md +++ b/README.md @@ -27,14 +27,16 @@ I've tested this project on the actual PineTime hardware. * Project builds and runs on the Pinetime; * Logs available via JLink RTT; * SPI (DMA & IRQ based) LCD driver; - * BLE advertising, connection and bonding; + * Open source BLE stack : [NimBLE](https://github.com/apache/mynewt-nimble); + * BLE advertising and connection connection; * BLE CTS client (retrieves the time from the connected device if it implements a CTS server); * Push button to go to disable screen (and go to low power mode) / enable screen (and wake-up) and UI navigation * Touch panel support; * Rich user interface (using [LittleVGL](https://littlevgl.com/)) via display, touchpanel and push button. * Digital watch face and 4 demo applications (spinning meter, analog gauche, push button and message box); * Watchdog (automatic reset in case of firmware crash) and reset support (push and hold the button for 7 - 10s); - * BLE Notification support (still Work-In-Progress, [companion app](https://github.com/JF002/gobbledegook) needed). + * BLE Notification support (still Work-In-Progress, [companion app](https://github.com/JF002/gobbledegook) needed); + * Supported by companion app [Amazfish](https://openrepos.net/content/piggz/amazfish) (time synchronization and notifications are integrated). ## Documentation @@ -77,10 +79,6 @@ See [this page](./doc/PinetimeStubWithNrf52DK.md) - -DOPENOCD_BIN_PATH=[path to openocd] - * Optionally, you can define MERGEHEX with the path to the ```mergehex``` tool from [NRF5X Command Line Tools](https://infocenter.nordicsemi.com/index.jsp?topic=%2Fug_nrf5x_cltools%2FUG%2Fcltools%2Fnrf5x_command_line_tools_lpage.html&cp=6_1) to be able to merge the application and softdevice into one HEX file. In this case the merged file is generated in src/pinetime-app-full.hex - - - -DMERGEHEX=[Path to the mergehex executable] - JLINK ``` $ mkdir build @@ -116,18 +114,6 @@ $ make -j pinetime-app $ make FLASH_ERASE ``` -* Flash softdevice & application - -``` -$ make FLASH_SOFTDEVICE -$ make FLASH_pinetime-app -``` - -Or, with ```mergehex``` - -``` -$ make FLASH_MERGED_pinetime-app -``` * For your information : list make targets : @@ -208,7 +194,7 @@ $ JLinkRTTClient - https://github.com/eliotstock/memory : display the memory usage (FLASH/RAM) using the .map file from GCC. -## BLE connection, bonding and time synchronization +## BLE connection and time synchronization At runtime, BLE advertising is started. You can then use a smartphone or computer to connect and bond to your Pinetime. As soon as a device is bonded, Pinetime will look for a **CTS** server (**C**urrent **T**ime **S**ervice) on the connected device. @@ -223,7 +209,7 @@ Here is how to do it with an Android smartphone running NRFConnect: - Select server configuration "Current Time Service" and tap OK * Go back to the main screen and scan for BLE devices. A device called "PineTime" should appear * Tap the button "Connect" next to the PineTime device. It should connect to the PineTime and switch to a new tab. -* On this tab, on the top right, there is a 3 dots button. Tap on it and select Bond. The bonding process begins, and if it is sucessful, the PineTime should update its time and display it on the screen. +* If a CTS server is found, the Pinetime should update its time with the time provided by the server. ### Using Linux and bluetoothctl * Ensure that your bluetooth controller is enabled and working fine. I've tested this on a x86 Debian computer and on a RaspberryPi 3. diff --git a/cmake-nRF5x/CMake_nRF5x.cmake b/cmake-nRF5x/CMake_nRF5x.cmake index 3e8e96aa..c1785d36 100755 --- a/cmake-nRF5x/CMake_nRF5x.cmake +++ b/cmake-nRF5x/CMake_nRF5x.cmake @@ -5,10 +5,6 @@ if (NOT NRF5_SDK_PATH) message(FATAL_ERROR "The path to the nRF5 SDK (NRF5_SDK_PATH) must be set.") endif () -#if (NOT NRFJPROG) -# message(FATAL_ERROR "The path to the nrfjprog utility (NRFJPROG) must be set.") -#endif () - # convert toolchain path to bin path if(DEFINED ARM_NONE_EABI_TOOLCHAIN_PATH) set(ARM_NONE_EABI_TOOLCHAIN_BIN_PATH ${ARM_NONE_EABI_TOOLCHAIN_PATH}/bin) @@ -70,22 +66,15 @@ macro(nRF5x_setup) endif() set(CPU_FLAGS "-mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16") add_definitions(-DNRF52 -DNRF52832 -DNRF52832_XXAA -DNRF52_PAN_74 -DNRF52_PAN_64 -DNRF52_PAN_12 -DNRF52_PAN_58 -DNRF52_PAN_54 -DNRF52_PAN_31 -DNRF52_PAN_51 -DNRF52_PAN_36 -DNRF52_PAN_15 -DNRF52_PAN_20 -DNRF52_PAN_55 -DBOARD_PCA10040) - add_definitions(-DSOFTDEVICE_PRESENT -DS132 -DSWI_DISABLE0 -DBLE_STACK_SUPPORT_REQD -DNRF_SD_BLE_API_VERSION=6) add_definitions(-DFREERTOS) add_definitions(-DDEBUG_NRF_USER) - add_definitions(-D__STARTUP_CLEAR_BSS) - add_definitions(-D__HEAP_SIZE=8192) - add_definitions(-D__STACK_SIZE=2048) - include_directories( - "${NRF5_SDK_PATH}/components/softdevice/s132/headers" - "${NRF5_SDK_PATH}/components/softdevice/s132/headers/nrf52" + "${NRF5_SDK_PATH}/components/drivers_nrf/nrf_soc_nosd" ) list(APPEND SDK_SOURCE_FILES "${NRF5_SDK_PATH}/modules/nrfx/mdk/system_nrf52.c" "${NRF5_SDK_PATH}/modules/nrfx/mdk/gcc_startup_nrf52.S" ) - set(SOFTDEVICE_PATH "${NRF5_SDK_PATH}/components/softdevice/s132/hex/s132_nrf52_6.1.1_softdevice.hex") endif () set(COMMON_FLAGS "-MP -MD -mthumb -mabi=aapcs -Wall -g3 -ffunction-sections -fdata-sections -fno-strict-aliasing -fno-builtin --short-enums ${CPU_FLAGS} -Wreturn-type -Werror=return-type") @@ -242,7 +231,6 @@ macro(nRF5x_setup) # Other external include_directories( "${NRF5_SDK_PATH}/external/fprintf/" -# "${NRF5_SDK_PATH}/external/utf_converter/" ) list(APPEND SDK_SOURCE_FILES @@ -254,103 +242,25 @@ macro(nRF5x_setup) # LCD/GFX include_directories( "${NRF5_SDK_PATH}/external/thedotfactory_fonts" - "${NRF5_SDK_PATH}/components/ble/ble_db_discovery" ) - list(APPEND SDK_SOURCE_FILES - "${NRF5_SDK_PATH}/components/ble/ble_db_discovery/ble_db_discovery.c" - "${NRF5_SDK_PATH}/components/ble/ble_services/ble_cts_c/ble_cts_c.c" - "${NRF5_SDK_PATH}/components/ble/ble_services/ble_ans_c/ble_ans_c.c" -# "${NRF5_SDK_PATH}/external/thedotfactory_fonts/orkney24pts.c" - ) - - #BLE S132 - include_directories( - "${NRF5_SDK_PATH}/components/ble/common" - "${NRF5_SDK_PATH}/components/ble/ble_advertising" - "${NRF5_SDK_PATH}/components/ble/ble_services/ble_bas" - "${NRF5_SDK_PATH}/components/ble/ble_services/ble_hrs" - "${NRF5_SDK_PATH}/components/ble/ble_services/ble_dis" - "${NRF5_SDK_PATH}/components/ble/nrf_ble_gatt" - "${NRF5_SDK_PATH}/components/libraries/sensorsim" - "${NRF5_SDK_PATH}/components/ble/peer_manager" - "${NRF5_SDK_PATH}/components/ble/nrf_ble_qwr" - ) - - LIST(APPEND SDK_SOURCE_FILES - "${NRF5_SDK_PATH}//components/ble/common/ble_srv_common.c" - "${NRF5_SDK_PATH}/components/ble/ble_advertising/ble_advertising.c" - "${NRF5_SDK_PATH}/components/ble/common/ble_advdata.c" - "${NRF5_SDK_PATH}/components/ble/ble_services/ble_bas/ble_bas.c" - "${NRF5_SDK_PATH}/components/ble/ble_services/ble_hrs/ble_hrs.c" - "${NRF5_SDK_PATH}/components/ble/ble_services/ble_dis/ble_dis.c" - "${NRF5_SDK_PATH}/components/ble/nrf_ble_gatt/nrf_ble_gatt.c" - "${NRF5_SDK_PATH}/components/libraries/sensorsim/sensorsim.c" - "${NRF5_SDK_PATH}/components/ble/peer_manager/peer_manager.c" - "${NRF5_SDK_PATH}/components/ble/nrf_ble_qwr/nrf_ble_qwr.c" - "${NRF5_SDK_PATH}/components/ble/common/ble_conn_state.c" - "${NRF5_SDK_PATH}/components/ble/peer_manager/auth_status_tracker.c" - "${NRF5_SDK_PATH}/components/ble/peer_manager/gatt_cache_manager.c" - "${NRF5_SDK_PATH}/components/ble/peer_manager/gatts_cache_manager.c" - "${NRF5_SDK_PATH}/components/ble/peer_manager/id_manager.c" - "${NRF5_SDK_PATH}/components/ble/peer_manager/peer_data_storage.c" - "${NRF5_SDK_PATH}/components/ble/peer_manager/peer_database.c" - "${NRF5_SDK_PATH}/components/ble/peer_manager/peer_id.c" - "${NRF5_SDK_PATH}/components/ble/peer_manager/peer_manager.c" - "${NRF5_SDK_PATH}/components/ble/peer_manager/peer_manager_handler.c" - "${NRF5_SDK_PATH}/components/ble/peer_manager/pm_buffer.c" - "${NRF5_SDK_PATH}/components/ble/peer_manager/security_dispatcher.c" - "${NRF5_SDK_PATH}/components/ble/peer_manager/security_manager.c" - "${NRF5_SDK_PATH}/components/ble/common/ble_conn_state.c" - "${NRF5_SDK_PATH}/components/ble/common/ble_conn_params.c" - "${NRF5_SDK_PATH}/components/ble/common/ble_conn_state.c" - "${NRF5_SDK_PATH}/components/libraries/atomic_flags/nrf_atflags.c" - "${NRF5_SDK_PATH}/components/libraries/fds/fds.c" - "${NRF5_SDK_PATH}/components/libraries/fstorage/nrf_fstorage.c" - "${NRF5_SDK_PATH}/components/libraries/fstorage/nrf_fstorage_sd.c" - "${NRF5_SDK_PATH}/components/libraries/atomic_fifo/nrf_atfifo.c" - "${NRF5_SDK_PATH}/components/softdevice/common/nrf_sdh.c" - "${NRF5_SDK_PATH}/components/softdevice/common/nrf_sdh_ble.c" - "${NRF5_SDK_PATH}/components/softdevice/common/nrf_sdh_freertos.c" - "${NRF5_SDK_PATH}/components/softdevice/common/nrf_sdh_soc.c" - "${NRF5_SDK_PATH}/components/libraries/experimental_section_vars/nrf_section_iter.c" - "${NRF5_SDK_PATH}/components/libraries/bsp/bsp_btn_ble.c" - "${NRF5_SDK_PATH}/components/libraries/hardfault/hardfault_implementation.c" - "${NRF5_SDK_PATH}/components/libraries/hardfault/nrf52/handler/hardfault_handler_gcc.c" - ) - LIST(APPEND SDK_SOURCE_FILES "${NRF5_SDK_PATH}/modules/nrfx/drivers/src/nrfx_twi.c" ) - # adds target for erasing and flashing the board with a softdevice + # adds target for erasing if(USE_JLINK) - add_custom_target(FLASH_SOFTDEVICE - COMMAND ${NRFJPROG} --program ${SOFTDEVICE_PATH} -f ${NRF_TARGET} --sectorerase - COMMAND sleep 0.5s - COMMAND ${NRFJPROG} --reset -f ${NRF_TARGET} - COMMENT "flashing SoftDevice" - ) - add_custom_target(FLASH_ERASE COMMAND ${NRFJPROG} --eraseall -f ${NRF_TARGET} COMMENT "erasing flashing" ) elseif(USE_GDB_CLIENT) - add_custom_target(FLASH_SOFTDEVICE - COMMAND ${GDB_CLIENT_BIN_PATH} -nx --batch -ex 'target extended-remote ${GDB_CLIENT_TARGET_REMOTE}' -ex 'monitor swdp_scan' -ex 'attach 1' -ex 'load' -ex 'kill' ${SOFTDEVICE_PATH} - COMMENT "flashing SoftDevice" - ) add_custom_target(FLASH_ERASE COMMAND ${GDB_CLIENT_BIN_PATH} -nx --batch -ex 'target extended-remote ${GDB_CLIENT_TARGET_REMOTE}' -ex 'monitor swdp_scan' -ex 'attach 1' -ex 'mon erase_mass' COMMENT "erasing flashing" ) elseif(USE_OPENOCD) - add_custom_target(FLASH_SOFTDEVICE - COMMAND ${OPENOCD_BIN_PATH} -c "tcl_port disabled" -c "gdb_port 3333" -c "telnet_port 4444" -f interface/stlink.cfg -c 'transport select hla_swd' -f target/nrf52.cfg -c "program \"${SOFTDEVICE_PATH}\"" -c reset -c shutdown - COMMENT "flashing SoftDevice" - ) - add_custom_target(FLASH_ERASE + add_custom_target(FLASH_ERASE COMMAND ${OPENOCD_BIN_PATH} -f interface/stlink.cfg -c 'transport select hla_swd' -f target/nrf52.cfg -c init -c halt -c 'nrf5 mass_erase' -c reset -c shutdown COMMENT "erasing flashing" ) @@ -391,35 +301,6 @@ macro(nRF5x_addExecutable EXECUTABLE_NAME SOURCE_FILES) COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE_NAME}.out "${EXECUTABLE_NAME}.hex" COMMENT "post build steps for ${EXECUTABLE_NAME}") - if(MERGEHEX) - add_custom_command(TARGET ${EXECUTABLE_NAME} - POST_BUILD - COMMAND ${MERGEHEX} --merge ${EXECUTABLE_NAME}.hex ${NRF5_SDK_PATH}/components/softdevice/s132/hex/s132_nrf52_6.1.1_softdevice.hex --output ${EXECUTABLE_NAME}-full.hex - COMMENT "merging HEX files") - - if(USE_JLINK) - add_custom_target("FLASH_MERGED_${EXECUTABLE_NAME}" - DEPENDS ${EXECUTABLE_NAME} - COMMAND ${NRFJPROG} --program ${EXECUTABLE_NAME}-full.hex -f ${NRF_TARGET} --sectorerase - COMMAND sleep 0.5s - COMMAND ${NRFJPROG} --reset -f ${NRF_TARGET} - COMMENT "flashing ${EXECUTABLE_NAME}-full.hex" - ) - elseif(USE_GDB_CLIENT) - add_custom_target("FLASH_MERGED_${EXECUTABLE_NAME}" - DEPENDS ${EXECUTABLE_NAME} - COMMAND ${GDB_CLIENT_BIN_PATH} -nx --batch -ex 'target extended-remote ${GDB_CLIENT_TARGET_REMOTE}' -ex 'monitor swdp_scan' -ex 'attach 1' -ex 'load' -ex 'kill' ${EXECUTABLE_NAME}-full.hex - COMMENT "flashing ${EXECUTABLE_NAME}-full.hex" - ) - elseif(USE_OPENOCD) - add_custom_target("FLASH_MERGED_${EXECUTABLE_NAME}" - DEPENDS ${EXECUTABLE_NAME} - COMMAND ${OPENOCD_BIN_PATH} -c "tcl_port disabled" -c "gdb_port 3333" -c "telnet_port 4444" -f interface/stlink.cfg -c 'transport select hla_swd' -f target/nrf52.cfg -c "program \"${EXECUTABLE_NAME}-full.hex\"" -c reset -c shutdown - COMMENT "flashing ${EXECUTABLE_NAME}-full.hex" - ) - endif() - endif() - # custom target for flashing the board if(USE_JLINK) add_custom_target("FLASH_${EXECUTABLE_NAME}" diff --git a/doc/ble.md b/doc/ble.md index 0302b471..9a7c59a8 100644 --- a/doc/ble.md +++ b/doc/ble.md @@ -19,9 +19,10 @@ If **CTS** is detected, it'll request the current time to the companion applicat [List of standard BLE services](https://www.bluetooth.com/specifications/gatt/services/) ### CTS -[Current Time Service](https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Services/org.bluetooth.service.alert_notification.xml) +[Current Time Service](https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Services/org.bluetooth.service.current_time.xml) ### ANS -[Alert Notification Service](https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Services/org.bluetooth.service.current_time.xml) +[Alert Notification Service](https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Services/org.bluetooth.service.alert_notification.xml) + +![ANS sequence diagram](./ble/ans_sequence.png "ANS sequence diagram") -![ANS sequence diagram](./ble/ans_sequence.png "ANS sequence diagram") \ No newline at end of file diff --git a/gcc_nrf52.ld b/gcc_nrf52.ld index 992c6c2e..b849ea2c 100644 --- a/gcc_nrf52.ld +++ b/gcc_nrf52.ld @@ -5,8 +5,8 @@ GROUP(-lgcc -lc -lnosys) MEMORY { - FLASH (rx) : ORIGIN = 0x26000, LENGTH = 0x5a000 - RAM (rwx) : ORIGIN = 0x200057b8, LENGTH = 0xa848 + FLASH (rx) : ORIGIN = 0x00000, LENGTH = 0x80000 + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x10000 } SECTIONS diff --git a/src/BLE/BleManager.c b/src/BLE/BleManager.c deleted file mode 100644 index 8caf120a..00000000 --- a/src/BLE/BleManager.c +++ /dev/null @@ -1,780 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "nrf_sdh_soc.h" - -#include "BleManager.h" - -void ble_manager_init_stack(); - -void ble_manager_init_gap_params(); - - -void ble_manager_init_gatt(); -void ble_manager_init_db_discovery(); -void ble_manager_init_advertising(); -void ble_manager_init_peer_manager(); -void ble_manager_init_services(); -void ble_manager_init_connection_params(); - -void ble_manager_event_handler(ble_evt_t const *p_ble_evt, void *p_context); -void ble_manager_discover_handler(ble_db_discovery_evt_t *p_evt); -void ble_manager_advertising_event_handler(ble_adv_evt_t ble_adv_evt); -void ble_manager_peer_manager_event_handler(pm_evt_t const *p_evt); -void ble_manager_delete_bonds(); -void ble_manager_queue_write_error_handler(uint32_t nrf_error); -void ble_manager_cts_event_handler(ble_cts_c_t *p_cts, ble_cts_c_evt_t *p_evt); -void ble_manager_cts_error_handler(uint32_t nrf_error); -void ble_manager_cts_print_time(ble_cts_c_evt_t *p_evt); -void ble_manager_conn_params_event_handler(ble_conn_params_evt_t *p_evt); -void ble_manager_conn_params_error_handler(uint32_t nrf_error); - -typedef enum -{ - ALERT_NOTIFICATION_DISABLED, /**< Alert Notifications has been disabled. */ - ALERT_NOTIFICATION_ENABLED, /**< Alert Notifications has been enabled. */ - ALERT_NOTIFICATION_ON, /**< Alert State is on. */ -} ble_ans_c_alert_state_t; - -void on_ans_c_evt(ble_ans_c_evt_t * p_evt); -void alert_notification_error_handler(uint32_t nrf_error); -void handle_alert_notification(ble_ans_c_evt_t * p_evt); -void supported_alert_notification_read(void); -void alert_notification_setup(void); -void control_point_setup(ble_ans_c_evt_t * p_evt); - -uint16_t ble_manager_connection_handle = BLE_CONN_HANDLE_INVALID; // Handle of the current connection. -NRF_BLE_QWR_DEF(ble_manager_queue_write); // Context for the Queued Write module. -BLE_CTS_C_DEF(ble_manager_cts_client); // Current Time service instance. -NRF_BLE_GATT_DEF(ble_manager_gatt); // GATT module instance. -BLE_ADVERTISING_DEF(ble_manager_advertising); // Advertising module instance. -BLE_DB_DISCOVERY_DEF(ble_manager_db_discovery); -BLE_ANS_C_DEF(m_ans_c); - -static uint8_t m_alert_message_buffer[MESSAGE_BUFFER_SIZE]; /**< Message buffer for optional notify messages. */ -static ble_ans_c_alert_state_t m_new_alert_state = ALERT_NOTIFICATION_DISABLED; /**< State that holds the current state of New Alert Notifications, i.e. Enabled, Alert On, Disabled. */ -static ble_ans_c_alert_state_t m_unread_alert_state = ALERT_NOTIFICATION_DISABLED; /**< State that holds the current state of Unread Alert Notifications, i.e. Enabled, Alert On, Disabled. */ - -static ble_uuid_t ble_manager_advertising_uuids[] = /* Universally unique service identifiers.*/ - { -// {BLE_UUID_HEART_RATE_SERVICE, BLE_UUID_TYPE_BLE}, -// {BLE_UUID_BATTERY_SERVICE, BLE_UUID_TYPE_BLE}, - {BLE_UUID_DEVICE_INFORMATION_SERVICE, BLE_UUID_TYPE_BLE}, - {BLE_UUID_CURRENT_TIME_SERVICE, BLE_UUID_TYPE_BLE} - }; - -static char const *day_of_week[] = - { - "Unknown", - "Monday", - "Tuesday", - "Wednesday", - "Thursday", - "Friday", - "Saturday", - "Sunday" - }; - -static char const *month_of_year[] = - { - "Unknown", - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December" - }; - -static char const * lit_catid[BLE_ANS_NB_OF_CATEGORY_ID] = - { - "Simple alert", - "Email", - "News", - "Incoming call", - "Missed call", - "SMS/MMS", - "Voice mail", - "Schedule", - "High prioritized alert", - "Instant message" - }; - - -void ble_manager_init() { - ble_manager_init_stack(); - ble_manager_init_gap_params(); - ble_manager_init_gatt(); - ble_manager_init_db_discovery(); - ble_manager_init_advertising(); - ble_manager_init_services(); - ble_manager_init_connection_params(); -} - -void ble_manager_init_stack() { - ret_code_t err_code; - - err_code = nrf_sdh_enable_request(); - APP_ERROR_CHECK(err_code); - - // Configure the BLE stack using the default settings. - // Fetch the start address of the application RAM. - uint32_t ram_start = 0; - err_code = nrf_sdh_ble_default_cfg_set(BLE_MANAGER_CONN_CFG_TAG, &ram_start); - APP_ERROR_CHECK(err_code); - - // Enable BLE stack. - err_code = nrf_sdh_ble_enable(&ram_start); - APP_ERROR_CHECK(err_code); - - // Register a handler for BLE events. - NRF_SDH_BLE_OBSERVER(m_ble_observer, BLE_MANAGER__OBSERVER_PRIO, ble_manager_event_handler, NULL); -} - -void (*OnNewTimeCallback)(current_time_char_t*); -void ble_manager_set_new_time_callback(void (*OnNewTime)(current_time_char_t*)) { - OnNewTimeCallback = OnNewTime; -} - -void (*OnBleConnectionCallback)(); -void ble_manager_set_ble_connection_callback(void (*OnBleConnection)()) { - OnBleConnectionCallback = OnBleConnection; -} - -void (*OnBleDisconnectionCallback)(); -void ble_manager_set_ble_disconnection_callback(void (*OnBleDisconnection)()) { - OnBleDisconnectionCallback = OnBleDisconnection; -} - -void (*OnNewNotificationCallback)(const char* message, uint8_t size); -void ble_manager_set_new_notification_callback(void (*OnNewNotification)(const char*, uint8_t size)) { - OnNewNotificationCallback = OnNewNotification; -} - - -void ble_manager_event_handler(ble_evt_t const *p_ble_evt, void *p_context) { - uint32_t err_code; - - switch (p_ble_evt->header.evt_id) { - case BLE_GAP_EVT_CONNECTED: - NRF_LOG_INFO("[BLE] Connected to peer"); - ble_manager_connection_handle = p_ble_evt->evt.gap_evt.conn_handle; - err_code = nrf_ble_qwr_conn_handle_assign(&ble_manager_queue_write, ble_manager_connection_handle); - OnBleConnectionCallback(); - APP_ERROR_CHECK(err_code); - break; - - case BLE_GAP_EVT_DISCONNECTED: - NRF_LOG_INFO("[Ble] Disconnected from peer]"); - ble_manager_connection_handle = BLE_CONN_HANDLE_INVALID; - if (p_ble_evt->evt.gap_evt.conn_handle == ble_manager_cts_client.conn_handle) { - ble_manager_cts_client.conn_handle = BLE_CONN_HANDLE_INVALID; - } - OnBleDisconnectionCallback(); - break; - - case BLE_GAP_EVT_PHY_UPDATE_REQUEST: { - NRF_LOG_INFO("[BLE] PHY update request."); - ble_gap_phys_t const phys = - { - - .tx_phys = BLE_GAP_PHY_AUTO, - .rx_phys = BLE_GAP_PHY_AUTO, - }; - err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys); - APP_ERROR_CHECK(err_code); - } - break; - - case BLE_GATTC_EVT_TIMEOUT: - // Disconnect on GATT Client timeout event. - NRF_LOG_INFO("[BLE] GATT Client Timeout."); - err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle, - BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); - APP_ERROR_CHECK(err_code); - break; - - case BLE_GATTS_EVT_TIMEOUT: - // Disconnect on GATT Server timeout event. - NRF_LOG_INFO("[BLE] GATT Server Timeout."); - err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle, - BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); - APP_ERROR_CHECK(err_code); - break; - - default: - // No implementation needed. - break; - } -} - -void ble_manager_init_gap_params() { - ret_code_t err_code; - ble_gap_conn_params_t gap_conn_params; - ble_gap_conn_sec_mode_t sec_mode; - - BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); - - err_code = sd_ble_gap_device_name_set(&sec_mode, (const uint8_t *) BLE_MANAGER_DEVICE_NAME, - strlen(BLE_MANAGER_DEVICE_NAME)); - APP_ERROR_CHECK(err_code); - - err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_HEART_RATE_SENSOR_HEART_RATE_BELT); - APP_ERROR_CHECK(err_code); - - memset(&gap_conn_params, 0, sizeof(gap_conn_params)); - - gap_conn_params.min_conn_interval = BLE_MANAGER_MIN_CONN_INTERVAL; - gap_conn_params.max_conn_interval = BLE_MANAGER_MAX_CONN_INTERVAL; - gap_conn_params.slave_latency = BLE_MANAGER_SLAVE_LATENCY; - gap_conn_params.conn_sup_timeout = BLE_MANAGER_CONN_SUP_TIMEOUT; - - err_code = sd_ble_gap_ppcp_set(&gap_conn_params); - APP_ERROR_CHECK(err_code); -} - -void ble_manager_init_gatt() { - ret_code_t err_code = nrf_ble_gatt_init(&ble_manager_gatt, NULL); - APP_ERROR_CHECK(err_code); -} - -void ble_manager_init_db_discovery() { - ret_code_t err_code = ble_db_discovery_init(ble_manager_discover_handler); - APP_ERROR_CHECK(err_code); -} - -void ble_manager_discover_handler(ble_db_discovery_evt_t *p_evt) { - ble_cts_c_on_db_disc_evt(&ble_manager_cts_client, p_evt); - ble_ans_c_on_db_disc_evt(&m_ans_c, p_evt); -} - -void ble_manager_init_advertising() { - ret_code_t err_code; - ble_advertising_init_t init; - - memset(&init, 0, sizeof(init)); - - init.advdata.name_type = BLE_ADVDATA_FULL_NAME; - init.advdata.include_appearance = true; - init.advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; - init.advdata.uuids_complete.uuid_cnt = - sizeof(ble_manager_advertising_uuids) / sizeof(ble_manager_advertising_uuids[0]); - init.advdata.uuids_complete.p_uuids = ble_manager_advertising_uuids; - - init.config.ble_adv_whitelist_enabled = true; - init.config.ble_adv_fast_enabled = true; - init.config.ble_adv_fast_interval = BLE_MANAGER_ADV_INTERVAL; - init.config.ble_adv_fast_timeout = BLE_MANAGER_ADV_DURATION; - - init.evt_handler = ble_manager_advertising_event_handler; - - err_code = ble_advertising_init(&ble_manager_advertising, &init); - APP_ERROR_CHECK(err_code); - - ble_advertising_conn_cfg_tag_set(&ble_manager_advertising, BLE_MANAGER_CONN_CFG_TAG); -} - -void ble_manager_advertising_event_handler(ble_adv_evt_t ble_adv_evt) { - uint32_t err_code; - - switch (ble_adv_evt) { - case BLE_ADV_EVT_FAST: - NRF_LOG_INFO("[Advertising] Fast advertising started."); - break; - - case BLE_ADV_EVT_IDLE: - NRF_LOG_INFO("[Advertising] Idling..."); - break; - - default: - break; - } -} - -bool record_delete_next(void) -{ - fds_find_token_t tok = {0}; - fds_record_desc_t desc = {0}; - - if (fds_record_iterate(&desc, &tok) == FDS_SUCCESS) - { - ret_code_t rc = fds_record_delete(&desc); - if (rc != FDS_SUCCESS) - { - return false; - } - - return true; - } - else - { - /* No records left to delete. */ - return false; - } -} - -void ble_manager_init_peer_manager() { - ble_gap_sec_params_t sec_param; - ret_code_t err_code; - - err_code = pm_init(); - if(err_code != NRF_SUCCESS) { - // Many errors can occur here, but the most probable is the "Storage full" error from sdk/components/libraries/fds/fds.c : FDS_ERR_NO_PAGES. - // TODO : it erases the whole memory, it's not nice to do that... - NRF_LOG_WARNING("Error while initializing BLE peer management, Erasing all record from memory"); - do { - - } while(record_delete_next()); - err_code = pm_init(); - } - APP_ERROR_CHECK(err_code); - - memset(&sec_param, 0, sizeof(ble_gap_sec_params_t)); - - // Security parameters to be used for all security procedures. - sec_param.bond = BLE_MANAGER_SEC_PARAM_BOND; - sec_param.mitm = BLE_MANAGER_SEC_PARAM_MITM; - sec_param.lesc = BLE_MANAGER_SEC_PARAM_LESC; - sec_param.keypress = BLE_MANAGER_SEC_PARAM_KEYPRESS; - sec_param.io_caps = BLE_MANAGER_SEC_PARAM_IO_CAPABILITIES; - sec_param.oob = BLE_MANAGER_SEC_PARAM_OOB; - sec_param.min_key_size = BLE_MANAGER_SEC_PARAM_MIN_KEY_SIZE; - sec_param.max_key_size = BLE_MANAGER_SEC_PARAM_MAX_KEY_SIZE; - sec_param.kdist_own.enc = 1; - sec_param.kdist_own.id = 1; - sec_param.kdist_peer.enc = 1; - sec_param.kdist_peer.id = 1; - - err_code = pm_sec_params_set(&sec_param); - APP_ERROR_CHECK(err_code); - - err_code = pm_register(ble_manager_peer_manager_event_handler); - APP_ERROR_CHECK(err_code); -} - -void ble_manager_peer_manager_event_handler(pm_evt_t const *p_evt) { - bool delete_bonds = false; - ret_code_t err_code; - pm_handler_on_pm_evt(p_evt); - pm_handler_flash_clean(p_evt); - - switch (p_evt->evt_id) { - case PM_EVT_CONN_SEC_SUCCEEDED: { - NRF_LOG_INFO("[Peer management] A link has been secured, starting service discovery."); - err_code = ble_db_discovery_start(&ble_manager_db_discovery, p_evt->conn_handle); - APP_ERROR_CHECK(err_code); - } - break; - - case PM_EVT_PEERS_DELETE_SUCCEEDED: - NRF_LOG_INFO("[Peer management] All peers data has been successfuly deleted."); - ble_manager_start_advertising(&delete_bonds); - break; - - case PM_EVT_STORAGE_FULL: { - NRF_LOG_INFO("[Peer management] Storage full, trying to run garbage collection on flash storage."); - // Run garbage collection on the flash. - err_code = fds_gc(); - if (err_code == FDS_ERR_BUSY || err_code == FDS_ERR_NO_SPACE_IN_QUEUES) - { - NRF_LOG_INFO("[Peer management] Garbage collection issue."); - // Retry. - } - else - { - APP_ERROR_CHECK(err_code); - NRF_LOG_INFO("[Peer management] Garbage collection done."); - } - }break; - - default: - break; - } -} - -void ble_manager_start_advertising(void *p_erase_bonds) { - bool erase_bonds = *(bool *) p_erase_bonds; - - if (erase_bonds) { - ble_manager_delete_bonds(); - // Advertising is started by PM_EVT_PEERS_DELETE_SUCCEEDED event. - } else { - ret_code_t err_code = ble_advertising_start(&ble_manager_advertising, BLE_ADV_MODE_FAST); - APP_ERROR_CHECK(err_code); - } -} - -void handle_alert_notification(ble_ans_c_evt_t * p_evt) -{ - ret_code_t err_code; - - if (p_evt->uuid.uuid == BLE_UUID_UNREAD_ALERT_CHAR) - { - if (m_unread_alert_state == ALERT_NOTIFICATION_ENABLED) - { -// err_code = bsp_indication_set(BSP_INDICATE_ALERT_1); - APP_ERROR_CHECK(err_code); - m_unread_alert_state = ALERT_NOTIFICATION_ON; - NRF_LOG_INFO("Unread Alert state: On."); - NRF_LOG_INFO(" Category: %s", - (uint32_t)lit_catid[p_evt->data.alert.alert_category]); - NRF_LOG_INFO(" Number of unread alerts: %d", - p_evt->data.alert.alert_category_count); - } - } - else if (p_evt->uuid.uuid == BLE_UUID_NEW_ALERT_CHAR) - { - m_new_alert_state = ALERT_NOTIFICATION_ON; - NRF_LOG_INFO("New Alert state: On."); - NRF_LOG_INFO(" Category: %s", - (uint32_t)lit_catid[p_evt->data.alert.alert_category]); - NRF_LOG_INFO(" Number of new alerts: %d", - p_evt->data.alert.alert_category_count); - NRF_LOG_INFO(" Text String Information: (%d) %s", - p_evt->data.alert.alert_msg_length, (uint32_t)p_evt->data.alert.p_alert_msg_buf); - - OnNewNotificationCallback(p_evt->data.alert.p_alert_msg_buf, p_evt->data.alert.alert_msg_length); - } - else - { - // Only Unread and New Alerts exists, thus do nothing. - } -} - -void supported_alert_notification_read(void) -{ - NRF_LOG_INFO("Read supported Alert Notification characteristics on the connected peer."); - ret_code_t err_code; - - err_code = ble_ans_c_new_alert_read(&m_ans_c); - APP_ERROR_CHECK(err_code); - - err_code = ble_ans_c_unread_alert_read(&m_ans_c); - APP_ERROR_CHECK(err_code); -} - -void alert_notification_setup(void) -{ - ret_code_t err_code; - - err_code = ble_ans_c_enable_notif_new_alert(&m_ans_c); - APP_ERROR_CHECK(err_code); - - m_new_alert_state = ALERT_NOTIFICATION_ENABLED; - NRF_LOG_INFO("New Alert State: Enabled."); - - err_code = ble_ans_c_enable_notif_unread_alert(&m_ans_c); - APP_ERROR_CHECK(err_code); - - m_unread_alert_state = ALERT_NOTIFICATION_ENABLED; - NRF_LOG_INFO("Unread Alert State: Enabled."); - - NRF_LOG_INFO("Notifications enabled."); -} - -void control_point_setup(ble_ans_c_evt_t * p_evt) -{ - uint32_t err_code; - ble_ans_control_point_t setting; - - if (p_evt->uuid.uuid == BLE_UUID_SUPPORTED_UNREAD_ALERT_CATEGORY_CHAR) - { - setting.command = ANS_ENABLE_UNREAD_CATEGORY_STATUS_NOTIFICATION; - setting.category = (ble_ans_category_id_t)p_evt->data.alert.alert_category; - NRF_LOG_INFO("Unread status notification enabled for received categories."); - } - else if (p_evt->uuid.uuid == BLE_UUID_SUPPORTED_NEW_ALERT_CATEGORY_CHAR) - { - setting.command = ANS_ENABLE_NEW_INCOMING_ALERT_NOTIFICATION; - setting.category = (ble_ans_category_id_t)p_evt->data.alert.alert_category; - NRF_LOG_INFO("New incoming notification enabled for received categories."); - } - else - { - return; - } - - err_code = ble_ans_c_control_point_write(&m_ans_c, &setting); - APP_ERROR_CHECK(err_code); -} - -void on_ans_c_evt(ble_ans_c_evt_t * p_evt) -{ - ret_code_t err_code; - - switch (p_evt->evt_type) { - case BLE_ANS_C_EVT_DISCOVERY_FAILED: - // TODO When another service is found, this event is sent to all the other service handled. - // In this case, this is not an error, it just tells that the service that have just been found is not this one... - NRF_LOG_INFO("[ANS] Discovery failed"); - break; - case BLE_ANS_C_EVT_NOTIFICATION: - handle_alert_notification(p_evt); - NRF_LOG_INFO("[ANS] Alert Notification received from server, UUID: %X.", p_evt->uuid.uuid); - break; // BLE_ANS_C_EVT_NOTIFICATION - - case BLE_ANS_C_EVT_DISCOVERY_COMPLETE: - NRF_LOG_INFO("[ANS] Alert Notification Service discovered on the server."); - err_code = ble_ans_c_handles_assign(&m_ans_c, - p_evt->conn_handle, - &p_evt->data.service); - APP_ERROR_CHECK(err_code); - supported_alert_notification_read(); - alert_notification_setup(); - break; // BLE_ANS_C_EVT_DISCOVERY_COMPLETE - - case BLE_ANS_C_EVT_READ_RESP: - NRF_LOG_INFO("[ANS] Alert Setup received from server, UUID: %X.", p_evt->uuid.uuid); - control_point_setup(p_evt); - break; // BLE_ANS_C_EVT_READ_RESP - - case BLE_ANS_C_EVT_DISCONN_COMPLETE: - NRF_LOG_INFO("[ANS] ANS : disconnecting from server"); - m_new_alert_state = ALERT_NOTIFICATION_DISABLED; - m_unread_alert_state = ALERT_NOTIFICATION_DISABLED; - break; // BLE_ANS_C_EVT_DISCONN_COMPLETE - - default: - // No implementation needed. - break; - } -} - -void alert_notification_error_handler(uint32_t nrf_error) -{ - APP_ERROR_HANDLER(nrf_error); -} - -void ble_manager_init_services() { - ret_code_t err_code; - ble_hrs_init_t hrs_init; - ble_bas_init_t bas_init; - ble_dis_init_t dis_init; - ble_cts_c_init_t cts_init; - ble_ans_c_init_t ans_init_obj; - nrf_ble_qwr_init_t qwr_init = {0}; - uint8_t body_sensor_location; - - // Initialize Queued Write Module. - qwr_init.error_handler = ble_manager_queue_write_error_handler; - - err_code = nrf_ble_qwr_init(&ble_manager_queue_write, &qwr_init); - APP_ERROR_CHECK(err_code); - - // Initialize Heart Rate Service. - body_sensor_location = BLE_HRS_BODY_SENSOR_LOCATION_FINGER; - - memset(&hrs_init, 0, sizeof(hrs_init)); - - hrs_init.evt_handler = NULL; - hrs_init.is_sensor_contact_supported = true; - hrs_init.p_body_sensor_location = &body_sensor_location; - - // Here the sec level for the Heart Rate Service can be changed/increased. - hrs_init.hrm_cccd_wr_sec = SEC_OPEN; - hrs_init.bsl_rd_sec = SEC_OPEN; - - // Initialize Battery Service. -// memset(&bas_init, 0, sizeof(bas_init)); -// -// // Here the sec level for the Battery Service can be changed/increased. -// bas_init.bl_rd_sec = SEC_OPEN; -// bas_init.bl_cccd_wr_sec = SEC_OPEN; -// bas_init.bl_report_rd_sec = SEC_OPEN; -// -// bas_init.evt_handler = NULL; -// bas_init.support_notification = true; -// bas_init.p_report_ref = NULL; -// bas_init.initial_batt_level = 100; -// -// err_code = ble_bas_init(&m_bas, &bas_init); -// APP_ERROR_CHECK(err_code); - - // Initialize Device Information Service. - memset(&dis_init, 0, sizeof(dis_init)); - - ble_srv_ascii_to_utf8(&dis_init.manufact_name_str, (char *) BLE_MANAGER_MANUFACTURER_NAME); - - dis_init.dis_char_rd_sec = SEC_OPEN; - - err_code = ble_dis_init(&dis_init); - APP_ERROR_CHECK(err_code); - - // Initialize CTS. - cts_init.evt_handler = ble_manager_cts_event_handler; - cts_init.error_handler = ble_manager_cts_error_handler; - err_code = ble_cts_c_init(&ble_manager_cts_client, &cts_init); - APP_ERROR_CHECK(err_code); - - // Alert Notification service - memset(&ans_init_obj, 0, sizeof(ans_init_obj)); - memset(m_alert_message_buffer, 0, MESSAGE_BUFFER_SIZE); - - ans_init_obj.evt_handler = on_ans_c_evt; - ans_init_obj.message_buffer_size = MESSAGE_BUFFER_SIZE; - ans_init_obj.p_message_buffer = m_alert_message_buffer; - ans_init_obj.error_handler = alert_notification_error_handler; - - err_code = ble_ans_c_init(&m_ans_c, &ans_init_obj); - - APP_ERROR_CHECK(err_code); -} - -void ble_manager_queue_write_error_handler(uint32_t nrf_error) { - APP_ERROR_HANDLER(nrf_error); -} - -void ble_manager_cts_event_handler(ble_cts_c_t *p_cts, ble_cts_c_evt_t *p_evt) { - ret_code_t err_code; - - switch (p_evt->evt_type) { - case BLE_CTS_C_EVT_DISCOVERY_COMPLETE: - NRF_LOG_INFO("[CTS] Current Time Service discovered on server, requesting current time..."); - err_code = ble_cts_c_handles_assign(&ble_manager_cts_client, - p_evt->conn_handle, - &p_evt->params.char_handles); - - ble_cts_c_current_time_read(&ble_manager_cts_client); - APP_ERROR_CHECK(err_code); - break; - - case BLE_CTS_C_EVT_DISCOVERY_FAILED: - // TODO When another service is found, this event is sent to all the other service handled. - // In this case, this is not an error, it just tells that the service that have just been found is not this one... - NRF_LOG_INFO("[CTS] Current Time Service not found on server."); - break; - - case BLE_CTS_C_EVT_DISCONN_COMPLETE: - NRF_LOG_INFO("[CTS] Disconnect Complete."); - break; - - case BLE_CTS_C_EVT_CURRENT_TIME: - NRF_LOG_INFO("[CTS] Current Time received."); - ble_manager_cts_print_time(p_evt); - break; - - case BLE_CTS_C_EVT_INVALID_TIME: - NRF_LOG_INFO("[CTS] Invalid Time received."); - break; - - default: - break; - } -} - -void ble_manager_cts_error_handler(uint32_t nrf_error) { - APP_ERROR_HANDLER(nrf_error); -} - -void ble_manager_cts_print_time(ble_cts_c_evt_t *p_evt) { - NRF_LOG_INFO("\r\nCurrent Time:"); - NRF_LOG_INFO("\r\nDate:"); - - NRF_LOG_INFO("\tDay of week %s", (uint32_t) day_of_week[p_evt-> - params. - current_time. - exact_time_256. - day_date_time. - day_of_week]); - - if (p_evt->params.current_time.exact_time_256.day_date_time.date_time.day == 0) { - NRF_LOG_INFO("\tDay of month Unknown"); - } else { - NRF_LOG_INFO("\tDay of month %i", - p_evt->params.current_time.exact_time_256.day_date_time.date_time.day); - } - - NRF_LOG_INFO("\tMonth of year %s", - (uint32_t) month_of_year[p_evt->params.current_time.exact_time_256.day_date_time.date_time.month]); - if (p_evt->params.current_time.exact_time_256.day_date_time.date_time.year == 0) { - NRF_LOG_INFO("\tYear Unknown"); - } else { - NRF_LOG_INFO("\tYear %i", - p_evt->params.current_time.exact_time_256.day_date_time.date_time.year); - } - NRF_LOG_INFO("\r\nTime:"); - NRF_LOG_INFO("\tHours %i", - p_evt->params.current_time.exact_time_256.day_date_time.date_time.hours); - NRF_LOG_INFO("\tMinutes %i", - p_evt->params.current_time.exact_time_256.day_date_time.date_time.minutes); - NRF_LOG_INFO("\tSeconds %i", - p_evt->params.current_time.exact_time_256.day_date_time.date_time.seconds); - NRF_LOG_INFO("\tFractions %i/256 of a second", - p_evt->params.current_time.exact_time_256.fractions256); - - NRF_LOG_INFO("\r\nAdjust reason:\r"); - NRF_LOG_INFO("\tDaylight savings %x", - p_evt->params.current_time.adjust_reason.change_of_daylight_savings_time); - NRF_LOG_INFO("\tTime zone %x", - p_evt->params.current_time.adjust_reason.change_of_time_zone); - NRF_LOG_INFO("\tExternal update %x", - p_evt->params.current_time.adjust_reason.external_reference_time_update); - NRF_LOG_INFO("\tManual update %x", - p_evt->params.current_time.adjust_reason.manual_time_update); - - OnNewTimeCallback(&p_evt->params.current_time); -} - -void ble_manager_init_connection_params() { - ret_code_t err_code; - ble_conn_params_init_t cp_init; - - memset(&cp_init, 0, sizeof(cp_init)); - - cp_init.p_conn_params = NULL; - cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY; - cp_init.next_conn_params_update_delay = NEXT_CONN_PARAMS_UPDATE_DELAY; - cp_init.max_conn_params_update_count = MAX_CONN_PARAMS_UPDATE_COUNT; - cp_init.start_on_notify_cccd_handle = BLE_GATT_HANDLE_INVALID; - cp_init.disconnect_on_fail = false; - cp_init.evt_handler = ble_manager_conn_params_event_handler; - cp_init.error_handler = ble_manager_conn_params_error_handler; - - err_code = ble_conn_params_init(&cp_init); - APP_ERROR_CHECK(err_code); -} - -void ble_manager_conn_params_event_handler(ble_conn_params_evt_t *p_evt) { - ret_code_t err_code; - - if(p_evt->evt_type == BLE_CONN_PARAMS_EVT_SUCCEEDED) { - NRF_LOG_INFO("BLE connection parameters negotiation successful!"); - } else if(p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED) { - NRF_LOG_ERROR("BLE connection parameters negotiation error, disconnecting."); - err_code = sd_ble_gap_disconnect(ble_manager_connection_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE); - APP_ERROR_CHECK(err_code); - } -} - -void ble_manager_conn_params_error_handler(uint32_t nrf_error) { - APP_ERROR_HANDLER(nrf_error); -} - -void ble_manager_delete_bonds() { - ret_code_t err_code; - - NRF_LOG_INFO("Erase bonds!"); - - err_code = pm_peers_delete(); - APP_ERROR_CHECK(err_code); -} diff --git a/src/BLE/BleManager.h b/src/BLE/BleManager.h deleted file mode 100644 index da5f8e3f..00000000 --- a/src/BLE/BleManager.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define BLE_MANAGER_CONN_CFG_TAG 1 /* A tag identifying the SoftDevice BLE configuration. */ -#define BLE_MANAGER__OBSERVER_PRIO 3 /* Application's BLE observer priority. You shouldn't need to modify this value. */ -#define BLE_MANAGER_DEVICE_NAME "PineTime" /* Name of device. Will be included in the advertising data.*/ -#define BLE_MANAGER_MANUFACTURER_NAME "Codingfield" - -#define BLE_MANAGER_MIN_CONN_INTERVAL MSEC_TO_UNITS(100, UNIT_1_25_MS) /* Minimum acceptable connection interval (0.4 seconds).*/ -#define BLE_MANAGER_MAX_CONN_INTERVAL MSEC_TO_UNITS(650, UNIT_1_25_MS) /*Maximum acceptable connection interval (0.65 second).*/ -#define BLE_MANAGER_SLAVE_LATENCY 0 /* Slave latency.*/ -#define BLE_MANAGER_CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) /* Connection supervisory time-out (4 seconds).*/ - -#define BLE_MANAGER_ADV_INTERVAL 300 /* The advertising interval (in units of 0.625 ms. This value corresponds to 187.5 ms).*/ -#define BLE_MANAGER_ADV_DURATION 18000 /* The advertising duration (180 seconds) in units of 10 milliseconds.*/ - -#define BLE_MANAGER_SEC_PARAM_BOND 1 /* Perform bonding. */ -#define BLE_MANAGER_SEC_PARAM_MITM 0 /* Man In The Middle protection not required. */ -#define BLE_MANAGER_SEC_PARAM_LESC 0 /* LE Secure Connections not enabled. */ -#define BLE_MANAGER_SEC_PARAM_KEYPRESS 0 /* Keypress notifications not enabled. */ -#define BLE_MANAGER_SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_NONE /* No I/O capabilities. */ -#define BLE_MANAGER_SEC_PARAM_OOB 0 /* Out Of Band data not available. */ -#define BLE_MANAGER_SEC_PARAM_MIN_KEY_SIZE 7 /* Minimum encryption key size. */ -#define BLE_MANAGER_SEC_PARAM_MAX_KEY_SIZE 16 /* Maximum encryption key size. */ - -#define FIRST_CONN_PARAMS_UPDATE_DELAY 5000 /* Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */ -#define NEXT_CONN_PARAMS_UPDATE_DELAY 30000 /* Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */ -#define MAX_CONN_PARAMS_UPDATE_COUNT 3 /* Number of attempts before giving up the connection parameter negotiation. */ - -#define MESSAGE_BUFFER_SIZE 18 /**< Size of buffer holding optional messages in notifications. */ -#define BLE_ANS_NB_OF_CATEGORY_ID 10 /**< Number of categories. */ - -void ble_manager_init(); -void ble_manager_start_advertising(void *p_erase_bonds); -void ble_manager_init_peer_manager(); - -// TODO use signals from RTOS to notify new time -void ble_manager_set_new_time_callback(void (*OnNewTime)(current_time_char_t* currentTime)); -void ble_manager_set_ble_disconnection_callback(void (*OnBleDisconnection)()); -void ble_manager_set_ble_connection_callback(void (*OnBleConnection)()); - -void ble_manager_set_new_notification_callback(void (*OnNewNotification)(const char* message, uint8_t size)); - - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e92e3998..4b1f7c16 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -23,11 +23,101 @@ nRF5x_addAppGpiote() add_definitions(-DCONFIG_GPIO_AS_PINRESET) add_definitions(-DDEBUG) - +add_definitions(-DNIMBLE_CFG_CONTROLLER) +add_definitions(-DOS_CPUTIME_FREQ) include_directories(.) include_directories(libs/) +set(TINYCRYPT_SRC + libs/mynewt-nimble/ext/tinycrypt/src/aes_encrypt.c + libs/mynewt-nimble/ext/tinycrypt/src/utils.c + ) + +set(NIMBLE_SRC + libs/mynewt-nimble/porting/npl/freertos/src/nimble_port_freertos.c + libs/mynewt-nimble/porting/npl/freertos/src/npl_os_freertos.c + + + + libs/mynewt-nimble/nimble/host/src/ble_hs.c + libs/mynewt-nimble/nimble/host/src/ble_hs_hci_evt.c + libs/mynewt-nimble/nimble/host/src/ble_l2cap_sig_cmd.c + libs/mynewt-nimble/nimble/host/src/ble_l2cap_sig.c + libs/mynewt-nimble/nimble/host/src/ble_l2cap.c + libs/mynewt-nimble/nimble/host/src/ble_hs_mbuf.c + libs/mynewt-nimble/nimble/host/src/ble_sm.c + libs/mynewt-nimble/nimble/host/src/ble_gap.c + libs/mynewt-nimble/nimble/host/src/ble_gatts.c + libs/mynewt-nimble/nimble/host/src/ble_gattc.c + libs/mynewt-nimble/nimble/host/src/ble_hs_conn.c + libs/mynewt-nimble/nimble/host/src/ble_att_svr.c + libs/mynewt-nimble/nimble/host/src/ble_store.c + libs/mynewt-nimble/nimble/host/src/ble_store_util.c + libs/mynewt-nimble/nimble/host/src/ble_hs_pvcy.c + libs/mynewt-nimble/nimble/host/src/ble_hs_hci.c + libs/mynewt-nimble/nimble/host/src/ble_hs_log.c + libs/mynewt-nimble/nimble/host/src/ble_hs_hci_util.c + libs/mynewt-nimble/nimble/host/src/ble_hs_hci_cmd.c + libs/mynewt-nimble/nimble/host/src/ble_hs_cfg.c + libs/mynewt-nimble/nimble/host/src/ble_uuid.c + libs/mynewt-nimble/nimble/host/src/ble_hs_id.c + libs/mynewt-nimble/nimble/host/src/ble_hs_misc.c + libs/mynewt-nimble/nimble/host/src/ble_att.c + libs/mynewt-nimble/nimble/host/src/ble_att_clt.c + libs/mynewt-nimble/nimble/host/src/ble_att_svr.c + libs/mynewt-nimble/nimble/host/src/ble_att_cmd.c + libs/mynewt-nimble/nimble/host/src/ble_hs_atomic.c + libs/mynewt-nimble/nimble/host/src/ble_hs_adv.c + libs/mynewt-nimble/nimble/host/src/ble_hs_flow.c + libs/mynewt-nimble/nimble/host/src/ble_sm.c + libs/mynewt-nimble/nimble/host/src/ble_sm_cmd.c + libs/mynewt-nimble/nimble/host/src/ble_sm_lgcy.c + libs/mynewt-nimble/nimble/host/src/ble_sm_alg.c + libs/mynewt-nimble/nimble/host/src/ble_hs_mqueue.c + libs/mynewt-nimble/nimble/host/src/ble_hs_stop.c + libs/mynewt-nimble/nimble/host/src/ble_hs_startup.c + libs/mynewt-nimble/nimble/host/store/ram/src/ble_store_ram.c + + libs/mynewt-nimble/nimble/transport/ram/src/ble_hci_ram.c + + + + libs/mynewt-nimble/nimble/controller/src/ble_ll.c + libs/mynewt-nimble/nimble/controller/src/ble_ll_rand.c + libs/mynewt-nimble/nimble/controller/src/ble_ll_conn.c + libs/mynewt-nimble/nimble/controller/src/ble_ll_ctrl.c + libs/mynewt-nimble/nimble/controller/src/ble_ll_hci.c + libs/mynewt-nimble/nimble/controller/src/ble_ll_conn_hci.c + libs/mynewt-nimble/nimble/controller/src/ble_ll_utils.c + libs/mynewt-nimble/nimble/controller/src/ble_ll_scan.c + libs/mynewt-nimble/nimble/controller/src/ble_ll_whitelist.c + libs/mynewt-nimble/nimble/controller/src/ble_ll_adv.c + libs/mynewt-nimble/nimble/controller/src/ble_ll_sched.c + libs/mynewt-nimble/nimble/controller/src/ble_ll_supp_cmd.c + libs/mynewt-nimble/nimble/controller/src/ble_ll_hci_ev.c + libs/mynewt-nimble/nimble/controller/src/ble_ll_rfmgmt.c + + + + libs/mynewt-nimble/porting/nimble/src/os_cputime.c + libs/mynewt-nimble/porting/nimble/src/os_cputime_pwr2.c + libs/mynewt-nimble/porting/nimble/src/os_mbuf.c + libs/mynewt-nimble/porting/nimble/src/os_mempool.c + libs/mynewt-nimble/porting/nimble/src/hal_timer.c + libs/mynewt-nimble/porting/nimble/src/mem.c + libs/mynewt-nimble/porting/nimble/src/endian.c + libs/mynewt-nimble/porting/nimble/src/os_msys_init.c + + libs/mynewt-nimble/nimble/drivers/nrf52/src/ble_hw.c + libs/mynewt-nimble/nimble/drivers/nrf52/src/ble_phy.c + + libs/mynewt-nimble/nimble/host/services/gap/src/ble_svc_gap.c + libs/mynewt-nimble/nimble/host/services/gatt/src/ble_svc_gatt.c + + libs/mynewt-nimble/nimble/host/util/src/addr.c + ) + set(LVGL_SRC libs/lv_conf.h libs/lvgl/lvgl.h @@ -220,16 +310,23 @@ list(APPEND SOURCE_FILES drivers/SpiMaster.cpp drivers/Watchdog.cpp drivers/DebugPins.cpp - BLE/BleManager.c Components/Battery/BatteryController.cpp Components/Ble/BleController.cpp Components/Ble/NotificationManager.cpp Components/DateTime/DateTimeController.cpp Components/Brightness/BrightnessController.cpp + Components/Ble/NimbleController.cpp + Components/Ble/DeviceInformationService.cpp + Components/Ble/CurrentTimeClient.cpp + Components/Ble/AlertNotificationClient.cpp + Components/Ble/CurrentTimeService.cpp + Components/Ble/AlertNotificationService.cpp drivers/Cst816s.cpp FreeRTOS/port.c FreeRTOS/port_cmsis_systick.c FreeRTOS/port_cmsis.c + ${TINYCRYPT_SRC} + ${NIMBLE_SRC} ${LVGL_SRC} ${IMAGE_FILES} @@ -262,12 +359,15 @@ set(INCLUDE_FILES drivers/SpiMaster.h drivers/Watchdog.h drivers/DebugPins.h - BLE/BleManager.h Components/Battery/BatteryController.h Components/Ble/BleController.h Components/Ble/NotificationManager.h Components/DateTime/DateTimeController.h Components/Brightness/BrightnessController.h + Components/Ble/NimbleController.h + Components/Ble/DeviceInformationService.h + Components/Ble/CurrentTimeClient.h + Components/Ble/AlertNotificationClient.h drivers/Cst816s.h FreeRTOS/portmacro.h FreeRTOS/portmacro_cmsis.h @@ -288,6 +388,18 @@ set(INCLUDE_FILES include_directories( FreeRTOS/ libs/date/includes + libs/mynewt-nimble/porting/npl/freertos/include + libs/mynewt-nimble/nimble/include + libs/mynewt-nimble/porting/nimble/include + libs/mynewt-nimble/nimble/host/include + libs/mynewt-nimble/nimble/controller/include + libs/mynewt-nimble/nimble/transport/ram/include + libs/mynewt-nimble/nimble/drivers/nrf52/include + libs/mynewt-nimble/ext/tinycrypt/include + libs/mynewt-nimble/nimble/host/services/gap/include + libs/mynewt-nimble/nimble/host/services/gatt/include + libs/mynewt-nimble/nimble/host/util/include + libs/mynewt-nimble/nimble/host/store/ram/include ) link_directories( diff --git a/src/Components/Ble/AlertNotificationClient.cpp b/src/Components/Ble/AlertNotificationClient.cpp new file mode 100644 index 00000000..6e096353 --- /dev/null +++ b/src/Components/Ble/AlertNotificationClient.cpp @@ -0,0 +1,138 @@ +#include +#include "NotificationManager.h" + +#include "AlertNotificationClient.h" + + +using namespace Pinetime::Controllers; +constexpr ble_uuid16_t AlertNotificationClient::ansServiceUuid; + +constexpr ble_uuid16_t AlertNotificationClient::supportedNewAlertCategoryUuid; +constexpr ble_uuid16_t AlertNotificationClient::supportedUnreadAlertCategoryUuid ; +constexpr ble_uuid16_t AlertNotificationClient::newAlertUuid; +constexpr ble_uuid16_t AlertNotificationClient::unreadAlertStatusUuid; +constexpr ble_uuid16_t AlertNotificationClient::controlPointUuid; + +int Pinetime::Controllers::NewAlertSubcribeCallback(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) { + auto client = static_cast(arg); + return client->OnNewAlertSubcribe(conn_handle, error, attr); +} + +AlertNotificationClient::AlertNotificationClient(Pinetime::System::SystemTask& systemTask, + Pinetime::Controllers::NotificationManager& notificationManager) : + systemTask{systemTask}, notificationManager{notificationManager}{ + +} + +bool AlertNotificationClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service) { + if(service == nullptr && error->status == BLE_HS_EDONE) { + NRF_LOG_INFO("ANS Discovery complete"); + return true; + } + + if(service != nullptr && ble_uuid_cmp(((ble_uuid_t*)&ansServiceUuid), &service->uuid.u) == 0) { + NRF_LOG_INFO("ANS discovered : 0x%x", service->start_handle); + ansStartHandle = service->start_handle; + ansEndHandle = service->end_handle; + isDiscovered = true; + } + return false; +} + +void AlertNotificationClient::Init() { + +} + +int AlertNotificationClient::OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, + const ble_gatt_chr *characteristic) { + if(error->status != 0 && error->status != BLE_HS_EDONE) { + NRF_LOG_INFO("ANS Characteristic discovery ERROR"); + return 0; + } + + if(characteristic == nullptr && error->status == BLE_HS_EDONE) { + NRF_LOG_INFO("ANS Characteristic discovery complete"); + } else { + if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&supportedNewAlertCategoryUuid), &characteristic->uuid.u) == 0) { + NRF_LOG_INFO("ANS Characteristic discovered : supportedNewAlertCategoryUuid"); + supportedNewAlertCategoryHandle = characteristic->val_handle; + } else if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&supportedUnreadAlertCategoryUuid), &characteristic->uuid.u) == 0) { + NRF_LOG_INFO("ANS Characteristic discovered : supportedUnreadAlertCategoryUuid"); + supportedUnreadAlertCategoryHandle = characteristic->val_handle; + } else if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&newAlertUuid), &characteristic->uuid.u) == 0) { + NRF_LOG_INFO("ANS Characteristic discovered : newAlertUuid"); + newAlertHandle = characteristic->val_handle; + newAlertDefHandle = characteristic->def_handle; + } else if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&unreadAlertStatusUuid), &characteristic->uuid.u) == 0) { + NRF_LOG_INFO("ANS Characteristic discovered : unreadAlertStatusUuid"); + unreadAlertStatusHandle = characteristic->val_handle; + } else if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)&controlPointUuid), &characteristic->uuid.u) == 0) { + NRF_LOG_INFO("ANS Characteristic discovered : controlPointUuid"); + controlPointHandle = characteristic->val_handle; + }else + NRF_LOG_INFO("ANS Characteristic discovered : 0x%x", characteristic->val_handle); + } + return 0; +} + +int AlertNotificationClient::OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error *error, + ble_gatt_attr *attribute) { + if(error->status == 0) { + NRF_LOG_INFO("ANS New alert subscribe OK"); + } else { + NRF_LOG_INFO("ANS New alert subscribe ERROR"); + } + + return 0; +} + +int AlertNotificationClient::OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error, + uint16_t characteristicValueHandle, + const ble_gatt_dsc *descriptor) { + if(error->status == 0) { + if(characteristicValueHandle == newAlertHandle && ble_uuid_cmp(((ble_uuid_t*)&newAlertUuid), &descriptor->uuid.u)) { + if(newAlertDescriptorHandle == 0) { + NRF_LOG_INFO("ANS Descriptor discovered : %d", descriptor->handle); + newAlertDescriptorHandle = descriptor->handle; + uint8_t value[2]; + value[0] = 1; + value[1] = 0; + ble_gattc_write_flat(connectionHandle, newAlertDescriptorHandle, value, sizeof(value), NewAlertSubcribeCallback, this); + } + } + } + return 0; +} + +void AlertNotificationClient::OnNotification(ble_gap_event *event) { + if(event->notify_rx.attr_handle == newAlertHandle) { + size_t notifSize = OS_MBUF_PKTLEN(event->notify_rx.om); + uint8_t data[notifSize + 1]; + data[notifSize] = '\0'; + os_mbuf_copydata(event->notify_rx.om, 0, notifSize, data); + char *s = (char *) &data[2]; + NRF_LOG_INFO("DATA : %s", s); + + notificationManager.Push(Pinetime::Controllers::NotificationManager::Categories::SimpleAlert, s, notifSize + 1); + systemTask.PushMessage(Pinetime::System::SystemTask::Messages::OnNewNotification); + } +} + +bool AlertNotificationClient::IsDiscovered() const { + return isDiscovered; +} + +uint16_t AlertNotificationClient::StartHandle() const { + return ansStartHandle; +} + +uint16_t AlertNotificationClient::EndHandle() const { + return ansEndHandle; +} + +uint16_t AlertNotificationClient::NewAlerthandle() const { + return newAlertHandle; +} diff --git a/src/Components/Ble/AlertNotificationClient.h b/src/Components/Ble/AlertNotificationClient.h new file mode 100644 index 00000000..7a085b7e --- /dev/null +++ b/src/Components/Ble/AlertNotificationClient.h @@ -0,0 +1,82 @@ +#pragma once + +#include +#include +#include + + +namespace Pinetime { + namespace Controllers { + int NewAlertSubcribeCallback(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg); + + class AlertNotificationClient { + public: + explicit AlertNotificationClient(Pinetime::System::SystemTask &systemTask, + Pinetime::Controllers::NotificationManager ¬ificationManager); + void Init(); + + bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service); + int OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, + const ble_gatt_chr *characteristic); + int OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error *error, ble_gatt_attr *attribute); + int OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error, + uint16_t characteristicValueHandle, const ble_gatt_dsc *descriptor); + void OnNotification(ble_gap_event *event); + bool IsDiscovered() const; + uint16_t StartHandle() const; + uint16_t EndHandle() const; + + static constexpr const ble_uuid16_t &Uuid() { return ansServiceUuid; } + + uint16_t NewAlerthandle() const; + private: + static constexpr uint16_t ansServiceId{0x1811}; + static constexpr uint16_t supportedNewAlertCategoryId = 0x2a47; + static constexpr uint16_t supportedUnreadAlertCategoryId = 0x2a48; + static constexpr uint16_t newAlertId = 0x2a46; + static constexpr uint16_t unreadAlertStatusId = 0x2a45; + static constexpr uint16_t controlPointId = 0x2a44; + + static constexpr ble_uuid16_t ansServiceUuid{ + .u {.type = BLE_UUID_TYPE_16}, + .value = ansServiceId + }; + static constexpr ble_uuid16_t supportedNewAlertCategoryUuid{ + .u {.type = BLE_UUID_TYPE_16}, + .value = supportedNewAlertCategoryId + }; + static constexpr ble_uuid16_t supportedUnreadAlertCategoryUuid{ + .u {.type = BLE_UUID_TYPE_16}, + .value = supportedUnreadAlertCategoryId + }; + static constexpr ble_uuid16_t newAlertUuid{ + .u {.type = BLE_UUID_TYPE_16}, + .value = newAlertId + }; + static constexpr ble_uuid16_t unreadAlertStatusUuid{ + .u {.type = BLE_UUID_TYPE_16}, + .value = unreadAlertStatusId + }; + static constexpr ble_uuid16_t controlPointUuid{ + .u {.type = BLE_UUID_TYPE_16}, + .value = controlPointId + }; + + uint16_t ansStartHandle; + uint16_t ansEndHandle; + uint16_t supportedNewAlertCategoryHandle; + uint16_t supportedUnreadAlertCategoryHandle; + uint16_t newAlertHandle; + uint16_t newAlertDescriptorHandle = 0; + uint16_t newAlertDefHandle; + uint16_t unreadAlertStatusHandle; + uint16_t controlPointHandle; + bool isDiscovered = false; + Pinetime::System::SystemTask &systemTask; + Pinetime::Controllers::NotificationManager ¬ificationManager; + }; + } +} \ No newline at end of file diff --git a/src/Components/Ble/AlertNotificationService.cpp b/src/Components/Ble/AlertNotificationService.cpp new file mode 100644 index 00000000..8e3b712d --- /dev/null +++ b/src/Components/Ble/AlertNotificationService.cpp @@ -0,0 +1,73 @@ + +#include +#include "NotificationManager.h" +#include + +#include "AlertNotificationService.h" + +using namespace Pinetime::Controllers; + +constexpr ble_uuid16_t AlertNotificationService::ansUuid; +constexpr ble_uuid16_t AlertNotificationService::ansCharUuid; + + +int AlertNotificationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) { + auto anService = static_cast(arg); + return anService->OnAlert(conn_handle, attr_handle, ctxt); +} + +void AlertNotificationService::Init() { + ble_gatts_count_cfg(serviceDefinition); + ble_gatts_add_svcs(serviceDefinition); +} + +AlertNotificationService::AlertNotificationService ( Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::NotificationManager& notificationManager ) : m_systemTask{systemTask}, m_notificationManager{notificationManager}, + characteristicDefinition{ + { + .uuid = (ble_uuid_t *) &ansCharUuid, + .access_cb = AlertNotificationCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_WRITE + }, + { + 0 + } + }, + serviceDefinition{ + { + /* Device Information Service */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = (ble_uuid_t *) &ansUuid, + .characteristics = characteristicDefinition + }, + { + 0 + }, + } +{ +} + +int AlertNotificationService::OnAlert(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt) { + + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + size_t notifSize = OS_MBUF_PKTLEN(ctxt->om); + uint8_t data[notifSize + 1]; + data[notifSize] = '\0'; + os_mbuf_copydata(ctxt->om, 0, notifSize, data); + char *s = (char *) &data[3]; + NRF_LOG_INFO("DATA : %s", s); + + for(int i = 0; i <= notifSize; i++) + { + if(s[i] == 0x00) + { + s[i] = 0x0A; + } + } + + m_notificationManager.Push(Pinetime::Controllers::NotificationManager::Categories::SimpleAlert, s, notifSize + 1); + m_systemTask.PushMessage(Pinetime::System::SystemTask::Messages::OnNewNotification); + } + return 0; +} diff --git a/src/Components/Ble/AlertNotificationService.h b/src/Components/Ble/AlertNotificationService.h new file mode 100644 index 00000000..53cb44cc --- /dev/null +++ b/src/Components/Ble/AlertNotificationService.h @@ -0,0 +1,39 @@ +#pragma once +#include +#include +#include + +namespace Pinetime { + namespace Controllers { + class AlertNotificationService { + public: + AlertNotificationService(Pinetime::System::SystemTask &systemTask, + Pinetime::Controllers::NotificationManager ¬ificationManager); + void Init(); + + int OnAlert(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt); + + + private: + static constexpr uint16_t ansId {0x1811}; + static constexpr uint16_t ansCharId {0x2a46}; + + static constexpr ble_uuid16_t ansUuid { + .u { .type = BLE_UUID_TYPE_16 }, + .value = ansId + }; + + static constexpr ble_uuid16_t ansCharUuid { + .u { .type = BLE_UUID_TYPE_16 }, + .value = ansCharId + }; + + struct ble_gatt_chr_def characteristicDefinition[2]; + struct ble_gatt_svc_def serviceDefinition[2]; + + Pinetime::System::SystemTask &m_systemTask; + NotificationManager &m_notificationManager; + }; + } +} diff --git a/src/Components/Ble/BleController.h b/src/Components/Ble/BleController.h index 31d66986..f2bd77e0 100644 --- a/src/Components/Ble/BleController.h +++ b/src/Components/Ble/BleController.h @@ -1,6 +1,6 @@ #pragma once -#include > +#include #include namespace Pinetime { diff --git a/src/Components/Ble/CurrentTimeClient.cpp b/src/Components/Ble/CurrentTimeClient.cpp new file mode 100644 index 00000000..fdebc084 --- /dev/null +++ b/src/Components/Ble/CurrentTimeClient.cpp @@ -0,0 +1,77 @@ +#include +#include "CurrentTimeClient.h" + +using namespace Pinetime::Controllers; + +constexpr ble_uuid16_t CurrentTimeClient::ctsServiceUuid; +constexpr ble_uuid16_t CurrentTimeClient::currentTimeCharacteristicUuid; + +CurrentTimeClient::CurrentTimeClient(DateTime& dateTimeController) : dateTimeController{dateTimeController} { + +} + +void CurrentTimeClient::Init() { + +} + +bool CurrentTimeClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service) { + if(service == nullptr && error->status == BLE_HS_EDONE) { + NRF_LOG_INFO("CTS Discovery complete"); + return true; + } + + if(service != nullptr && ble_uuid_cmp(((ble_uuid_t*)&ctsServiceUuid), &service->uuid.u) == 0) { + NRF_LOG_INFO("CTS discovered : 0x%x", service->start_handle); + isDiscovered = true; + ctsStartHandle = service->start_handle; + ctsEndHandle = service->end_handle; + return false; + } + return false; +} + +int CurrentTimeClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_error *error, + const ble_gatt_chr *characteristic) { + if(characteristic == nullptr && error->status == BLE_HS_EDONE) { + NRF_LOG_INFO("CTS Characteristic discovery complete"); + return 0; + } + + if(characteristic != nullptr && ble_uuid_cmp(((ble_uuid_t*)¤tTimeCharacteristicUuid), &characteristic->uuid.u) == 0) { + NRF_LOG_INFO("CTS Characteristic discovered : 0x%x", characteristic->val_handle); + currentTimeHandle = characteristic->val_handle; + } + return 0; +} + +int CurrentTimeClient::OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error *error, const ble_gatt_attr *attribute) { + if(error->status == 0) { + // TODO check that attribute->handle equals the handle discovered in OnCharacteristicDiscoveryEvent + CtsData result; + os_mbuf_copydata(attribute->om, 0, sizeof(CtsData), &result); + NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d", result.year, + result.month, result.dayofmonth, + result.hour, result.minute, result.second); + dateTimeController.SetTime(result.year, result.month, result.dayofmonth, + 0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG)); + } else { + NRF_LOG_INFO("Error retrieving current time: %d", error->status); + } + return 0; +} + +bool CurrentTimeClient::IsDiscovered() const { + return isDiscovered; +} + +uint16_t CurrentTimeClient::StartHandle() const { + return ctsStartHandle; +} + +uint16_t CurrentTimeClient::EndHandle() const { + return ctsEndHandle; +} + +uint16_t CurrentTimeClient::CurrentTimeHandle() const { + return currentTimeHandle; +} diff --git a/src/Components/Ble/CurrentTimeClient.h b/src/Components/Ble/CurrentTimeClient.h new file mode 100644 index 00000000..2278ef15 --- /dev/null +++ b/src/Components/Ble/CurrentTimeClient.h @@ -0,0 +1,55 @@ +#pragma once +#include +#include +#include +#include + +namespace Pinetime { + namespace Controllers { + + class CurrentTimeClient { + public: + explicit CurrentTimeClient(DateTime& dateTimeController); + void Init(); + bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, const ble_gatt_svc *service); + int OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_error *error, + const ble_gatt_chr *characteristic); + int OnCurrentTimeReadResult(uint16_t conn_handle, const ble_gatt_error *error, const ble_gatt_attr *attribute); + bool IsDiscovered() const; + uint16_t StartHandle() const; + uint16_t EndHandle() const; + uint16_t CurrentTimeHandle() const; + static constexpr const ble_uuid16_t* Uuid() { return &CurrentTimeClient::ctsServiceUuid; } + static constexpr const ble_uuid16_t* CurrentTimeCharacteristicUuid() { return &CurrentTimeClient::currentTimeCharacteristicUuid; } + private: + typedef struct __attribute__((packed)) { + uint16_t year; + uint8_t month; + uint8_t dayofmonth; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint8_t millis; + uint8_t reason; + } CtsData; + + static constexpr uint16_t ctsServiceId {0x1805}; + static constexpr uint16_t currentTimeCharacteristicId {0x2a2b}; + + static constexpr ble_uuid16_t ctsServiceUuid { + .u { .type = BLE_UUID_TYPE_16 }, + .value = ctsServiceId + }; + static constexpr ble_uuid16_t currentTimeCharacteristicUuid { + .u { .type = BLE_UUID_TYPE_16 }, + .value = currentTimeCharacteristicId + }; + + uint16_t currentTimeHandle; + DateTime& dateTimeController; + bool isDiscovered = false; + uint16_t ctsStartHandle; + uint16_t ctsEndHandle; + }; + } +} \ No newline at end of file diff --git a/src/Components/Ble/CurrentTimeService.cpp b/src/Components/Ble/CurrentTimeService.cpp new file mode 100644 index 00000000..9ae4c3b8 --- /dev/null +++ b/src/Components/Ble/CurrentTimeService.cpp @@ -0,0 +1,83 @@ +#include "CurrentTimeService.h" +#include + +using namespace Pinetime::Controllers; + +constexpr ble_uuid16_t CurrentTimeService::ctsUuid; +constexpr ble_uuid16_t CurrentTimeService::ctChrUuid; + + +int CTSCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) { + auto cts = static_cast(arg); + return cts->OnTimeAccessed(conn_handle, attr_handle, ctxt); +} + +void CurrentTimeService::Init() { + ble_gatts_count_cfg(serviceDefinition); + ble_gatts_add_svcs(serviceDefinition); +} + + +int CurrentTimeService::OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt) { + + NRF_LOG_INFO("Setting time..."); + + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + CtsData result; + os_mbuf_copydata(ctxt->om, 0, sizeof(CtsData), &result); + + NRF_LOG_INFO("Received data: %d-%d-%d %d:%d:%d", result.year, + result.month, result.dayofmonth, + result.hour, result.minute, result.second); + + m_dateTimeController.SetTime(result.year, result.month, result.dayofmonth, + 0, result.hour, result.minute, result.second, nrf_rtc_counter_get(portNRF_RTC_REG)); + + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + CtsData currentDateTime; + currentDateTime.year = m_dateTimeController.Year(); + currentDateTime.month = static_cast(m_dateTimeController.Month()); + currentDateTime.dayofmonth = m_dateTimeController.Day(); + currentDateTime.hour = m_dateTimeController.Hours(); + currentDateTime.minute = m_dateTimeController.Minutes(); + currentDateTime.second = m_dateTimeController.Seconds(); + currentDateTime.millis = 0; + + + int res = os_mbuf_append(ctxt->om, ¤tDateTime, sizeof(CtsData)); + return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + + } + + return 0; +} + +CurrentTimeService::CurrentTimeService(DateTime &dateTimeController) : m_dateTimeController{dateTimeController}, + characteristicDefinition{ + { + .uuid = (ble_uuid_t *) &ctChrUuid, + .access_cb = CTSCallback, + + .arg = this, + .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ + }, + { + 0 + } + }, + serviceDefinition{ + { + /* Device Information Service */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = (ble_uuid_t *) &ctsUuid, + .characteristics = characteristicDefinition + }, + { + 0 + }, + } + { + +} + diff --git a/src/Components/Ble/CurrentTimeService.h b/src/Components/Ble/CurrentTimeService.h new file mode 100644 index 00000000..58bc5ba6 --- /dev/null +++ b/src/Components/Ble/CurrentTimeService.h @@ -0,0 +1,48 @@ +#pragma once +#include +#include +#include +#include + +namespace Pinetime { + namespace Controllers { + class CurrentTimeService { + public: + CurrentTimeService(DateTime &dateTimeController); + void Init(); + + int OnTimeAccessed(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt); + + private: + static constexpr uint16_t ctsId {0x1805}; + static constexpr uint16_t ctsCharId {0x2a2b}; + + static constexpr ble_uuid16_t ctsUuid { + .u { .type = BLE_UUID_TYPE_16 }, + .value = ctsId + }; + + static constexpr ble_uuid16_t ctChrUuid { + .u { .type = BLE_UUID_TYPE_16 }, + .value = ctsCharId + }; + + struct ble_gatt_chr_def characteristicDefinition[2]; + struct ble_gatt_svc_def serviceDefinition[2]; + + typedef struct __attribute__((packed)) { + uint16_t year; + uint8_t month; + uint8_t dayofmonth; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint8_t millis; + uint8_t reason; + } CtsData; + + DateTime &m_dateTimeController; + }; + } +} diff --git a/src/Components/Ble/DeviceInformationService.cpp b/src/Components/Ble/DeviceInformationService.cpp new file mode 100644 index 00000000..3099b1bf --- /dev/null +++ b/src/Components/Ble/DeviceInformationService.cpp @@ -0,0 +1,101 @@ +#include "DeviceInformationService.h" + +using namespace Pinetime::Controllers; + +constexpr ble_uuid16_t DeviceInformationService::manufacturerNameUuid; +constexpr ble_uuid16_t DeviceInformationService::modelNumberUuid; +constexpr ble_uuid16_t DeviceInformationService::serialNumberUuid; +constexpr ble_uuid16_t DeviceInformationService::fwRevisionUuid; +constexpr ble_uuid16_t DeviceInformationService::deviceInfoUuid; +constexpr ble_uuid16_t DeviceInformationService::hwRevisionUuid; + +int DeviceInformationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) { + auto deviceInformationService = static_cast(arg); + return deviceInformationService->OnDeviceInfoRequested(conn_handle, attr_handle, ctxt); +} + +void DeviceInformationService::Init() { + ble_gatts_count_cfg(serviceDefinition); + ble_gatts_add_svcs(serviceDefinition); +} + + +int DeviceInformationService::OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt) { + const char *str; + + switch (ble_uuid_u16(ctxt->chr->uuid)) { + case manufacturerNameId: + str = manufacturerName; + break; + case modelNumberId: + str = modelNumber; + break; + case serialNumberId: + str = serialNumber; + break; + case fwRevisionId: + str = fwRevision; + break; + case hwRevisionId: + str = hwRevision; + break; + default: + return BLE_ATT_ERR_UNLIKELY; + } + + int res = os_mbuf_append(ctxt->om, str, strlen(str)); + return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; +} + +DeviceInformationService::DeviceInformationService() : + characteristicDefinition{ + { + .uuid = (ble_uuid_t *) &manufacturerNameUuid, + .access_cb = DeviceInformationCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_READ, + }, + { + .uuid = (ble_uuid_t *) &modelNumberUuid, + .access_cb = DeviceInformationCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_READ, + }, + { + .uuid = (ble_uuid_t *) &serialNumberUuid, + .access_cb = DeviceInformationCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_READ, + }, + { + .uuid = (ble_uuid_t *) &fwRevisionUuid, + .access_cb = DeviceInformationCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_READ, + }, + { + .uuid = (ble_uuid_t *) &hwRevisionUuid, + .access_cb = DeviceInformationCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_READ, + }, + { + 0 + } + }, + serviceDefinition{ + { + /* Device Information Service */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = (ble_uuid_t *) &deviceInfoUuid, + .characteristics = characteristicDefinition + }, + { + 0 + }, + } + { + +} + diff --git a/src/Components/Ble/DeviceInformationService.h b/src/Components/Ble/DeviceInformationService.h new file mode 100644 index 00000000..6249893d --- /dev/null +++ b/src/Components/Ble/DeviceInformationService.h @@ -0,0 +1,67 @@ +#pragma once +#include +#include + +#include + +namespace Pinetime { + namespace Controllers { + class DeviceInformationService { + public: + DeviceInformationService(); + void Init(); + + int OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt); + + private: + static constexpr uint16_t deviceInfoId {0x180a}; + static constexpr uint16_t manufacturerNameId {0x2a29}; + static constexpr uint16_t modelNumberId {0x2a24}; + static constexpr uint16_t serialNumberId {0x2a25}; + static constexpr uint16_t fwRevisionId {0x2a26}; + static constexpr uint16_t hwRevisionId {0x2a27}; + + static constexpr char* manufacturerName = "Codingfield"; + static constexpr char* modelNumber = "1"; + static constexpr char* serialNumber = "9.8.7.6.5.4"; + static constexpr char* fwRevision = "0.5.0"; + static constexpr char* hwRevision = "1.0.0"; + + static constexpr ble_uuid16_t deviceInfoUuid { + .u { .type = BLE_UUID_TYPE_16 }, + .value = deviceInfoId + }; + + static constexpr ble_uuid16_t manufacturerNameUuid { + .u { .type = BLE_UUID_TYPE_16 }, + .value = manufacturerNameId + }; + + static constexpr ble_uuid16_t modelNumberUuid { + .u { .type = BLE_UUID_TYPE_16 }, + .value = modelNumberId + }; + + static constexpr ble_uuid16_t serialNumberUuid { + .u { .type = BLE_UUID_TYPE_16 }, + .value = serialNumberId + }; + + static constexpr ble_uuid16_t fwRevisionUuid { + .u { .type = BLE_UUID_TYPE_16 }, + .value = fwRevisionId + }; + + static constexpr ble_uuid16_t hwRevisionUuid { + .u {.type = BLE_UUID_TYPE_16}, + .value = hwRevisionId + }; + + struct ble_gatt_chr_def characteristicDefinition[6]; + struct ble_gatt_svc_def serviceDefinition[2]; + + + }; + } +} \ No newline at end of file diff --git a/src/Components/Ble/DfuService.cpp b/src/Components/Ble/DfuService.cpp new file mode 100644 index 00000000..3099b1bf --- /dev/null +++ b/src/Components/Ble/DfuService.cpp @@ -0,0 +1,101 @@ +#include "DeviceInformationService.h" + +using namespace Pinetime::Controllers; + +constexpr ble_uuid16_t DeviceInformationService::manufacturerNameUuid; +constexpr ble_uuid16_t DeviceInformationService::modelNumberUuid; +constexpr ble_uuid16_t DeviceInformationService::serialNumberUuid; +constexpr ble_uuid16_t DeviceInformationService::fwRevisionUuid; +constexpr ble_uuid16_t DeviceInformationService::deviceInfoUuid; +constexpr ble_uuid16_t DeviceInformationService::hwRevisionUuid; + +int DeviceInformationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) { + auto deviceInformationService = static_cast(arg); + return deviceInformationService->OnDeviceInfoRequested(conn_handle, attr_handle, ctxt); +} + +void DeviceInformationService::Init() { + ble_gatts_count_cfg(serviceDefinition); + ble_gatts_add_svcs(serviceDefinition); +} + + +int DeviceInformationService::OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt) { + const char *str; + + switch (ble_uuid_u16(ctxt->chr->uuid)) { + case manufacturerNameId: + str = manufacturerName; + break; + case modelNumberId: + str = modelNumber; + break; + case serialNumberId: + str = serialNumber; + break; + case fwRevisionId: + str = fwRevision; + break; + case hwRevisionId: + str = hwRevision; + break; + default: + return BLE_ATT_ERR_UNLIKELY; + } + + int res = os_mbuf_append(ctxt->om, str, strlen(str)); + return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; +} + +DeviceInformationService::DeviceInformationService() : + characteristicDefinition{ + { + .uuid = (ble_uuid_t *) &manufacturerNameUuid, + .access_cb = DeviceInformationCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_READ, + }, + { + .uuid = (ble_uuid_t *) &modelNumberUuid, + .access_cb = DeviceInformationCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_READ, + }, + { + .uuid = (ble_uuid_t *) &serialNumberUuid, + .access_cb = DeviceInformationCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_READ, + }, + { + .uuid = (ble_uuid_t *) &fwRevisionUuid, + .access_cb = DeviceInformationCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_READ, + }, + { + .uuid = (ble_uuid_t *) &hwRevisionUuid, + .access_cb = DeviceInformationCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_READ, + }, + { + 0 + } + }, + serviceDefinition{ + { + /* Device Information Service */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = (ble_uuid_t *) &deviceInfoUuid, + .characteristics = characteristicDefinition + }, + { + 0 + }, + } + { + +} + diff --git a/src/Components/Ble/DfuService.h b/src/Components/Ble/DfuService.h new file mode 100644 index 00000000..6249893d --- /dev/null +++ b/src/Components/Ble/DfuService.h @@ -0,0 +1,67 @@ +#pragma once +#include +#include + +#include + +namespace Pinetime { + namespace Controllers { + class DeviceInformationService { + public: + DeviceInformationService(); + void Init(); + + int OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt); + + private: + static constexpr uint16_t deviceInfoId {0x180a}; + static constexpr uint16_t manufacturerNameId {0x2a29}; + static constexpr uint16_t modelNumberId {0x2a24}; + static constexpr uint16_t serialNumberId {0x2a25}; + static constexpr uint16_t fwRevisionId {0x2a26}; + static constexpr uint16_t hwRevisionId {0x2a27}; + + static constexpr char* manufacturerName = "Codingfield"; + static constexpr char* modelNumber = "1"; + static constexpr char* serialNumber = "9.8.7.6.5.4"; + static constexpr char* fwRevision = "0.5.0"; + static constexpr char* hwRevision = "1.0.0"; + + static constexpr ble_uuid16_t deviceInfoUuid { + .u { .type = BLE_UUID_TYPE_16 }, + .value = deviceInfoId + }; + + static constexpr ble_uuid16_t manufacturerNameUuid { + .u { .type = BLE_UUID_TYPE_16 }, + .value = manufacturerNameId + }; + + static constexpr ble_uuid16_t modelNumberUuid { + .u { .type = BLE_UUID_TYPE_16 }, + .value = modelNumberId + }; + + static constexpr ble_uuid16_t serialNumberUuid { + .u { .type = BLE_UUID_TYPE_16 }, + .value = serialNumberId + }; + + static constexpr ble_uuid16_t fwRevisionUuid { + .u { .type = BLE_UUID_TYPE_16 }, + .value = fwRevisionId + }; + + static constexpr ble_uuid16_t hwRevisionUuid { + .u {.type = BLE_UUID_TYPE_16}, + .value = hwRevisionId + }; + + struct ble_gatt_chr_def characteristicDefinition[6]; + struct ble_gatt_svc_def serviceDefinition[2]; + + + }; + } +} \ No newline at end of file diff --git a/src/Components/Ble/NimbleController.cpp b/src/Components/Ble/NimbleController.cpp new file mode 100644 index 00000000..2fe03571 --- /dev/null +++ b/src/Components/Ble/NimbleController.cpp @@ -0,0 +1,316 @@ + +#include + +#include +#include +#include + +#include "NimbleController.h" +#include +#include +#include +#include +#include +#include + + + +using namespace Pinetime::Controllers; + +// TODO I'm not satisfied by how this code looks like (AlertNotificationClient and CurrentTimeClient must +// expose too much data, too many callbacks -> NimbleController -> CTS/ANS client. +// Let's try to improve this code (and keep it working!) + +NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask, + Pinetime::Controllers::Ble& bleController, + DateTime& dateTimeController, + Pinetime::Controllers::NotificationManager& notificationManager) : + systemTask{systemTask}, + bleController{bleController}, + dateTimeController{dateTimeController}, + notificationManager{notificationManager}, + currentTimeClient{dateTimeController}, + alertNotificationClient{systemTask, notificationManager}, + anService{systemTask, notificationManager}, + currentTimeService{dateTimeController} { + +} + +int GAPEventCallback(struct ble_gap_event *event, void *arg) { + auto nimbleController = static_cast(arg); + return nimbleController->OnGAPEvent(event); +} + +int CurrentTimeCharacteristicDiscoveredCallback(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_chr *chr, void *arg) { + auto client = static_cast(arg); + return client->OnCTSCharacteristicDiscoveryEvent(conn_handle, error, chr); +} + +int AlertNotificationCharacteristicDiscoveredCallback(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_chr *chr, void *arg) { + auto client = static_cast(arg); + return client->OnANSCharacteristicDiscoveryEvent(conn_handle, error, chr); +} + +int CurrentTimeReadCallback(uint16_t conn_handle, const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg) { + auto client = static_cast(arg); + return client->OnCurrentTimeReadResult(conn_handle, error, attr); +} + +int AlertNotificationDescriptorDiscoveryEventCallback(uint16_t conn_handle, + const struct ble_gatt_error *error, + uint16_t chr_val_handle, + const struct ble_gatt_dsc *dsc, + void *arg) { + auto client = static_cast(arg); + return client->OnANSDescriptorDiscoveryEventCallback(conn_handle, error, chr_val_handle, dsc); +} + +void NimbleController::Init() { + while (!ble_hs_synced()) {} + + ble_svc_gap_init(); + ble_svc_gatt_init(); + + deviceInformationService.Init(); + currentTimeClient.Init(); + currentTimeService.Init(); + + anService.Init(); + + int res; + res = ble_hs_util_ensure_addr(0); + ASSERT(res == 0); + res = ble_hs_id_infer_auto(0, &addrType); + ASSERT(res == 0); + res = ble_svc_gap_device_name_set(deviceName); + + ASSERT(res == 0); + res = ble_gatts_start(); + ASSERT(res == 0); +} + +void NimbleController::StartAdvertising() { + ble_svc_gap_device_name_set("Pinetime-JF"); + + /* set adv parameters */ + struct ble_gap_adv_params adv_params; + struct ble_hs_adv_fields fields; + /* advertising payload is split into advertising data and advertising + response, because all data cannot fit into single packet; name of device + is sent as response to scan request */ + struct ble_hs_adv_fields rsp_fields; + + /* fill all fields and parameters with zeros */ + memset(&adv_params, 0, sizeof(adv_params)); + memset(&fields, 0, sizeof(fields)); + memset(&rsp_fields, 0, sizeof(rsp_fields)); + + adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; + adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + + fields.flags = BLE_HS_ADV_F_DISC_GEN | + BLE_HS_ADV_F_BREDR_UNSUP; +// fields.uuids128 = BLE_UUID128(BLE_UUID128_DECLARE( +// 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, +// 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff)); + fields.num_uuids128 = 0; + fields.uuids128_is_complete = 0;; + fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + + rsp_fields.name = (uint8_t *)"Pinetime-JF"; + rsp_fields.name_len = strlen("Pinetime-JF"); + rsp_fields.name_is_complete = 1; + + int res; + res = ble_gap_adv_set_fields(&fields); + //ASSERT(res == 0); + + res = ble_gap_adv_rsp_set_fields(&rsp_fields); + //ASSERT(res == 0); + + res = ble_gap_adv_start(addrType, NULL, 10000, + &adv_params, GAPEventCallback, this); + //ASSERT(res == 0); + + // TODO I've disabled these ASSERT as they sometime asserts and reset the mcu. + // For now, the advertising is restarted as soon as it ends. There may be a race condition + // that prevent the advertising from restarting reliably. + // I remove the assert to prevent this uncesseray crash, but in the long term, the management of + // the advertising should be improve (better error handling, and advertise for 3 minutes after + // the application has been woken up, for example. +} + +int OnAllSvrDisco(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_svc *service, + void *arg) { + auto nimbleController = static_cast(arg); + return nimbleController->OnDiscoveryEvent(conn_handle, error, service); + return 0; +} + +int NimbleController::OnGAPEvent(ble_gap_event *event) { + switch (event->type) { + case BLE_GAP_EVENT_ADV_COMPLETE: + NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_ADV_COMPLETE"); + NRF_LOG_INFO("advertise complete; reason=%dn status=%d", event->adv_complete.reason, event->connect.status); + StartAdvertising(); + break; + case BLE_GAP_EVENT_CONNECT: { + NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_CONNECT"); + + /* A new connection was established or a connection attempt failed. */ + NRF_LOG_INFO("connection %s; status=%d ", event->connect.status == 0 ? "established" : "failed", + event->connect.status); + + if (event->connect.status != 0) { + /* Connection failed; resume advertising. */ + StartAdvertising(); + bleController.Disconnect(); + } else { + bleController.Connect(); + connectionHandle = event->connect.conn_handle; + ble_gattc_disc_all_svcs(connectionHandle, OnAllSvrDisco, this); + } + } + break; + case BLE_GAP_EVENT_DISCONNECT: + NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_DISCONNECT"); + NRF_LOG_INFO("disconnect; reason=%d", event->disconnect.reason); + + /* Connection terminated; resume advertising. */ + bleController.Disconnect(); + StartAdvertising(); + break; + case BLE_GAP_EVENT_CONN_UPDATE: + NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_CONN_UPDATE"); + /* The central has updated the connection parameters. */ + NRF_LOG_INFO("connection updated; status=%d ", event->conn_update.status); + break; + case BLE_GAP_EVENT_ENC_CHANGE: + /* Encryption has been enabled or disabled for this connection. */ + NRF_LOG_INFO("encryption change event; status=%d ", event->enc_change.status); + return 0; + case BLE_GAP_EVENT_SUBSCRIBE: + NRF_LOG_INFO("subscribe event; conn_handle=%d attr_handle=%d " + "reason=%d prevn=%d curn=%d previ=%d curi=???\n", + event->subscribe.conn_handle, + event->subscribe.attr_handle, + event->subscribe.reason, + event->subscribe.prev_notify, + event->subscribe.cur_notify, + event->subscribe.prev_indicate); + return 0; + case BLE_GAP_EVENT_MTU: + NRF_LOG_INFO("mtu update event; conn_handle=%d cid=%d mtu=%d\n", + event->mtu.conn_handle, + event->mtu.channel_id, + event->mtu.value); + return 0; + + case BLE_GAP_EVENT_REPEAT_PAIRING: { + /* We already have a bond with the peer, but it is attempting to + * establish a new secure link. This app sacrifices security for + * convenience: just throw away the old bond and accept the new link. + */ + + /* Delete the old bond. */ + struct ble_gap_conn_desc desc; + ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc); + ble_store_util_delete_peer(&desc.peer_id_addr); + + /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should + * continue with the pairing operation. + */ + } + return BLE_GAP_REPEAT_PAIRING_RETRY; + + case BLE_GAP_EVENT_NOTIFY_RX: { + /* Peer sent us a notification or indication. */ + size_t notifSize = OS_MBUF_PKTLEN(event->notify_rx.om); + + NRF_LOG_INFO("received %s; conn_handle=%d attr_handle=%d " + "attr_len=%d", + event->notify_rx.indication ? + "indication" : + "notification", + event->notify_rx.conn_handle, + event->notify_rx.attr_handle, + notifSize); + + alertNotificationClient.OnNotification(event); + return 0; + } + /* Attribute data is contained in event->notify_rx.attr_data. */ + + default: + NRF_LOG_INFO("Advertising event : %d", event->type); + break; + } + return 0; +} + +int NimbleController::OnDiscoveryEvent(uint16_t i, const ble_gatt_error *error, const ble_gatt_svc *service) { + if(service == nullptr && error->status == BLE_HS_EDONE) { + NRF_LOG_INFO("Service Discovery complete"); + if(currentTimeClient.IsDiscovered()) { + ble_gattc_disc_all_chrs(connectionHandle, currentTimeClient.StartHandle(), currentTimeClient.EndHandle(), + CurrentTimeCharacteristicDiscoveredCallback, this); + + } else if(alertNotificationClient.IsDiscovered()) { + ble_gattc_disc_all_chrs(connectionHandle, alertNotificationClient.StartHandle(), alertNotificationClient.EndHandle(), + AlertNotificationCharacteristicDiscoveredCallback, this); + } + return 0; + } + + alertNotificationClient.OnDiscoveryEvent(i, error, service); + currentTimeClient.OnDiscoveryEvent(i, error, service); + return 0; +} + +int NimbleController::OnCTSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, + const ble_gatt_chr *characteristic) { + if(characteristic == nullptr && error->status == BLE_HS_EDONE) { + NRF_LOG_INFO("CTS characteristic Discovery complete"); + ble_gattc_read(connectionHandle, currentTimeClient.CurrentTimeHandle(), CurrentTimeReadCallback, this); + return 0; + } + return currentTimeClient.OnCharacteristicDiscoveryEvent(connectionHandle, error, characteristic); +} + +int NimbleController::OnANSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, + const ble_gatt_chr *characteristic) { + if(characteristic == nullptr && error->status == BLE_HS_EDONE) { + NRF_LOG_INFO("ANS characteristic Discovery complete"); + ble_gattc_disc_all_dscs(connectionHandle, + alertNotificationClient.NewAlerthandle(), alertNotificationClient.EndHandle(), + AlertNotificationDescriptorDiscoveryEventCallback, this); + return 0; + } + return alertNotificationClient.OnCharacteristicsDiscoveryEvent(connectionHandle, error, characteristic); +} + +int NimbleController::OnCurrentTimeReadResult(uint16_t connectionHandle, const ble_gatt_error *error, ble_gatt_attr *attribute) { + currentTimeClient.OnCurrentTimeReadResult(connectionHandle, error, attribute); + + if (alertNotificationClient.IsDiscovered()) { + ble_gattc_disc_all_chrs(connectionHandle, alertNotificationClient.StartHandle(), + alertNotificationClient.EndHandle(), + AlertNotificationCharacteristicDiscoveredCallback, this); + } + return 0; +} + +int NimbleController::OnANSDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error, + uint16_t characteristicValueHandle, + const ble_gatt_dsc *descriptor) { + return alertNotificationClient.OnDescriptorDiscoveryEventCallback(connectionHandle, error, characteristicValueHandle, descriptor); +} + + + + diff --git a/src/Components/Ble/NimbleController.h b/src/Components/Ble/NimbleController.h new file mode 100644 index 00000000..44fbbe2c --- /dev/null +++ b/src/Components/Ble/NimbleController.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include "AlertNotificationService.h" +#include "AlertNotificationClient.h" +#include "DeviceInformationService.h" +#include "CurrentTimeClient.h" +#include "CurrentTimeService.h" +#include + +namespace Pinetime { + namespace Controllers { + class DateTime; + class NimbleController { + + public: + NimbleController(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::Ble& bleController, DateTime& dateTimeController, Pinetime::Controllers::NotificationManager& notificationManager); + void Init(); + void StartAdvertising(); + int OnGAPEvent(ble_gap_event *event); + + int OnDiscoveryEvent(uint16_t i, const ble_gatt_error *pError, const ble_gatt_svc *pSvc); + int OnCTSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, + const ble_gatt_chr *characteristic); + int OnANSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error *error, + const ble_gatt_chr *characteristic); + int OnCurrentTimeReadResult(uint16_t connectionHandle, const ble_gatt_error *error, ble_gatt_attr *attribute); + int OnANSDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error *error, + uint16_t characteristicValueHandle, const ble_gatt_dsc *descriptor); + private: + static constexpr char* deviceName = "Pinetime-JF"; + Pinetime::System::SystemTask& systemTask; + Pinetime::Controllers::Ble& bleController; + DateTime& dateTimeController; + Pinetime::Controllers::NotificationManager& notificationManager; + + DeviceInformationService deviceInformationService; + CurrentTimeClient currentTimeClient; + AlertNotificationService anService; + AlertNotificationClient alertNotificationClient; + CurrentTimeService currentTimeService; + + uint8_t addrType; + uint16_t connectionHandle; + }; + } +} diff --git a/src/FreeRTOSConfig.h b/src/FreeRTOSConfig.h index 609c3f2b..557e5edf 100644 --- a/src/FreeRTOSConfig.h +++ b/src/FreeRTOSConfig.h @@ -63,7 +63,7 @@ #define configTICK_RATE_HZ 1024 #define configMAX_PRIORITIES ( 3 ) #define configMINIMAL_STACK_SIZE ( 120 ) -#define configTOTAL_HEAP_SIZE ( 1024*10 ) +#define configTOTAL_HEAP_SIZE ( 1024*20 ) #define configMAX_TASK_NAME_LEN ( 4 ) #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 1 diff --git a/src/SystemTask/SystemTask.cpp b/src/SystemTask/SystemTask.cpp index e65abb61..fc37ecb2 100644 --- a/src/SystemTask/SystemTask.cpp +++ b/src/SystemTask/SystemTask.cpp @@ -3,11 +3,15 @@ #include #include #include -#include -#include #include +#include +#include #include "SystemTask.h" +#include +#include +#include #include "../main.h" + using namespace Pinetime::System; SystemTask::SystemTask(Drivers::SpiMaster &spi, Drivers::St7789 &lcd, Drivers::Cst816S &touchPanel, @@ -17,7 +21,8 @@ SystemTask::SystemTask(Drivers::SpiMaster &spi, Drivers::St7789 &lcd, Drivers::C Pinetime::Controllers::NotificationManager& notificationManager) : spi{spi}, lcd{lcd}, touchPanel{touchPanel}, lvgl{lvgl}, batteryController{batteryController}, bleController{bleController}, dateTimeController{dateTimeController}, - watchdog{}, watchdogView{watchdog}, notificationManager{notificationManager} { + watchdog{}, watchdogView{watchdog}, notificationManager{notificationManager}, + nimbleController(*this, bleController,dateTimeController, notificationManager) { systemTaksMsgQueue = xQueueCreate(10, 1); } @@ -37,9 +42,11 @@ void SystemTask::Work() { watchdog.Start(); NRF_LOG_INFO("Last reset reason : %s", Pinetime::Drivers::Watchdog::ResetReasonToString(watchdog.ResetReason())); APP_GPIOTE_INIT(2); - bool erase_bonds=true; - ble_manager_init_peer_manager(); - nrf_sdh_freertos_init(ble_manager_start_advertising, &erase_bonds); + +/* BLE */ + nimbleController.Init(); + nimbleController.StartAdvertising(); +/* /BLE*/ spi.Init(); lcd.Init(); diff --git a/src/SystemTask/SystemTask.h b/src/SystemTask/SystemTask.h index a1ba277a..5eba391b 100644 --- a/src/SystemTask/SystemTask.h +++ b/src/SystemTask/SystemTask.h @@ -8,6 +8,7 @@ #include #include #include +#include namespace Pinetime { namespace System { @@ -44,6 +45,7 @@ namespace Pinetime { Pinetime::Drivers::Watchdog watchdog; Pinetime::Drivers::WatchdogView watchdogView; Pinetime::Controllers::NotificationManager& notificationManager; + Pinetime::Controllers::NimbleController nimbleController; static constexpr uint8_t pinSpiSck = 2; diff --git a/src/drivers/SpiMaster.cpp b/src/drivers/SpiMaster.cpp index 71986054..4b3dd677 100644 --- a/src/drivers/SpiMaster.cpp +++ b/src/drivers/SpiMaster.cpp @@ -9,7 +9,8 @@ using namespace Pinetime::Drivers; SpiMaster::SpiMaster(const SpiMaster::SpiModule spi, const SpiMaster::Parameters ¶ms) : spi{spi}, params{params} { - + mutex = xSemaphoreCreateBinary(); + ASSERT(mutex != NULL); } bool SpiMaster::Init() { @@ -68,6 +69,8 @@ bool SpiMaster::Init() { NRFX_IRQ_PRIORITY_SET(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn,2); NRFX_IRQ_ENABLE(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn); + + xSemaphoreGive(mutex); return true; } @@ -82,6 +85,7 @@ void SpiMaster::SetupWorkaroundForFtpan58(NRF_SPIM_Type *spim, uint32_t ppi_chan NRF_PPI->CH[ppi_channel].EEP = (uint32_t) &NRF_GPIOTE->EVENTS_IN[gpiote_channel]; NRF_PPI->CH[ppi_channel].TEP = (uint32_t) &spim->TASKS_STOP; NRF_PPI->CHENSET = 1U << ppi_channel; + spiBaseAddress->EVENTS_END = 0; // Disable IRQ spim->INTENCLR = (1<<6); @@ -94,13 +98,16 @@ void SpiMaster::DisableWorkaroundForFtpan58(NRF_SPIM_Type *spim, uint32_t ppi_ch NRF_PPI->CH[ppi_channel].EEP = 0; NRF_PPI->CH[ppi_channel].TEP = 0; NRF_PPI->CHENSET = ppi_channel; + spiBaseAddress->EVENTS_END = 0; spim->INTENSET = (1<<6); spim->INTENSET = (1<<1); spim->INTENSET = (1<<19); } void SpiMaster::OnEndEvent() { - if(!busy) return; + if(currentBufferAddr == 0) { + return; + } auto s = currentBufferSize; if(s > 0) { @@ -113,21 +120,21 @@ void SpiMaster::OnEndEvent() { } else { uint8_t* buffer = nullptr; size_t size = 0; - busy = false; + if(taskToNotify != nullptr) { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + vTaskNotifyGiveFromISR(taskToNotify, &xHigherPriorityTaskWoken); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } - - if(taskToNotify != nullptr) { + nrf_gpio_pin_set(this->pinCsn); + currentBufferAddr = 0; BaseType_t xHigherPriorityTaskWoken = pdFALSE; - vTaskNotifyGiveFromISR(taskToNotify, &xHigherPriorityTaskWoken); + xSemaphoreGiveFromISR(mutex, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); - } - - nrf_gpio_pin_set(pinCsn); } } void SpiMaster::OnStartedEvent() { - if(!busy) return; } void SpiMaster::PrepareTx(const volatile uint32_t bufferAddress, const volatile size_t size) { @@ -142,10 +149,9 @@ void SpiMaster::PrepareTx(const volatile uint32_t bufferAddress, const volatile bool SpiMaster::Write(const uint8_t *data, size_t size) { if(data == nullptr) return false; + auto ok = xSemaphoreTake(mutex, portMAX_DELAY); + ASSERT(ok == true); taskToNotify = xTaskGetCurrentTaskHandle(); - while(busy) { - asm("nop"); - } if(size == 1) { SetupWorkaroundForFtpan58(spiBaseAddress, 0,0); @@ -157,7 +163,6 @@ bool SpiMaster::Write(const uint8_t *data, size_t size) { currentBufferAddr = (uint32_t)data; currentBufferSize = size; - busy = true; auto currentSize = std::min((size_t)255, (size_t)currentBufferSize); PrepareTx(currentBufferAddr, currentSize); @@ -167,7 +172,7 @@ bool SpiMaster::Write(const uint8_t *data, size_t size) { if(size == 1) { while (spiBaseAddress->EVENTS_END == 0); - busy = false; + xSemaphoreGive(mutex); } return true; diff --git a/src/drivers/SpiMaster.h b/src/drivers/SpiMaster.h index 362f480c..8a633b7f 100644 --- a/src/drivers/SpiMaster.h +++ b/src/drivers/SpiMaster.h @@ -7,6 +7,8 @@ #include #include "BufferProvider.h" +#include + namespace Pinetime { namespace Drivers { class SpiMaster { @@ -51,10 +53,10 @@ namespace Pinetime { SpiMaster::SpiModule spi; SpiMaster::Parameters params; - volatile bool busy = false; volatile uint32_t currentBufferAddr = 0; volatile size_t currentBufferSize = 0; volatile TaskHandle_t taskToNotify; + SemaphoreHandle_t mutex; }; } } diff --git a/src/drivers/Watchdog.cpp b/src/drivers/Watchdog.cpp index 55b6de73..850fd2f1 100644 --- a/src/drivers/Watchdog.cpp +++ b/src/drivers/Watchdog.cpp @@ -33,16 +33,16 @@ void Watchdog::Kick() { Watchdog::ResetReasons Watchdog::ActualResetReason() const { uint32_t resetReason; - sd_power_reset_reason_get(&resetReason); - sd_power_reset_reason_clr(0xFFFFFFFF); - if(resetReason & 0x01u) return ResetReasons::ResetPin; - if((resetReason >> 1u) & 0x01u) return ResetReasons::Watchdog; - if((resetReason >> 2u) & 0x01u) return ResetReasons::SoftReset; - if((resetReason >> 3u) & 0x01u) return ResetReasons::CpuLockup; - if((resetReason >> 16u) & 0x01u) return ResetReasons::SystemOff; - if((resetReason >> 17u) & 0x01u) return ResetReasons::LpComp; - if((resetReason >> 18u) & 0x01u) return ResetReasons::DebugInterface; - if((resetReason >> 19u) & 0x01u) return ResetReasons::NFC; +// sd_power_reset_reason_get(&resetReason); +// sd_power_reset_reason_clr(0xFFFFFFFF); +// if(resetReason & 0x01u) return ResetReasons::ResetPin; +// if((resetReason >> 1u) & 0x01u) return ResetReasons::Watchdog; +// if((resetReason >> 2u) & 0x01u) return ResetReasons::SoftReset; +// if((resetReason >> 3u) & 0x01u) return ResetReasons::CpuLockup; +// if((resetReason >> 16u) & 0x01u) return ResetReasons::SystemOff; +// if((resetReason >> 17u) & 0x01u) return ResetReasons::LpComp; +// if((resetReason >> 18u) & 0x01u) return ResetReasons::DebugInterface; +// if((resetReason >> 19u) & 0x01u) return ResetReasons::NFC; return ResetReasons::HardReset; } diff --git a/src/libs/lvgl/library.json b/src/libs/lvgl/library.json index 23ecc3fb..d8b0bf76 100644 --- a/src/libs/lvgl/library.json +++ b/src/libs/lvgl/library.json @@ -1,6 +1,6 @@ { "name": "lvgl", - "version": "6.1.1", + "version": "v6.1.2", "keywords": "graphics, gui, embedded, littlevgl", "description": "Graphics library to create embedded GUI with easy-to-use graphical elements, beautiful visual effects and low memory footprint. It offers anti-aliasing, opacity, and animations using only one frame buffer.", "repository": diff --git a/src/libs/lvgl/porting/lv_port_disp_template.c b/src/libs/lvgl/porting/lv_port_disp_template.c index 295dbe1c..9752d5d9 100644 --- a/src/libs/lvgl/porting/lv_port_disp_template.c +++ b/src/libs/lvgl/porting/lv_port_disp_template.c @@ -9,7 +9,7 @@ /********************* * INCLUDES *********************/ -#include "lv_port_disp_templ.h" +#include "lv_port_disp_template.h" /********************* * DEFINES @@ -26,8 +26,9 @@ static void disp_init(void); static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p); #if LV_USE_GPU -static void gpu_blend(lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa); -static void gpu_fill(lv_color_t * dest, uint32_t length, lv_color_t color); +static void gpu_blend(lv_disp_drv_t * disp_drv, lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa); +static void gpu_fill(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, lv_coord_t dest_width, + const lv_area_t * fill_area, lv_color_t color); #endif /********************** @@ -112,10 +113,10 @@ void lv_port_disp_init(void) /*Optionally add functions to access the GPU. (Only in buffered mode, LV_VDB_SIZE != 0)*/ /*Blend two color array using opacity*/ - disp_drv.gpu_blend = gpu_blend; + disp_drv.gpu_blend_cb = gpu_blend; /*Fill a memory array with a color*/ - disp_drv.gpu_fill = gpu_fill; + disp_drv.gpu_fill_cb = gpu_fill; #endif /*Finally register the driver*/ @@ -151,7 +152,7 @@ static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_colo /* IMPORTANT!!! * Inform the graphics library that you are ready with the flushing*/ - lv_disp_flush_ready(disp); + lv_disp_flush_ready(disp_drv); } @@ -171,11 +172,11 @@ static void gpu_blend(lv_disp_drv_t * disp_drv, lv_color_t * dest, const lv_colo /* If your MCU has hardware accelerator (GPU) then you can use it to fill a memory with a color * It can be used only in buffered mode (LV_VDB_SIZE != 0 in lv_conf.h)*/ -static void gpu_fill_cb(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, lv_coord_t dest_width, - const lv_area_t * fill_area, lv_color_t color); +static void gpu_fill(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, lv_coord_t dest_width, + const lv_area_t * fill_area, lv_color_t color) { /*It's an example code which should be done by your GPU*/ - uint32_t x, y; + int32_t x, y; dest_buf += dest_width * fill_area->y1; /*Go to the first line*/ for(y = fill_area->y1; y < fill_area->y2; y++) { diff --git a/src/libs/lvgl/porting/lv_port_fs_template.c b/src/libs/lvgl/porting/lv_port_fs_template.c index dab94608..454899d6 100644 --- a/src/libs/lvgl/porting/lv_port_fs_template.c +++ b/src/libs/lvgl/porting/lv_port_fs_template.c @@ -9,7 +9,7 @@ /********************* * INCLUDES *********************/ -#include "lv_port_fs_templ.h" +#include "lv_port_fs_template.h" /********************* * DEFINES @@ -85,30 +85,30 @@ void lv_port_fs_init(void) *--------------------------------------------------*/ /* Add a simple drive to open images */ - lv_fs_drv_t fs_drv; /*A driver descriptor*/ - memset(&fs_drv, 0, sizeof(lv_fs_drv_t)); /*Initialization*/ + lv_fs_drv_t fs_drv; + lv_fs_drv_init(&fs_drv); /*Set up fields...*/ fs_drv.file_size = sizeof(file_t); fs_drv.letter = 'P'; - fs_drv.open = fs_open; - fs_drv.close = fs_close; - fs_drv.read = fs_read; - fs_drv.write = fs_write; - fs_drv.seek = fs_seek; - fs_drv.tell = fs_tell; - fs_drv.free = fs_free; - fs_drv.size = fs_size; - fs_drv.remove = fs_remove; - fs_drv.rename = fs_rename; - fs_drv.trunc = fs_trunc; + fs_drv.open_cb = fs_open; + fs_drv.close_cb = fs_close; + fs_drv.read_cb = fs_read; + fs_drv.write_cb = fs_write; + fs_drv.seek_cb = fs_seek; + fs_drv.tell_cb = fs_tell; + fs_drv.free_space_cb = fs_free; + fs_drv.size_cb = fs_size; + fs_drv.remove_cb = fs_remove; + fs_drv.rename_cb = fs_rename; + fs_drv.trunc_cb = fs_trunc; fs_drv.rddir_size = sizeof(dir_t); - fs_drv.dir_close = fs_dir_close; - fs_drv.dir_open = fs_dir_open; - fs_drv.dir_read = fs_dir_read; + fs_drv.dir_close_cb = fs_dir_close; + fs_drv.dir_open_cb = fs_dir_open; + fs_drv.dir_read_cb = fs_dir_read; - lv_fs_add_drv(&fs_drv); + lv_fs_drv_register(&fs_drv); } /********************** @@ -315,7 +315,7 @@ static lv_fs_res_t fs_rename (lv_fs_drv_t * drv, const char * oldname, const cha * @param free_p pointer to store the free size [kB] * @return LV_FS_RES_OK or any error from lv_fs_res_t enum */ -static lv_fs_res_t fs_free (uint32_t * total_p, uint32_t * free_p) +static lv_fs_res_t fs_free (lv_fs_drv_t * drv, uint32_t * total_p, uint32_t * free_p) { lv_fs_res_t res = LV_FS_RES_NOT_IMP; diff --git a/src/libs/lvgl/porting/lv_port_indev_template.c b/src/libs/lvgl/porting/lv_port_indev_template.c index 7666023b..54b8c9fd 100644 --- a/src/libs/lvgl/porting/lv_port_indev_template.c +++ b/src/libs/lvgl/porting/lv_port_indev_template.c @@ -9,7 +9,7 @@ /********************* * INCLUDES *********************/ -#include "lv_port_indev_templ.h" +#include "lv_port_indev_template.h" /********************* * DEFINES diff --git a/src/libs/lvgl/scripts/Doxyfile b/src/libs/lvgl/scripts/Doxyfile index 2f93286b..7120f5d2 100644 --- a/src/libs/lvgl/scripts/Doxyfile +++ b/src/libs/lvgl/scripts/Doxyfile @@ -724,7 +724,7 @@ CITE_BIB_FILES = # messages are off. # The default value is: NO. -QUIET = NO +QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES @@ -733,14 +733,14 @@ QUIET = NO # Tip: Turn warnings on while writing the documentation. # The default value is: YES. -WARNINGS = YES +WARNINGS = NO # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. -WARN_IF_UNDOCUMENTED = YES +WARN_IF_UNDOCUMENTED = NO # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters @@ -748,7 +748,7 @@ WARN_IF_UNDOCUMENTED = YES # markup commands wrongly. # The default value is: YES. -WARN_IF_DOC_ERROR = YES +WARN_IF_DOC_ERROR = NO # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return diff --git a/src/libs/lvgl/src/lv_core/lv_debug.h b/src/libs/lvgl/src/lv_core/lv_debug.h index 72eb3367..a932004e 100644 --- a/src/libs/lvgl/src/lv_core/lv_debug.h +++ b/src/libs/lvgl/src/lv_core/lv_debug.h @@ -49,7 +49,7 @@ void lv_debug_log_error(const char * msg, uint64_t value); { \ if(!(expr)) { \ LV_LOG_ERROR(__func__); \ - lv_debug_log_error(msg, (uint64_t)value); \ + lv_debug_log_error(msg, (uint64_t)((uintptr_t)value)); \ while(1); \ } \ } diff --git a/src/libs/lvgl/src/lv_core/lv_obj.c b/src/libs/lvgl/src/lv_core/lv_obj.c index 089c5e34..510a8706 100644 --- a/src/libs/lvgl/src/lv_core/lv_obj.c +++ b/src/libs/lvgl/src/lv_core/lv_obj.c @@ -121,6 +121,20 @@ void lv_init(void) LV_LOG_INFO("lv_init ready"); } +#if LV_ENABLE_GC || !LV_MEM_CUSTOM +void lv_deinit(void) +{ + lv_gc_clear_roots(); +#if LV_USE_LOG + lv_log_register_print_cb(NULL); +#endif + lv_disp_set_default(NULL); + lv_mem_deinit(); + lv_initialized = false; + LV_LOG_INFO("lv_deinit done"); +} +#endif + /*-------------------- * Create and delete *-------------------*/ @@ -507,10 +521,12 @@ void lv_obj_clean(lv_obj_t * obj) } /** - * Mark the object as invalid therefore its current position will be redrawn by 'lv_refr_task' + * Mark an area of an object as invalid. + * This area will be redrawn by 'lv_refr_task' * @param obj pointer to an object + * @param area the area to redraw */ -void lv_obj_invalidate(const lv_obj_t * obj) +void lv_obj_invalidate_area(const lv_obj_t * obj, const lv_area_t * area) { LV_ASSERT_OBJ(obj, LV_OBJX_NAME); @@ -521,31 +537,56 @@ void lv_obj_invalidate(const lv_obj_t * obj) lv_disp_t * disp = lv_obj_get_disp(obj_scr); if(obj_scr == lv_disp_get_scr_act(disp) || obj_scr == lv_disp_get_layer_top(disp) || obj_scr == lv_disp_get_layer_sys(disp)) { - /*Truncate recursively to the parents*/ - lv_area_t area_trunc; - lv_obj_t * par = lv_obj_get_parent(obj); - bool union_ok = true; - /*Start with the original coordinates*/ - lv_coord_t ext_size = obj->ext_draw_pad; - lv_area_copy(&area_trunc, &obj->coords); - area_trunc.x1 -= ext_size; - area_trunc.y1 -= ext_size; - area_trunc.x2 += ext_size; - area_trunc.y2 += ext_size; - /*Check through all parents*/ + /*Truncate the area to the object*/ + lv_area_t obj_coords; + lv_coord_t ext_size = obj->ext_draw_pad; + lv_area_copy(&obj_coords, &obj->coords); + obj_coords.x1 -= ext_size; + obj_coords.y1 -= ext_size; + obj_coords.x2 += ext_size; + obj_coords.y2 += ext_size; + + bool is_common; + lv_area_t area_trunc; + + is_common = lv_area_intersect(&area_trunc, area, &obj_coords); + if(is_common == false) return; /*The area is not on the object*/ + + /*Truncate recursively to the parents*/ + lv_obj_t * par = lv_obj_get_parent(obj); while(par != NULL) { - union_ok = lv_area_intersect(&area_trunc, &area_trunc, &par->coords); - if(union_ok == false) break; /*If no common parts with parent break;*/ + is_common = lv_area_intersect(&area_trunc, &area_trunc, &par->coords); + if(is_common == false) break; /*If no common parts with parent break;*/ if(lv_obj_get_hidden(par)) return; /*If the parent is hidden then the child is hidden and won't be drawn*/ par = lv_obj_get_parent(par); } - if(union_ok) lv_inv_area(disp, &area_trunc); + if(is_common) lv_inv_area(disp, &area_trunc); } } +/** + * Mark the object as invalid therefore its current position will be redrawn by 'lv_refr_task' + * @param obj pointer to an object + */ +void lv_obj_invalidate(const lv_obj_t * obj) +{ + LV_ASSERT_OBJ(obj, LV_OBJX_NAME); + + /*Truncate the area to the object*/ + lv_area_t obj_coords; + lv_coord_t ext_size = obj->ext_draw_pad; + lv_area_copy(&obj_coords, &obj->coords); + obj_coords.x1 -= ext_size; + obj_coords.y1 -= ext_size; + obj_coords.x2 += ext_size; + obj_coords.y2 += ext_size; + + lv_obj_invalidate_area(obj, &obj_coords); + +} /*===================== * Setter functions *====================*/ @@ -1458,7 +1499,9 @@ lv_res_t lv_event_send(lv_obj_t * obj, lv_event_t event, const void * data) */ lv_res_t lv_event_send_func(lv_event_cb_t event_xcb, lv_obj_t * obj, lv_event_t event, const void * data) { - LV_ASSERT_OBJ(obj, LV_OBJX_NAME); + if(obj != NULL) { + LV_ASSERT_OBJ(obj, LV_OBJX_NAME); + } /* Build a simple linked list from the objects used in the events * It's important to know if an this object was deleted by a nested event diff --git a/src/libs/lvgl/src/lv_core/lv_obj.h b/src/libs/lvgl/src/lv_core/lv_obj.h index 86987189..24517241 100644 --- a/src/libs/lvgl/src/lv_core/lv_obj.h +++ b/src/libs/lvgl/src/lv_core/lv_obj.h @@ -269,6 +269,15 @@ typedef struct */ void lv_init(void); + +/** + * Deinit the 'lv' library + * Currently only implemented when not using custorm allocators, or GC is enabled. + */ +#if LV_ENABLE_GC || !LV_MEM_CUSTOM +void lv_deinit(void); +#endif + /*-------------------- * Create and delete *-------------------*/ @@ -303,6 +312,15 @@ void lv_obj_del_async(struct _lv_obj_t *obj); */ void lv_obj_clean(lv_obj_t * obj); + +/** + * Mark an area of an object as invalid. + * This area will be redrawn by 'lv_refr_task' + * @param obj pointer to an object + * @param area the area to redraw + */ +void lv_obj_invalidate_area(const lv_obj_t * obj, const lv_area_t * area); + /** * Mark the object as invalid therefore its current position will be redrawn by 'lv_refr_task' * @param obj pointer to an object diff --git a/src/libs/lvgl/src/lv_draw/lv_draw_label.c b/src/libs/lvgl/src/lv_draw/lv_draw_label.c index 29865438..bf75411d 100644 --- a/src/libs/lvgl/src/lv_draw/lv_draw_label.c +++ b/src/libs/lvgl/src/lv_draw/lv_draw_label.c @@ -179,6 +179,7 @@ void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask, const lv_st char *bidi_txt = lv_draw_get_buf(line_end - line_start + 1); lv_bidi_process_paragraph(txt + line_start, bidi_txt, line_end - line_start, bidi_dir, NULL, 0); #else + (void)bidi_dir; const char *bidi_txt = txt + line_start; #endif diff --git a/src/libs/lvgl/src/lv_draw/lv_img_cache.c b/src/libs/lvgl/src/lv_draw/lv_img_cache.c index 5ca48e43..841e1169 100644 --- a/src/libs/lvgl/src/lv_draw/lv_img_cache.c +++ b/src/libs/lvgl/src/lv_draw/lv_img_cache.c @@ -85,7 +85,7 @@ lv_img_cache_entry_t * lv_img_cache_open(const void * src, const lv_style_t * st bool match = false; lv_img_src_t src_type = lv_img_src_get_type(cache[i].dec_dsc.src); if(src_type == LV_IMG_SRC_VARIABLE) { - if(cache[i].dec_dsc.src == src) match = true; + if(cache[i].dec_dsc.src == src && cache[i].dec_dsc.style == style) match = true; } else if(src_type == LV_IMG_SRC_FILE) { if(strcmp(cache[i].dec_dsc.src, src) == 0) match = true; } diff --git a/src/libs/lvgl/src/lv_draw/lv_img_decoder.c b/src/libs/lvgl/src/lv_draw/lv_img_decoder.c index 5505b90e..b3c9d9c8 100644 --- a/src/libs/lvgl/src/lv_draw/lv_img_decoder.c +++ b/src/libs/lvgl/src/lv_draw/lv_img_decoder.c @@ -509,6 +509,7 @@ void lv_img_decoder_built_in_close(lv_img_decoder_t * decoder, lv_img_decoder_ds } #endif if(user_data->palette) lv_mem_free(user_data->palette); + if(user_data->opa) lv_mem_free(user_data->opa); lv_mem_free(user_data); diff --git a/src/libs/lvgl/src/lv_font/lv_font.h b/src/libs/lvgl/src/lv_font/lv_font.h index 50be635b..ee3300b8 100644 --- a/src/libs/lvgl/src/lv_font/lv_font.h +++ b/src/libs/lvgl/src/lv_font/lv_font.h @@ -75,7 +75,7 @@ typedef struct _lv_font_struct /*Pointer to the font in a font pack (must have the same line height)*/ uint8_t line_height; /**< The real line height where any text fits*/ - uint8_t base_line; /**< Base line measured from the top of the line_height*/ + int8_t base_line; /**< Base line measured from the top of the line_height*/ uint8_t subpx :2; /**< An element of `lv_font_subpx_t`*/ void * dsc; /**< Store implementation specific or run_time data or caching here*/ #if LV_USE_USER_DATA diff --git a/src/libs/lvgl/src/lv_font/lv_font_fmt_txt.c b/src/libs/lvgl/src/lv_font/lv_font_fmt_txt.c index fb02c743..78fcccdd 100644 --- a/src/libs/lvgl/src/lv_font/lv_font_fmt_txt.c +++ b/src/libs/lvgl/src/lv_font/lv_font_fmt_txt.c @@ -257,7 +257,7 @@ static int8_t get_kern_value(const lv_font_t * font, uint32_t gid_left, uint32_t /*Kern classes*/ const lv_font_fmt_txt_kern_classes_t * kdsc = fdsc->kern_dsc; uint8_t left_class = kdsc->left_class_mapping[gid_left]; - uint8_t right_class = kdsc->left_class_mapping[gid_right]; + uint8_t right_class = kdsc->right_class_mapping[gid_right]; /* If class = 0, kerning not exist for that glyph * else got the value form `class_pair_values` 2D array*/ @@ -475,5 +475,5 @@ static uint8_t rle_next(void) */ static int32_t unicode_list_compare(const void * ref, const void * element) { - return (*(uint16_t *)ref) - (*(uint16_t *)element); + return ((int32_t)(*(uint16_t *)ref)) - ((int32_t)(*(uint16_t *)element)); } diff --git a/src/libs/lvgl/src/lv_font/lv_symbol_def.h b/src/libs/lvgl/src/lv_font/lv_symbol_def.h index 6fe823b7..026f4a60 100644 --- a/src/libs/lvgl/src/lv_font/lv_symbol_def.h +++ b/src/libs/lvgl/src/lv_font/lv_symbol_def.h @@ -12,12 +12,12 @@ extern "C" { #endif /* In the font converter use this list as range: - 61441, 61448, 61451, 61452, 61452, 61453, 61457, 61459, 61461, 61465, - 61468, 61473, 61478, 61479, 61480, 61502, 61512, 61515, 61516, 61517, - 61521, 61522, 61523, 61524, 61543, 61544, 61550, 61552, 61553, 61556, - 61559, 61560, 61561, 61563, 61587, 61589, 61636, 61637, 61639, 61671, - 61674, 61683, 61724, 61732, 61787, 61931, 62016, 62017, 62018, 62019, - 62020, 62087, 62099, 62212, 62189, 62810, 63426, 63650 + 61441, 61448, 61451, 61452, 61453, 61457, 61459, 61461, 61465, 61468, + 61473, 61478, 61479, 61480, 61502, 61512, 61515, 61516, 61517, 61521, + 61522, 61523, 61524, 61543, 61544, 61550, 61552, 61553, 61556, 61559, + 61560, 61561, 61563, 61587, 61589, 61636, 61637, 61639, 61671, 61674, + 61683, 61724, 61732, 61787, 61931, 62016, 62017, 62018, 62019, 62020, + 62087, 62099, 62212, 62189, 62810, 63426, 63650 */ #define LV_SYMBOL_AUDIO "\xef\x80\x81" /*61441, 0xF001*/ @@ -93,7 +93,6 @@ enum { _LV_STR_SYMBOL_CLOSE, _LV_STR_SYMBOL_POWER, _LV_STR_SYMBOL_SETTINGS, - _LV_STR_SYMBOL_TRASH, _LV_STR_SYMBOL_HOME, _LV_STR_SYMBOL_DOWNLOAD, _LV_STR_SYMBOL_DRIVE, @@ -113,6 +112,8 @@ enum { _LV_STR_SYMBOL_RIGHT, _LV_STR_SYMBOL_PLUS, _LV_STR_SYMBOL_MINUS, + _LV_STR_SYMBOL_EYE_OPEN, + _LV_STR_SYMBOL_EYE_CLOSE, _LV_STR_SYMBOL_WARNING, _LV_STR_SYMBOL_SHUFFLE, _LV_STR_SYMBOL_UP, @@ -125,6 +126,7 @@ enum { _LV_STR_SYMBOL_COPY, _LV_STR_SYMBOL_SAVE, _LV_STR_SYMBOL_CHARGE, + _LV_STR_SYMBOL_PASTE, _LV_STR_SYMBOL_BELL, _LV_STR_SYMBOL_KEYBOARD, _LV_STR_SYMBOL_GPS, @@ -135,7 +137,12 @@ enum { _LV_STR_SYMBOL_BATTERY_2, _LV_STR_SYMBOL_BATTERY_1, _LV_STR_SYMBOL_BATTERY_EMPTY, + _LV_STR_SYMBOL_USB, _LV_STR_SYMBOL_BLUETOOTH, + _LV_STR_SYMBOL_TRASH, + _LV_STR_SYMBOL_BACKSPACE, + _LV_STR_SYMBOL_SD_CARD, + _LV_STR_SYMBOL_NEW_LINE, _LV_STR_SYMBOL_DUMMY, }; diff --git a/src/libs/lvgl/src/lv_hal/lv_hal_disp.c b/src/libs/lvgl/src/lv_hal/lv_hal_disp.c index c2d1f13a..e3a275a0 100644 --- a/src/libs/lvgl/src/lv_hal/lv_hal_disp.c +++ b/src/libs/lvgl/src/lv_hal/lv_hal_disp.c @@ -127,6 +127,7 @@ lv_disp_t * lv_disp_drv_register(lv_disp_drv_t * driver) memset(&disp->inv_area_joined, 0, sizeof(disp->inv_area_joined)); memset(&disp->inv_areas, 0, sizeof(disp->inv_areas)); lv_ll_init(&disp->scr_ll, sizeof(lv_obj_t)); + disp->last_activity_time = 0; if(disp_def == NULL) disp_def = disp; diff --git a/src/libs/lvgl/src/lv_misc/lv_bidi.c b/src/libs/lvgl/src/lv_misc/lv_bidi.c index bde75207..6e50d926 100644 --- a/src/libs/lvgl/src/lv_misc/lv_bidi.c +++ b/src/libs/lvgl/src/lv_misc/lv_bidi.c @@ -148,9 +148,11 @@ bool lv_bidi_letter_is_neutral(uint32_t letter) uint16_t lv_bidi_get_logical_pos(const char * str_in, char **bidi_txt, uint32_t len, lv_bidi_dir_t base_dir, uint32_t visual_pos, bool *is_rtl) { uint32_t pos_conv_len = get_txt_len(str_in, len); - void *buf = lv_draw_get_buf(len + pos_conv_len * sizeof(uint16_t)); + uint32_t txt_buf_size = len + 1; + txt_buf_size = (txt_buf_size + 3) & (~0x3); + void *buf = lv_draw_get_buf(txt_buf_size + pos_conv_len * sizeof(uint16_t)); if (bidi_txt) *bidi_txt = buf; - uint16_t *pos_conv_buf = (uint16_t*) ((char*)buf + len); + uint16_t *pos_conv_buf = (uint16_t*) ((char*)buf + txt_buf_size); lv_bidi_process_paragraph(str_in, bidi_txt? *bidi_txt: NULL, len, base_dir, pos_conv_buf, pos_conv_len); if (is_rtl) *is_rtl = IS_RTL_POS(pos_conv_buf[visual_pos]); return GET_POS(pos_conv_buf[visual_pos]); @@ -159,9 +161,11 @@ uint16_t lv_bidi_get_logical_pos(const char * str_in, char **bidi_txt, uint32_t uint16_t lv_bidi_get_visual_pos(const char * str_in, char **bidi_txt, uint16_t len, lv_bidi_dir_t base_dir, uint32_t logical_pos, bool *is_rtl) { uint32_t pos_conv_len = get_txt_len(str_in, len); - void *buf = lv_draw_get_buf(len + pos_conv_len * sizeof(uint16_t)); + uint32_t txt_buf_size = len + 1; + txt_buf_size = (txt_buf_size + 3) & (~0x3); + void *buf = lv_draw_get_buf(txt_buf_size + pos_conv_len * sizeof(uint16_t)); if (bidi_txt) *bidi_txt = buf; - uint16_t *pos_conv_buf = (uint16_t*) ((char*)buf + len); + uint16_t *pos_conv_buf = (uint16_t*) ((char*)buf + txt_buf_size); lv_bidi_process_paragraph(str_in, bidi_txt? *bidi_txt: NULL, len, base_dir, pos_conv_buf, pos_conv_len); for (uint16_t i = 0; i < pos_conv_len; i++){ if (GET_POS(pos_conv_buf[i]) == logical_pos){ diff --git a/src/libs/lvgl/src/lv_misc/lv_color.h b/src/libs/lvgl/src/lv_misc/lv_color.h index dc861690..1febbdce 100644 --- a/src/libs/lvgl/src/lv_misc/lv_color.h +++ b/src/libs/lvgl/src/lv_misc/lv_color.h @@ -104,9 +104,9 @@ enum { # define LV_COLOR_GET_B1(c) (c).ch.blue # define LV_COLOR_GET_A1(c) 1 -# define LV_COLOR_SET_R8(c, v) (c).ch.red = (uint8_t)((v) & 0x7); -# define LV_COLOR_SET_G8(c, v) (c).ch.green = (uint8_t)((v) & 0x7); -# define LV_COLOR_SET_B8(c, v) (c).ch.blue = (uint8_t)((v) & 0x3); +# define LV_COLOR_SET_R8(c, v) (c).ch.red = (uint8_t)(v) & 0x7U; +# define LV_COLOR_SET_G8(c, v) (c).ch.green = (uint8_t)(v) & 0x7U; +# define LV_COLOR_SET_B8(c, v) (c).ch.blue = (uint8_t)(v) & 0x3U; # define LV_COLOR_SET_A8(c, v) do {} while(0) # define LV_COLOR_GET_R8(c) (c).ch.red @@ -114,10 +114,10 @@ enum { # define LV_COLOR_GET_B8(c) (c).ch.blue # define LV_COLOR_GET_A8(c) 0xFF -# define LV_COLOR_SET_R16(c, v) (c).ch.red = (uint8_t)(((uint8_t)(v)) & 0x1F); -# define LV_COLOR_SET_G16(c, v) (c).ch.green = (uint8_t)((v) & 0x3F); +# define LV_COLOR_SET_R16(c, v) (c).ch.red = (uint8_t)(v) & 0x1FU; +# define LV_COLOR_SET_G16(c, v) (c).ch.green = (uint8_t)(v) & 0x3FU; # define LV_COLOR_SET_G16_SWAP(c, v) {(c).ch.green_h = (uint8_t)(((v) >> 3) & 0x7); (c).ch.green_l = (uint8_t)((v) & 0x7);} -# define LV_COLOR_SET_B16(c, v) (c).ch.blue = (uint8_t)((v) & 0x1F); +# define LV_COLOR_SET_B16(c, v) (c).ch.blue = (uint8_t)(v) & 0x1FU; # define LV_COLOR_SET_A16(c, v) do {} while(0) # define LV_COLOR_GET_R16(c) (c).ch.red @@ -344,7 +344,6 @@ static inline uint8_t lv_color_to8(lv_color_t color) static inline uint16_t lv_color_to16(lv_color_t color) { - #if LV_COLOR_DEPTH == 1 if(color.full == 0) return 0; @@ -373,9 +372,7 @@ static inline uint16_t lv_color_to16(lv_color_t color) #endif LV_COLOR_SET_B16(ret, LV_COLOR_GET_B(color) >> 3); /* 8 - 5 = 3*/ return ret.full; -#endif - - return 0; +#endif } static inline uint32_t lv_color_to32(lv_color_t color) @@ -464,14 +461,14 @@ static inline uint8_t lv_color_brightness(lv_color_t color) /* The most simple macro to create a color from R,G and B values */ #if LV_COLOR_DEPTH == 1 -#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){.full = (b8 >> 7 | g8 >> 7 | r8 >> 7)}) +#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){.full = (uint8_t)((b8 >> 7) | (g8 >> 7) | (r8 >> 7))}) #elif LV_COLOR_DEPTH == 8 -#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){{b8 >> 6, g8 >> 5, r8 >> 5}}) +#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){{(uint8_t)((b8 >> 6) & 0x3U), (uint8_t)((g8 >> 5) & 0x7U), (uint8_t)((r8 >> 5) & 0x7U)}}) #elif LV_COLOR_DEPTH == 16 #if LV_COLOR_16_SWAP == 0 -#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){{b8 >> 3, g8 >> 2, r8 >> 3}}) +#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){{(uint16_t)((b8 >> 3) & 0x1FU), (uint16_t)((g8 >> 2) & 0x3FU), (uint16_t)((r8 >> 3) & 0x1FU)}}) #else -#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){{g8 >> 5, r8 >> 3, b8 >> 3, (g8 >> 2) & 0x7}}) +#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){{(uint16_t)((g8 >> 5) & 0x7U), (uint16_t)((r8 >> 3) & 0x1FU), (uint16_t)((b8 >> 3) & 0x1FU), (uint16_t)((g8 >> 2) & 0x7U)}}) #endif #elif LV_COLOR_DEPTH == 32 #define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){{b8, g8, r8, 0xff}}) /*Fix 0xff alpha*/ diff --git a/src/libs/lvgl/src/lv_misc/lv_gc.c b/src/libs/lvgl/src/lv_misc/lv_gc.c index 70dfc9a8..94bf532a 100644 --- a/src/libs/lvgl/src/lv_misc/lv_gc.c +++ b/src/libs/lvgl/src/lv_misc/lv_gc.c @@ -8,6 +8,11 @@ *********************/ #include "lv_gc.h" +#include "string.h" + +#if defined(LV_GC_INCLUDE) +#include LV_GC_INCLUDE +#endif /* LV_ENABLE_GC */ /********************* * DEFINES @@ -35,6 +40,12 @@ LV_ROOTS * GLOBAL FUNCTIONS **********************/ +void lv_gc_clear_roots(void) +{ +#define LV_CLEAR_ROOT(root_type, root_name) memset(&LV_GC_ROOT(root_name), 0, sizeof(LV_GC_ROOT(root_name))); + LV_ITERATE_ROOTS(LV_CLEAR_ROOT) +} + /********************** * STATIC FUNCTIONS **********************/ diff --git a/src/libs/lvgl/src/lv_misc/lv_gc.h b/src/libs/lvgl/src/lv_misc/lv_gc.h index 0db9f5cb..afd4d600 100644 --- a/src/libs/lvgl/src/lv_misc/lv_gc.h +++ b/src/libs/lvgl/src/lv_misc/lv_gc.h @@ -30,21 +30,21 @@ extern "C" { * DEFINES *********************/ -#define LV_GC_ROOTS(prefix) \ - prefix lv_ll_t _lv_task_ll; /*Linked list to store the lv_tasks*/ \ - prefix lv_ll_t _lv_disp_ll; /*Linked list of screens*/ \ - prefix lv_ll_t _lv_indev_ll; /*Linked list of screens*/ \ - prefix lv_ll_t _lv_drv_ll; \ - prefix lv_ll_t _lv_file_ll; \ - prefix lv_ll_t _lv_anim_ll; \ - prefix lv_ll_t _lv_group_ll; \ - prefix lv_ll_t _lv_img_defoder_ll; \ - prefix lv_img_cache_entry_t * _lv_img_cache_array; \ - prefix void * _lv_task_act; \ - prefix void * _lv_draw_buf; +#define LV_ITERATE_ROOTS(f) \ + f(lv_ll_t, _lv_task_ll) /*Linked list to store the lv_tasks*/ \ + f(lv_ll_t, _lv_disp_ll) /*Linked list of screens*/ \ + f(lv_ll_t, _lv_indev_ll) /*Linked list of screens*/ \ + f(lv_ll_t, _lv_drv_ll) \ + f(lv_ll_t, _lv_file_ll) \ + f(lv_ll_t, _lv_anim_ll) \ + f(lv_ll_t, _lv_group_ll) \ + f(lv_ll_t, _lv_img_defoder_ll) \ + f(lv_img_cache_entry_t*, _lv_img_cache_array) \ + f(void*, _lv_task_act) \ + f(void*, _lv_draw_buf) -#define LV_NO_PREFIX -#define LV_ROOTS LV_GC_ROOTS(LV_NO_PREFIX) +#define LV_DEFINE_ROOT(root_type, root_name) root_type root_name; +#define LV_ROOTS LV_ITERATE_ROOTS(LV_DEFINE_ROOT) #if LV_ENABLE_GC == 1 #if LV_MEM_CUSTOM != 1 @@ -52,7 +52,8 @@ extern "C" { #endif /* LV_MEM_CUSTOM */ #else /* LV_ENABLE_GC */ #define LV_GC_ROOT(x) x -LV_GC_ROOTS(extern) +#define LV_EXTERN_ROOT(root_type, root_name) extern root_type root_name; +LV_ITERATE_ROOTS(LV_EXTERN_ROOT) #endif /* LV_ENABLE_GC */ /********************** @@ -63,6 +64,8 @@ LV_GC_ROOTS(extern) * GLOBAL PROTOTYPES **********************/ +void lv_gc_clear_roots(void); + /********************** * MACROS **********************/ diff --git a/src/libs/lvgl/src/lv_misc/lv_math.h b/src/libs/lvgl/src/lv_misc/lv_math.h index dc2c547d..0f93a7c6 100644 --- a/src/libs/lvgl/src/lv_misc/lv_math.h +++ b/src/libs/lvgl/src/lv_misc/lv_math.h @@ -22,6 +22,11 @@ extern "C" { #define LV_MATH_MAX(a, b) ((a) > (b) ? (a) : (b)) #define LV_MATH_ABS(x) ((x) > 0 ? (x) : (-(x))) +#define LV_IS_SIGNED(t) (((t)(-1)) < ((t) 0)) +#define LV_UMAX_OF(t) (((0x1ULL << ((sizeof(t) * 8ULL) - 1ULL)) - 1ULL) | (0xFULL << ((sizeof(t) * 8ULL) - 4ULL))) +#define LV_SMAX_OF(t) (((0x1ULL << ((sizeof(t) * 8ULL) - 1ULL)) - 1ULL) | (0x7ULL << ((sizeof(t) * 8ULL) - 4ULL))) +#define LV_MAX_OF(t) ((unsigned long) (LV_IS_SIGNED(t) ? LV_SMAX_OF(t) : LV_UMAX_OF(t))) + #define LV_TRIGO_SIN_MAX 32767 #define LV_TRIGO_SHIFT 15 /**< >> LV_TRIGO_SHIFT to normalize*/ diff --git a/src/libs/lvgl/src/lv_misc/lv_mem.c b/src/libs/lvgl/src/lv_misc/lv_mem.c index 461a2405..9e18310f 100644 --- a/src/libs/lvgl/src/lv_misc/lv_mem.c +++ b/src/libs/lvgl/src/lv_misc/lv_mem.c @@ -23,7 +23,7 @@ #define LV_MEM_ADD_JUNK 0 #endif -#ifdef LV_MEM_ENV64 +#ifdef LV_ARCH_64 #define MEM_UNIT uint64_t #else #define MEM_UNIT uint32_t @@ -102,6 +102,21 @@ void lv_mem_init(void) #endif } +/** + * Clean up the memory buffer which frees all the allocated memories. + * @note It work only if `LV_MEM_CUSTOM == 0` + */ +void lv_mem_deinit(void) +{ +#if LV_MEM_CUSTOM == 0 + memset(work_mem, 0x00, (LV_MEM_SIZE / sizeof(MEM_UNIT)) * sizeof(MEM_UNIT)); + lv_mem_ent_t * full = (lv_mem_ent_t *)work_mem; + full->header.s.used = 0; + /*The total mem size id reduced by the first header and the close patterns */ + full->header.s.d_size = LV_MEM_SIZE - sizeof(lv_mem_header_t); +#endif +} + /** * Allocate a memory dynamically * @param size size of the memory to allocate in bytes @@ -113,7 +128,7 @@ void * lv_mem_alloc(size_t size) return &zero_mem; } -#ifdef LV_MEM_ENV64 +#ifdef LV_ARCH_64 /*Round the size up to 8*/ if(size & 0x7) { size = size & (~0x7); @@ -262,7 +277,7 @@ void * lv_mem_realloc(void * data_p, size_t new_size) #else /* LV_ENABLE_GC */ -void * lv_mem_realloc(void * data_p, uint32_t new_size) +void * lv_mem_realloc(void * data_p, size_t new_size) { void * new_p = LV_MEM_CUSTOM_REALLOC(data_p, new_size); if(new_p == NULL) LV_LOG_WARN("Couldn't allocate memory"); @@ -432,7 +447,7 @@ static void * ent_alloc(lv_mem_ent_t * e, size_t size) */ static void ent_trunc(lv_mem_ent_t * e, size_t size) { -#ifdef LV_MEM_ENV64 +#ifdef LV_ARCH_64 /*Round the size up to 8*/ if(size & 0x7) { size = size & (~0x7); @@ -456,11 +471,11 @@ static void ent_trunc(lv_mem_ent_t * e, size_t size) uint8_t * e_data = &e->first_data; lv_mem_ent_t * after_new_e = (lv_mem_ent_t *)&e_data[size]; after_new_e->header.s.used = 0; - after_new_e->header.s.d_size = e->header.s.d_size - size - sizeof(lv_mem_header_t); + after_new_e->header.s.d_size = (uint32_t)e->header.s.d_size - size - sizeof(lv_mem_header_t); } /* Set the new size for the original entry */ - e->header.s.d_size = size; + e->header.s.d_size = (uint32_t)size; } #endif diff --git a/src/libs/lvgl/src/lv_misc/lv_mem.h b/src/libs/lvgl/src/lv_misc/lv_mem.h index 34ca3e9e..f7240742 100644 --- a/src/libs/lvgl/src/lv_misc/lv_mem.h +++ b/src/libs/lvgl/src/lv_misc/lv_mem.h @@ -55,6 +55,12 @@ typedef struct */ void lv_mem_init(void); +/** + * Clean up the memory buffer which frees all the allocated memories. + * @note It work only if `LV_MEM_CUSTOM == 0` + */ +void lv_mem_deinit(void); + /** * Allocate a memory dynamically * @param size size of the memory to allocate in bytes diff --git a/src/libs/lvgl/src/lv_misc/lv_txt.c b/src/libs/lvgl/src/lv_misc/lv_txt.c index 929fb751..9de132e9 100644 --- a/src/libs/lvgl/src/lv_misc/lv_txt.c +++ b/src/libs/lvgl/src/lv_misc/lv_txt.c @@ -8,6 +8,7 @@ *********************/ #include "lv_txt.h" #include "lv_math.h" +#include "lv_log.h" /********************* * DEFINES @@ -108,8 +109,14 @@ void lv_txt_get_size(lv_point_t * size_res, const char * text, const lv_font_t * /*Calc. the height and longest line*/ while(text[line_start] != '\0') { new_line_start += lv_txt_get_next_line(&text[line_start], font, letter_space, max_width, flag); - size_res->y += letter_height; - size_res->y += line_space; + + if ((unsigned long)size_res->y + (unsigned long)letter_height + (unsigned long)line_space > LV_MAX_OF(lv_coord_t)) { + LV_LOG_WARN("lv_txt_get_size: integer overflow while calculating text height"); + return; + } else { + size_res->y += letter_height; + size_res->y += line_space; + } /*Calculate the the longest line*/ act_line_length = lv_txt_get_width(&text[line_start], new_line_start - line_start, font, letter_space, flag); @@ -118,7 +125,7 @@ void lv_txt_get_size(lv_point_t * size_res, const char * text, const lv_font_t * line_start = new_line_start; } - /*Ma ke the text one line taller if the last character is '\n' or '\r'*/ + /*Make the text one line taller if the last character is '\n' or '\r'*/ if((line_start != 0) && (text[line_start - 1] == '\n' || text[line_start - 1] == '\r')) { size_res->y += letter_height + line_space; } @@ -200,8 +207,12 @@ static uint16_t lv_txt_get_next_word(const char * txt, const lv_font_t * font, letter_w = lv_font_get_glyph_width(font, letter, letter_next); cur_w += letter_w; + if(letter_w > 0) { + cur_w += letter_space; + } + /* Test if this character fits within max_width */ - if(break_index == NO_BREAK_FOUND && cur_w > max_width) { + if(break_index == NO_BREAK_FOUND && (cur_w - letter_space) > max_width) { break_index = i; break_letter_count = word_len - 1; /* break_index is now pointing at the character that doesn't fit */ @@ -219,9 +230,6 @@ static uint16_t lv_txt_get_next_word(const char * txt, const lv_font_t * font, /* Update the output width */ if( word_w_ptr != NULL && break_index == NO_BREAK_FOUND ) *word_w_ptr = cur_w; - if(letter_w > 0) { - cur_w += letter_space; - } i = i_next; i_next = i_next_next; diff --git a/src/libs/lvgl/src/lv_misc/lv_types.h b/src/libs/lvgl/src/lv_misc/lv_types.h index c588e245..2c28bca1 100644 --- a/src/libs/lvgl/src/lv_misc/lv_types.h +++ b/src/libs/lvgl/src/lv_misc/lv_types.h @@ -18,7 +18,7 @@ extern "C" { * DEFINES *********************/ // Check windows -#ifdef __WIN64 +#ifdef _WIN64 #define LV_ARCH_64 #endif diff --git a/src/libs/lvgl/src/lv_objx/lv_btnm.c b/src/libs/lvgl/src/lv_objx/lv_btnm.c index b54436d5..d8d5f700 100644 --- a/src/libs/lvgl/src/lv_objx/lv_btnm.c +++ b/src/libs/lvgl/src/lv_objx/lv_btnm.c @@ -815,7 +815,8 @@ static lv_res_t lv_btnm_signal(lv_obj_t * btnm, lv_signal_t sign, void * param) } else if(sign == LV_SIGNAL_RELEASED) { if(ext->btn_id_pr != LV_BTNM_BTN_NONE) { /*Toggle the button if enabled*/ - if(button_is_tgl_enabled(ext->ctrl_bits[ext->btn_id_pr])) { + if(button_is_tgl_enabled(ext->ctrl_bits[ext->btn_id_pr]) && + !button_is_inactive(ext->ctrl_bits[ext->btn_id_pr])) { if(button_get_tgl_state(ext->ctrl_bits[ext->btn_id_pr])) { ext->ctrl_bits[ext->btn_id_pr] &= (~LV_BTNM_CTRL_TGL_STATE); } else { @@ -863,9 +864,10 @@ static lv_res_t lv_btnm_signal(lv_obj_t * btnm, lv_signal_t sign, void * param) lv_indev_type_t indev_type = lv_indev_get_type(indev); /*If not focused by an input device assume the last input device*/ - if(indev_type == LV_INDEV_TYPE_NONE) { - indev_type = lv_indev_get_type(lv_indev_get_next(NULL)); - } + if(indev == NULL) { + indev = lv_indev_get_next(NULL); + indev_type = lv_indev_get_type(indev); + } if(indev_type == LV_INDEV_TYPE_POINTER) { /*Select the clicked button*/ @@ -1082,7 +1084,7 @@ static void invalidate_button_area(const lv_obj_t * btnm, uint16_t btn_idx) btn_area.x2 += btnm_area.x1; btn_area.y2 += btnm_area.y1; - lv_inv_area(lv_obj_get_disp(btnm), &btn_area); + lv_obj_invalidate_area(btnm, &btn_area); } /** diff --git a/src/libs/lvgl/src/lv_objx/lv_chart.c b/src/libs/lvgl/src/lv_objx/lv_chart.c index 5da3b848..2dfdef0e 100644 --- a/src/libs/lvgl/src/lv_objx/lv_chart.c +++ b/src/libs/lvgl/src/lv_objx/lv_chart.c @@ -816,7 +816,7 @@ static void lv_chart_draw_div(lv_obj_t * chart, const lv_area_t * mask) } p1.x = 0 + x_ofs; - p2.x = w + x_ofs; + p2.x = w - 1 + x_ofs; for(div_i = div_i_start; div_i <= div_i_end; div_i++) { p1.y = (int32_t)((int32_t)(h - style->line.width) * div_i) / (ext->hdiv_cnt + 1); p1.y += y_ofs; @@ -836,7 +836,7 @@ static void lv_chart_draw_div(lv_obj_t * chart, const lv_area_t * mask) } p1.y = 0 + y_ofs; - p2.y = h + y_ofs; + p2.y = h + y_ofs - 1; for(div_i = div_i_start; div_i <= div_i_end; div_i++) { p1.x = (int32_t)((int32_t)(w - style->line.width) * div_i) / (ext->vdiv_cnt + 1); p1.x += x_ofs; @@ -951,7 +951,7 @@ static void lv_chart_draw_points(lv_obj_t * chart, const lv_area_t * mask) y_tmp = (int32_t)((int32_t)ser->points[p_act] - ext->ymin) * h; y_tmp = y_tmp / (ext->ymax - ext->ymin); - cir_a.y1 = h - y_tmp + y_ofs; + cir_a.y1 = h - y_tmp + y_ofs - 1; cir_a.y2 = cir_a.y1 + style_point.body.radius; cir_a.y1 -= style_point.body.radius; @@ -1496,13 +1496,13 @@ static void lv_chart_inv_lines(lv_obj_t * chart, uint16_t i) if(i < ext->point_cnt - 1) { coords.x1 = ((w * i) / (ext->point_cnt - 1)) + x_ofs - ext->series.width; coords.x2 = ((w * (i + 1)) / (ext->point_cnt - 1)) + x_ofs + ext->series.width; - lv_inv_area(lv_obj_get_disp(chart), &coords); + lv_obj_invalidate_area(chart, &coords); } if(i > 0) { coords.x1 = ((w * (i - 1)) / (ext->point_cnt - 1)) + x_ofs - ext->series.width; coords.x2 = ((w * i) / (ext->point_cnt - 1)) + x_ofs + ext->series.width; - lv_inv_area(lv_obj_get_disp(chart), &coords); + lv_obj_invalidate_area(chart, &coords); } } } diff --git a/src/libs/lvgl/src/lv_objx/lv_cpicker.c b/src/libs/lvgl/src/lv_objx/lv_cpicker.c index fa574bf9..65826673 100644 --- a/src/libs/lvgl/src/lv_objx/lv_cpicker.c +++ b/src/libs/lvgl/src/lv_objx/lv_cpicker.c @@ -750,7 +750,7 @@ static void invalidate_indic(lv_obj_t * cpicker) { lv_area_t indic_area = get_indic_area(cpicker); - lv_inv_area(lv_obj_get_disp(cpicker), &indic_area); + lv_obj_invalidate_area(cpicker, &indic_area); } static lv_area_t get_indic_area(lv_obj_t * cpicker) diff --git a/src/libs/lvgl/src/lv_objx/lv_ddlist.c b/src/libs/lvgl/src/lv_objx/lv_ddlist.c index 27855d63..fb57cd2f 100644 --- a/src/libs/lvgl/src/lv_objx/lv_ddlist.c +++ b/src/libs/lvgl/src/lv_objx/lv_ddlist.c @@ -775,6 +775,7 @@ static lv_res_t lv_ddlist_scrl_signal(lv_obj_t * scrl, lv_signal_t sign, void * /* Include the ancient signal function */ res = ancestor_scrl_signal(scrl, sign, param); if(res != LV_RES_OK) return res; + if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, ""); lv_obj_t * ddlist = lv_obj_get_parent(scrl); @@ -806,6 +807,10 @@ static lv_res_t release_handler(lv_obj_t * ddlist) { lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + /*Only deal with clickable drop down lists*/ + if(!lv_obj_get_click(ddlist)) + return LV_RES_OK; + if(ext->opened == 0) { /*Open the list*/ ext->opened = 1; lv_obj_set_drag(lv_page_get_scrl(ddlist), true); diff --git a/src/libs/lvgl/src/lv_objx/lv_gauge.h b/src/libs/lvgl/src/lv_objx/lv_gauge.h index fb7cf366..408c1125 100644 --- a/src/libs/lvgl/src/lv_objx/lv_gauge.h +++ b/src/libs/lvgl/src/lv_objx/lv_gauge.h @@ -193,7 +193,7 @@ uint8_t lv_gauge_get_label_count(const lv_obj_t * gauge); * @param gauge pointer to a gauge object * @return number of the scale units */ -static inline uint8_t lv_gauge_get_line_count(const lv_obj_t * gauge) +static inline uint16_t lv_gauge_get_line_count(const lv_obj_t * gauge) { return lv_lmeter_get_line_count(gauge); } diff --git a/src/libs/lvgl/src/lv_objx/lv_img.c b/src/libs/lvgl/src/lv_objx/lv_img.c index 55e64e80..bf0269f9 100644 --- a/src/libs/lvgl/src/lv_objx/lv_img.c +++ b/src/libs/lvgl/src/lv_objx/lv_img.c @@ -355,6 +355,9 @@ static bool lv_img_design(lv_obj_t * img, const lv_area_t * mask, lv_design_mode if(ext->cf == LV_IMG_CF_TRUE_COLOR || ext->cf == LV_IMG_CF_RAW) cover = lv_area_is_in(mask, &img->coords); + const lv_style_t * style = lv_img_get_style(img, LV_IMG_STYLE_MAIN); + if(style->image.opa < LV_OPA_MAX) return false; + return cover; } else if(mode == LV_DESIGN_DRAW_MAIN) { if(ext->h == 0 || ext->w == 0) return true; diff --git a/src/libs/lvgl/src/lv_objx/lv_label.c b/src/libs/lvgl/src/lv_objx/lv_label.c index aa677b09..5c870d05 100644 --- a/src/libs/lvgl/src/lv_objx/lv_label.c +++ b/src/libs/lvgl/src/lv_objx/lv_label.c @@ -308,7 +308,6 @@ void lv_label_set_array_text(lv_obj_t * label, const char * array, uint16_t size void lv_label_set_static_text(lv_obj_t * label, const char * text) { LV_ASSERT_OBJ(label, LV_OBJX_NAME); - LV_ASSERT_STR(text); lv_label_ext_t * ext = lv_obj_get_ext_attr(label); if(ext->static_txt == 0 && ext->text != NULL) { @@ -1303,10 +1302,18 @@ static void lv_label_refr_text(lv_obj_t * label) p.y -= style->text.line_space; /*Trim the last line space*/ uint32_t letter_id = lv_label_get_letter_on(label, &p); - /*Save letters under the dots and replace them with dots*/ - uint32_t i; + + /*Be sure there is space for the dots*/ + size_t txt_len = strlen(ext->text); uint32_t byte_id = lv_txt_encoded_get_byte_id(ext->text, letter_id); + while(byte_id + LV_LABEL_DOT_NUM > txt_len) { + byte_id -= lv_txt_encoded_size(&ext->text[byte_id]); + letter_id--; + } + + /*Save letters under the dots and replace them with dots*/ uint32_t byte_id_ori = byte_id; + uint32_t i; uint8_t len = 0; for(i = 0; i <= LV_LABEL_DOT_NUM; i++) { len += lv_txt_encoded_size(&ext->text[byte_id]); diff --git a/src/libs/lvgl/src/lv_objx/lv_list.c b/src/libs/lvgl/src/lv_objx/lv_list.c index cf18e73f..54fe16a2 100644 --- a/src/libs/lvgl/src/lv_objx/lv_list.c +++ b/src/libs/lvgl/src/lv_objx/lv_list.c @@ -179,6 +179,13 @@ lv_obj_t * lv_list_add_btn(lv_obj_t * list, const void * img_src, const char * t { LV_ASSERT_OBJ(list, LV_OBJX_NAME); + lv_obj_t * last_btn = lv_list_get_prev_btn(list, NULL); + + /*The coordinates may changed due to autofit so revert them at the end*/ + lv_coord_t pos_x_ori = lv_obj_get_x(list); + lv_coord_t pos_y_ori = lv_obj_get_y(list); + + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); ext->size++; /*Create a list element with the image an the text*/ @@ -197,7 +204,22 @@ lv_obj_t * lv_list_add_btn(lv_obj_t * list, const void * img_src, const char * t lv_page_glue_obj(liste, true); lv_btn_set_layout(liste, LV_LAYOUT_ROW_M); - lv_btn_set_fit2(liste, LV_FIT_FLOOD, LV_FIT_TIGHT); + + lv_layout_t list_layout = lv_list_get_layout(list); + bool layout_ver = false; + if(list_layout == LV_LAYOUT_COL_M || list_layout == LV_LAYOUT_COL_L || list_layout == LV_LAYOUT_COL_R) { + layout_ver = true; + } + + if(layout_ver) { + lv_btn_set_fit2(liste, LV_FIT_FLOOD, LV_FIT_TIGHT); + } else { + lv_coord_t w = last_btn ? lv_obj_get_width(last_btn) : (LV_DPI * 3) / 2; + lv_btn_set_fit2(liste, LV_FIT_NONE, LV_FIT_TIGHT); + lv_obj_set_width(liste, w); + } + + lv_obj_set_protect(liste, LV_PROTECT_PRESS_LOST); lv_obj_set_signal_cb(liste, lv_list_btn_signal); @@ -233,6 +255,8 @@ lv_obj_t * lv_list_add_btn(lv_obj_t * list, const void * img_src, const char * t } #endif + lv_obj_set_pos(list, pos_x_ori, pos_y_ori); + return liste; } @@ -399,16 +423,23 @@ void lv_list_set_style(lv_obj_t * list, lv_list_style_t type, const lv_style_t * while(btn != NULL) { /*If a column layout set the buttons' width to list width*/ if(layout == LV_LAYOUT_COL_M || layout == LV_LAYOUT_COL_L || layout == LV_LAYOUT_COL_R) { - lv_btn_set_fit2(list, LV_FIT_FLOOD, LV_FIT_TIGHT); + lv_btn_set_fit2(btn, LV_FIT_FLOOD, LV_FIT_TIGHT); } /*If a row layout set the buttons' width according to the content*/ else if (layout == LV_LAYOUT_ROW_M || layout == LV_LAYOUT_ROW_T || layout == LV_LAYOUT_ROW_B) { - lv_btn_set_fit(list, LV_FIT_TIGHT); + lv_btn_set_fit(btn, LV_FIT_TIGHT); } btn = lv_list_get_prev_btn(list, btn); } + if(layout == LV_LAYOUT_COL_M || layout == LV_LAYOUT_COL_L || layout == LV_LAYOUT_COL_R) { + lv_page_set_scrl_fit2(list, LV_FIT_FLOOD, LV_FIT_TIGHT); + } else if (layout == LV_LAYOUT_ROW_M || layout == LV_LAYOUT_ROW_T || layout == LV_LAYOUT_ROW_B) { + lv_page_set_scrl_fit2(list, LV_FIT_TIGHT, LV_FIT_TIGHT); + lv_cont_set_fit2(list, LV_FIT_NONE, LV_FIT_TIGHT); + } + lv_page_set_scrl_layout(list, layout); } diff --git a/src/libs/lvgl/src/lv_objx/lv_page.c b/src/libs/lvgl/src/lv_objx/lv_page.c index b0d308d6..b39eab51 100644 --- a/src/libs/lvgl/src/lv_objx/lv_page.c +++ b/src/libs/lvgl/src/lv_objx/lv_page.c @@ -150,16 +150,16 @@ lv_obj_t * lv_page_create(lv_obj_t * par, const lv_obj_t * copy) ext->scrl = lv_cont_create(new_page, copy_ext->scrl); lv_obj_set_signal_cb(ext->scrl, lv_page_scrollable_signal); - lv_page_set_sb_mode(new_page, copy_ext->sb.mode); + /* Add the signal function only if 'scrolling' is created + * because everything has to be ready before any signal is received*/ + lv_obj_set_signal_cb(new_page, lv_page_signal); + lv_obj_set_design_cb(new_page, lv_page_design); lv_page_set_style(new_page, LV_PAGE_STYLE_BG, lv_page_get_style(copy, LV_PAGE_STYLE_BG)); lv_page_set_style(new_page, LV_PAGE_STYLE_SCRL, lv_page_get_style(copy, LV_PAGE_STYLE_SCRL)); lv_page_set_style(new_page, LV_PAGE_STYLE_SB, lv_page_get_style(copy, LV_PAGE_STYLE_SB)); - /* Add the signal function only if 'scrolling' is created - * because everything has to be ready before any signal is received*/ - lv_obj_set_signal_cb(new_page, lv_page_signal); - lv_obj_set_design_cb(new_page, lv_page_design); + lv_page_set_sb_mode(new_page, copy_ext->sb.mode); /*Refresh the style with new signal function*/ lv_obj_refresh_style(new_page); @@ -828,6 +828,7 @@ static lv_res_t lv_page_signal(lv_obj_t * page, lv_signal_t sign, void * param) lv_page_ext_t * ext = lv_obj_get_ext_attr(page); lv_obj_t * child; if(sign == LV_SIGNAL_CHILD_CHG) { /*Automatically move children to the scrollable object*/ + if(ext->scrl == NULL) return LV_RES_OK; const lv_style_t * style_bg = lv_page_get_style(page, LV_PAGE_STYLE_BG); const lv_style_t * style_scrl = lv_page_get_style(page, LV_PAGE_STYLE_SCRL); lv_fit_t fit_left = lv_page_get_scrl_fit_left(page); @@ -1073,7 +1074,6 @@ static lv_res_t lv_page_scrollable_signal(lv_obj_t * scrl, lv_signal_t sign, voi /*Hide scrollbars if required*/ if(page_ext->sb.mode == LV_SB_MODE_DRAG) { - lv_disp_t * disp = lv_obj_get_disp(page); lv_area_t sb_area_tmp; if(page_ext->sb.hor_draw) { lv_area_copy(&sb_area_tmp, &page_ext->sb.hor_area); @@ -1081,7 +1081,7 @@ static lv_res_t lv_page_scrollable_signal(lv_obj_t * scrl, lv_signal_t sign, voi sb_area_tmp.y1 += page->coords.y1; sb_area_tmp.x2 += page->coords.x1; sb_area_tmp.y2 += page->coords.y1; - lv_inv_area(disp, &sb_area_tmp); + lv_obj_invalidate_area(page, &sb_area_tmp); page_ext->sb.hor_draw = 0; } if(page_ext->sb.ver_draw) { @@ -1090,10 +1090,12 @@ static lv_res_t lv_page_scrollable_signal(lv_obj_t * scrl, lv_signal_t sign, voi sb_area_tmp.y1 += page->coords.y1; sb_area_tmp.x2 += page->coords.x1; sb_area_tmp.y2 += page->coords.y1; - lv_inv_area(disp, &sb_area_tmp); + lv_obj_invalidate_area(page, &sb_area_tmp); page_ext->sb.ver_draw = 0; } } + } else if(sign == LV_SIGNAL_CLEANUP) { + page_ext->scrl = NULL; } return res; @@ -1150,7 +1152,6 @@ static void lv_page_sb_refresh(lv_obj_t * page) } /*Invalidate the current (old) scrollbar areas*/ - lv_disp_t * disp = lv_obj_get_disp(page); lv_area_t sb_area_tmp; if(ext->sb.hor_draw != 0) { lv_area_copy(&sb_area_tmp, &ext->sb.hor_area); @@ -1158,7 +1159,7 @@ static void lv_page_sb_refresh(lv_obj_t * page) sb_area_tmp.y1 += page->coords.y1; sb_area_tmp.x2 += page->coords.x1; sb_area_tmp.y2 += page->coords.y1; - lv_inv_area(disp, &sb_area_tmp); + lv_obj_invalidate_area(page, &sb_area_tmp); } if(ext->sb.ver_draw != 0) { lv_area_copy(&sb_area_tmp, &ext->sb.ver_area); @@ -1166,7 +1167,7 @@ static void lv_page_sb_refresh(lv_obj_t * page) sb_area_tmp.y1 += page->coords.y1; sb_area_tmp.x2 += page->coords.x1; sb_area_tmp.y2 += page->coords.y1; - lv_inv_area(disp, &sb_area_tmp); + lv_obj_invalidate_area(page, &sb_area_tmp); } if(ext->sb.mode == LV_SB_MODE_DRAG && lv_indev_is_dragging(lv_indev_get_act()) == false) { @@ -1228,7 +1229,7 @@ static void lv_page_sb_refresh(lv_obj_t * page) sb_area_tmp.y1 += page->coords.y1; sb_area_tmp.x2 += page->coords.x1; sb_area_tmp.y2 += page->coords.y1; - lv_inv_area(disp, &sb_area_tmp); + lv_obj_invalidate_area(page, &sb_area_tmp); } if(ext->sb.ver_draw != 0) { lv_area_copy(&sb_area_tmp, &ext->sb.ver_area); @@ -1236,7 +1237,7 @@ static void lv_page_sb_refresh(lv_obj_t * page) sb_area_tmp.y1 += page->coords.y1; sb_area_tmp.x2 += page->coords.x1; sb_area_tmp.y2 += page->coords.y1; - lv_inv_area(disp, &sb_area_tmp); + lv_obj_invalidate_area(page, &sb_area_tmp); } } diff --git a/src/libs/lvgl/src/lv_objx/lv_roller.c b/src/libs/lvgl/src/lv_objx/lv_roller.c index 558610cd..4d21bbad 100644 --- a/src/libs/lvgl/src/lv_objx/lv_roller.c +++ b/src/libs/lvgl/src/lv_objx/lv_roller.c @@ -149,8 +149,9 @@ void lv_roller_set_options(lv_obj_t * roller, const char * options, lv_roller_mo /* Make sure the roller's height and the scrollable's height is refreshed. * They are refreshed in `LV_SIGNAL_COORD_CHG` but if the new options has the same width - * that signal won't be called. (It called because LV_FIT_TIGHT hor fit)*/ + * that signal won't be called. (It's called because of LV_FIT_TIGHT hor fit)*/ refr_height(roller); + refr_position(roller, LV_ANIM_OFF); } else { ext->mode = LV_ROLLER_MODE_INIFINITE; @@ -508,6 +509,7 @@ static lv_res_t lv_roller_scrl_signal(lv_obj_t * roller_scrl, lv_signal_t sign, /* Include the ancient signal function */ res = ancestor_scrl_signal(roller_scrl, sign, param); if(res != LV_RES_OK) return res; + if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME); lv_indev_t * indev = lv_indev_get_act(); int32_t id = -1; diff --git a/src/libs/lvgl/src/lv_objx/lv_spinbox.c b/src/libs/lvgl/src/lv_objx/lv_spinbox.c index 42c229c2..6873085a 100644 --- a/src/libs/lvgl/src/lv_objx/lv_spinbox.c +++ b/src/libs/lvgl/src/lv_objx/lv_spinbox.c @@ -405,11 +405,15 @@ static void lv_spinbox_updatevalue(lv_obj_t * spinbox) char buf[LV_SPINBOX_MAX_DIGIT_COUNT + 8]; memset(buf, 0, sizeof(buf)); char * buf_p = buf; + uint8_t cur_shift_left = 0; if (ext->range_min < 0) { // hide sign if there are only positive values /*Add the sign*/ (*buf_p) = ext->value >= 0 ? '+' : '-'; buf_p++; + } else { + /*Cursor need shift to left*/ + cur_shift_left++; } int32_t i; @@ -467,7 +471,7 @@ static void lv_spinbox_updatevalue(lv_obj_t * spinbox) if(cur_pos > intDigits) cur_pos++; /*Skip teh decimal point*/ - cur_pos += ext->digit_padding_left; + cur_pos += (ext->digit_padding_left - cur_shift_left); lv_ta_set_cursor_pos(spinbox, cur_pos); } diff --git a/src/libs/lvgl/src/lv_objx/lv_sw.c b/src/libs/lvgl/src/lv_objx/lv_sw.c index 956ed2ee..428a4af1 100644 --- a/src/libs/lvgl/src/lv_objx/lv_sw.c +++ b/src/libs/lvgl/src/lv_objx/lv_sw.c @@ -275,6 +275,13 @@ uint16_t lv_sw_get_anim_time(const lv_obj_t * sw) */ static lv_res_t lv_sw_signal(lv_obj_t * sw, lv_signal_t sign, void * param) { + lv_res_t res; + if(sign == LV_SIGNAL_GET_TYPE) { + res = ancestor_signal(sw, sign, param); + if(res != LV_RES_OK) return res; + return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME); + } + lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw); /*Save the current (old) value before slider signal modifies it. It will be required in the @@ -289,12 +296,9 @@ static lv_res_t lv_sw_signal(lv_obj_t * sw, lv_signal_t sign, void * param) lv_event_cb_t event_cb = sw->event_cb; sw->event_cb = NULL; - lv_res_t res; /* Include the ancient signal function */ - res = ancestor_signal(sw, sign, param); if(res != LV_RES_OK) return res; - if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME); sw->event_cb = event_cb; diff --git a/src/libs/lvgl/src/lv_objx/lv_ta.c b/src/libs/lvgl/src/lv_objx/lv_ta.c index b79f2ae8..387e99b2 100644 --- a/src/libs/lvgl/src/lv_objx/lv_ta.c +++ b/src/libs/lvgl/src/lv_objx/lv_ta.c @@ -484,7 +484,9 @@ void lv_ta_set_text(lv_obj_t * ta, const char * txt) if(lv_ta_get_accepted_chars(ta) || lv_ta_get_max_length(ta)) { lv_label_set_text(ext->label, ""); lv_ta_set_cursor_pos(ta, LV_TA_CURSOR_LAST); - + if(ext->pwd_mode != 0) { + ext->pwd_tmp[0] = '\0'; /*Clear the password too*/ + } uint32_t i = 0; while(txt[i] != '\0') { uint32_t c = lv_txt_encoded_next(txt, &i); @@ -731,6 +733,7 @@ void lv_ta_set_one_line(lv_obj_t * ta, bool en) lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); if(ext->one_line == en) return; + lv_label_align_t old_align = lv_label_get_align(ext->label); if(en) { const lv_style_t * style_ta = lv_obj_get_style(ta); @@ -758,7 +761,8 @@ void lv_ta_set_one_line(lv_obj_t * ta, bool en) } placeholder_update(ta); - refr_cursor_area(ta); + /* `refr_cursor_area` is called at the end of lv_ta_set_text_align */ + lv_ta_set_text_align(ta, old_align); } /** @@ -943,6 +947,7 @@ void lv_ta_set_cursor_blink_time(lv_obj_t * ta, uint16_t time) a.path_cb = lv_anim_path_step; lv_anim_create(&a); } else { + lv_anim_del(ta, (lv_anim_exec_xcb_t)cursor_blink_anim); ext->cursor.state = 1; } #else @@ -1589,14 +1594,13 @@ static void cursor_blink_anim(lv_obj_t * ta, lv_anim_value_t show) if(show != ext->cursor.state) { ext->cursor.state = show == 0 ? 0 : 1; if(ext->cursor.type != LV_CURSOR_NONE && (ext->cursor.type & LV_CURSOR_HIDDEN) == 0) { - lv_disp_t * disp = lv_obj_get_disp(ta); lv_area_t area_tmp; lv_area_copy(&area_tmp, &ext->cursor.area); area_tmp.x1 += ext->label->coords.x1; area_tmp.y1 += ext->label->coords.y1; area_tmp.x2 += ext->label->coords.x1; area_tmp.y2 += ext->label->coords.y1; - lv_inv_area(disp, &area_tmp); + lv_obj_invalidate_area(ta, &area_tmp); } } } @@ -1791,14 +1795,13 @@ static void refr_cursor_area(lv_obj_t * ta) } /*Save the new area*/ - lv_disp_t * disp = lv_obj_get_disp(ta); lv_area_t area_tmp; lv_area_copy(&area_tmp, &ext->cursor.area); area_tmp.x1 += ext->label->coords.x1; area_tmp.y1 += ext->label->coords.y1; area_tmp.x2 += ext->label->coords.x1; area_tmp.y2 += ext->label->coords.y1; - lv_inv_area(disp, &area_tmp); + lv_obj_invalidate_area(ta, &area_tmp); lv_area_copy(&ext->cursor.area, &cur_area); @@ -1807,7 +1810,7 @@ static void refr_cursor_area(lv_obj_t * ta) area_tmp.y1 += ext->label->coords.y1; area_tmp.x2 += ext->label->coords.x1; area_tmp.y2 += ext->label->coords.y1; - lv_inv_area(disp, &area_tmp); + lv_obj_invalidate_area(ta, &area_tmp); } static void placeholder_update(lv_obj_t * ta) diff --git a/src/libs/lvgl/src/lv_objx/lv_tabview.c b/src/libs/lvgl/src/lv_objx/lv_tabview.c index 79727b15..809cf241 100644 --- a/src/libs/lvgl/src/lv_objx/lv_tabview.c +++ b/src/libs/lvgl/src/lv_objx/lv_tabview.c @@ -114,8 +114,17 @@ lv_obj_t * lv_tabview_create(lv_obj_t * par, const lv_obj_t * copy) /* Set a size which fits into the parent. * Don't use `par` directly because if the tabview is created on a page it is moved to the * scrollable so the parent has changed */ - lv_obj_set_size(new_tabview, lv_obj_get_width_fit(lv_obj_get_parent(new_tabview)), - lv_obj_get_height_fit(lv_obj_get_parent(new_tabview))); + lv_coord_t w; + lv_coord_t h; + if(par) { + w = lv_obj_get_width_fit(lv_obj_get_parent(new_tabview)); + h = lv_obj_get_height_fit(lv_obj_get_parent(new_tabview)); + } else { + w = lv_disp_get_hor_res(NULL); + h = lv_disp_get_ver_res(NULL); + } + + lv_obj_set_size(new_tabview, w, h); ext->content = lv_cont_create(new_tabview, NULL); ext->btns = lv_btnm_create(new_tabview, NULL); @@ -914,12 +923,14 @@ static void tabpage_pressing_handler(lv_obj_t * tabview, lv_obj_t * tabpage) p = ((tabpage->coords.x1 - tabview->coords.x1) * (indic_size + tabs_style->body.padding.inner)) / lv_obj_get_width(tabview); - uint16_t id = ext->tab_cur; - if(lv_obj_get_base_dir(tabview) == LV_BIDI_DIR_RTL) { - id = (ext->tab_cnt - (id + 1)); + { + uint16_t id = ext->tab_cur; + if(lv_obj_get_base_dir(tabview) == LV_BIDI_DIR_RTL) { + id = (ext->tab_cnt - (id + 1)); + } + lv_obj_set_x(ext->indic, indic_size * id + tabs_style->body.padding.inner * id + + indic_style->body.padding.left - p); } - lv_obj_set_x(ext->indic, indic_size * id + tabs_style->body.padding.inner * id + - indic_style->body.padding.left - p); break; case LV_TABVIEW_BTNS_POS_LEFT: case LV_TABVIEW_BTNS_POS_RIGHT: diff --git a/src/libs/lvgl/src/lv_objx/lv_tileview.c b/src/libs/lvgl/src/lv_objx/lv_tileview.c index 184bcbd5..3b2f8514 100644 --- a/src/libs/lvgl/src/lv_objx/lv_tileview.c +++ b/src/libs/lvgl/src/lv_objx/lv_tileview.c @@ -97,8 +97,17 @@ lv_obj_t * lv_tileview_create(lv_obj_t * par, const lv_obj_t * copy) /* Set a size which fits into the parent. * Don't use `par` directly because if the tileview is created on a page it is moved to the * scrollable so the parent has changed */ - lv_obj_set_size(new_tileview, lv_obj_get_width_fit(lv_obj_get_parent(new_tileview)), - lv_obj_get_height_fit(lv_obj_get_parent(new_tileview))); + lv_coord_t w; + lv_coord_t h; + if(par) { + w = lv_obj_get_width_fit(lv_obj_get_parent(new_tileview)); + h = lv_obj_get_height_fit(lv_obj_get_parent(new_tileview)); + } else { + w = lv_disp_get_hor_res(NULL); + h = lv_disp_get_ver_res(NULL); + } + + lv_obj_set_size(new_tileview, w, h); lv_obj_set_drag_throw(lv_page_get_scrl(new_tileview), false); lv_page_set_scrl_fit(new_tileview, LV_FIT_TIGHT); @@ -216,6 +225,7 @@ void lv_tileview_set_tile_act(lv_obj_t * tileview, lv_coord_t x, lv_coord_t y, l for(tile_id = 0; tile_id < ext->valid_pos_cnt; tile_id++) { if(ext->valid_pos[tile_id].x == x && ext->valid_pos[tile_id].y == y) { valid = true; + break; } } @@ -486,9 +496,9 @@ static void tileview_scrl_event_cb(lv_obj_t * scrl, lv_event_t event) lv_tileview_ext_t * ext = lv_obj_get_ext_attr(tileview); if(lv_indev_is_dragging(indev) && (ext->drag_hor || ext->drag_ver)) { indev->proc.types.pointer.drag_in_prog = 0; + drag_end_handler(tileview); } - drag_end_handler(tileview); } } diff --git a/src/libs/lvgl/src/lv_objx/lv_win.c b/src/libs/lvgl/src/lv_objx/lv_win.c index 03c689a0..689dad37 100644 --- a/src/libs/lvgl/src/lv_objx/lv_win.c +++ b/src/libs/lvgl/src/lv_objx/lv_win.c @@ -75,10 +75,18 @@ lv_obj_t * lv_win_create(lv_obj_t * par, const lv_obj_t * copy) /* Set a size which fits into the parent. * Don't use `par` directly because if the window is created on a page it is moved to the * scrollable so the parent has changed */ - lv_obj_set_size(new_win, lv_obj_get_width_fit(lv_obj_get_parent(new_win)), - lv_obj_get_height_fit(lv_obj_get_parent(new_win))); + lv_coord_t w; + lv_coord_t h; + if(par) { + w = lv_obj_get_width_fit(lv_obj_get_parent(new_win)); + h = lv_obj_get_height_fit(lv_obj_get_parent(new_win)); + } else { + w = lv_disp_get_hor_res(NULL); + h = lv_disp_get_ver_res(NULL); + } + + lv_obj_set_size(new_win, w, h); - lv_obj_set_pos(new_win, 0, 0); lv_obj_set_style(new_win, &lv_style_pretty); ext->page = lv_page_create(new_win, NULL); diff --git a/src/libs/mynewt-nimble/.gitignore b/src/libs/mynewt-nimble/.gitignore new file mode 100644 index 00000000..bcc3a72b --- /dev/null +++ b/src/libs/mynewt-nimble/.gitignore @@ -0,0 +1,5 @@ +# Dummy NPL build +*.o +/porting/examples/dummy/dummy +/porting/examples/linux/nimble-linux +/porting/examples/linux_blemesh/nimble-linux-blemesh diff --git a/src/libs/mynewt-nimble/.rat-excludes b/src/libs/mynewt-nimble/.rat-excludes new file mode 100644 index 00000000..672c0e7d --- /dev/null +++ b/src/libs/mynewt-nimble/.rat-excludes @@ -0,0 +1,30 @@ +# Can't easily add license to rat-excludes file. +.rat-excludes + +# Ignore documentation folder +docs + +# Non-source files +RELEASE_NOTES.md +.gitignore +README.md +pts-gap.txt +pts-gatt.txt +pts-l2cap.txt +pts-sm.txt +94654-20170317-085122560.tpg +94654-20170317-085441153.pts +uncrustify.cfg +.style_ignored_dirs + +# tinycrypt - BSD License. +tinycrypt + +# Bluetooth Mesh - Apache 2.0 License +mesh + +# Queue implementation - BSD License +queue.h + +# mbuf implementation - BSD License +os_mbuf.c diff --git a/src/libs/mynewt-nimble/.style_ignored_dirs b/src/libs/mynewt-nimble/.style_ignored_dirs new file mode 100644 index 00000000..4be8efa0 --- /dev/null +++ b/src/libs/mynewt-nimble/.style_ignored_dirs @@ -0,0 +1,4 @@ +# Skip those directories while doing style checks in the CI. Do not add '/' at +# the beginning! + +ext/tinycrypt diff --git a/src/libs/mynewt-nimble/.travis.yml b/src/libs/mynewt-nimble/.travis.yml new file mode 100644 index 00000000..3c1a5ef9 --- /dev/null +++ b/src/libs/mynewt-nimble/.travis.yml @@ -0,0 +1,167 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +language: go + +_addons: &addon_conf + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - gcc-multilib + - gcc-7-multilib + +go: + - "1.12" + +git: + depth: false + +matrix: + include: + # Style checking + - os: linux + language: python + python: + - "3.5" + addons: + apt: + packages: + - "python3-pip" + env: + - TEST=STYLE + - DEBUG=1 + + # newt build + - os: linux + addons: *addon_conf + env: + - TEST=BUILD_TARGETS + - VM_AMOUNT=4 + - TARGET_SET=1 + - os: linux + addons: *addon_conf + env: + - TEST=BUILD_TARGETS + - VM_AMOUNT=4 + - TARGET_SET=2 + - os: linux + addons: *addon_conf + env: + - TEST=BUILD_TARGETS + - VM_AMOUNT=4 + - TARGET_SET=3 + - os: linux + addons: *addon_conf + env: + - TEST=BUILD_TARGETS + - VM_AMOUNT=4 + - TARGET_SET=4 + + # newt test all (Linux) + - os: linux + addons: *addon_conf + env: + - TEST=TEST_ALL + - VM_AMOUNT=2 + - TARGET_SET=1 + - os: linux + addons: *addon_conf + env: + - TEST=TEST_ALL + - VM_AMOUNT=2 + - TARGET_SET=2 + + # ports + - os: linux + addons: *addon_conf + env: + - TEST=BUILD_PORTS + - VM_AMOUNT=1 + - TARGET_SET=1 + + # newt test all + - os: osx + osx_image: xcode9.2 + env: + - TEST=TEST_ALL + - VM_AMOUNT=3 + - TARGET_SET=1 + - os: osx + osx_image: xcode9.2 + env: + - TEST=TEST_ALL + - VM_AMOUNT=3 + - TARGET_SET=2 + - os: osx + osx_image: xcode9.2 + env: + - TEST=TEST_ALL + - VM_AMOUNT=3 + - TARGET_SET=3 + - os: windows + env: + - TEST=BUILD_TARGETS_WINDOWS + - VM_AMOUNT=1 + - TARGET_SET=1 + +before_install: + - printenv + - export GOPATH=$HOME/gopath + - go version + +install: + - git clone https://github.com/JuulLabs-OSS/mynewt-travis-ci $HOME/ci + - chmod +x $HOME/ci/*.sh + - | + if [ "${TEST}" == "STYLE" ]; then + pip3 install requests + else + $HOME/ci/${TRAVIS_OS_NAME}_travis_install.sh + fi + +before_script: + - | + if [ "${TEST}" == "STYLE" ]; then + $HOME/ci/install_uncrustify.sh + else + newt version + gcc --version + if [ "${TEST}" != "TEST_ALL" ]; then arm-none-eabi-gcc --version; fi + cp -R $HOME/ci/mynewt-nimble-project.yml project.yml + mkdir -p targets + cp -R $HOME/ci/mynewt-nimble-targets targets + $HOME/ci/prepare_test.sh $VM_AMOUNT + mkdir -p repos && pushd repos/ + git clone --depth=1 https://github.com/apache/mynewt-core apache-mynewt-core + git clone --depth=1 https://github.com/JuulLabs-OSS/mcuboot mcuboot + git clone --depth=1 https://github.com/apache/mynewt-mcumgr apache-mynewt-mcumgr + popd + fi + +script: + - | + if [ "${TEST}" == "STYLE" ]; then + python3 $HOME/ci/check_style.py + else + $HOME/ci/run_test.sh + fi + +cache: + directories: + - $HOME/TOOLCHAIN + - $HOME/Library/Caches/Homebrew diff --git a/src/libs/mynewt-nimble/CODING_STANDARDS.md b/src/libs/mynewt-nimble/CODING_STANDARDS.md new file mode 100644 index 00000000..d14b9fdb --- /dev/null +++ b/src/libs/mynewt-nimble/CODING_STANDARDS.md @@ -0,0 +1,267 @@ +# Coding Style for Apache NimBLE + +Apache NimBLE project is part of Apache Mynewt projct and follows its coding +style. + +# Coding Style for Apache Mynewt Core + +This document is meant to define the coding style for Apache Mynewt, and +all subprojects of Apache Mynewt. This covers C and Assembly coding +conventions, *only*. Other languages (such as Go), have their own +coding conventions. + +## Headers + +* All files that are newly written, should have the Apache License clause +at the top of them. + +* For files that are copied from another source, but contain an Apache +compatible license, the original license header shall be maintained. + +* For more information on applying the Apache license, the definitive +source is here: http://www.apache.org/dev/apply-license.html + +* The Apache License clause for the top of files is as follows: + +```no-highlight +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +``` + +## Whitespace and Braces + +* Code must be indented to 4 spaces, tabs should not be used. + +* Do not add whitespace at the end of a line. + +* Put space after keywords (for, if, return, switch, while). + +* for, else, if, while statements must have braces around their +code blocks, i.e., do: + +``` + if (x) { + assert(0); + } else { + assert(0); + } +``` + +Not: + +``` + if (x) + assert(0); + else + assert(0); +``` + +* Braces for statements must be on the same line as the statement. Good: + +``` + for (i = 0; i < 10; i++) { + if (i == 5) { + break; + } else { + continue; + } + } +``` + +Not: + +``` + for (i = 0; i < 10; i++) + { <-- brace must be on same line as for + if (i == 5) { + break; + } <-- no new line between else + else { + continue; + } + } +``` + +* After a function declaration, the braces should be on a newline, i.e. do: + +``` + static void * + function(int var1, int var2) + { +``` + +not: + +``` + static void * + function(int var1, int var2) { +``` + +## Line Length and Wrap + +* Line length should never exceed 79 columns. + +* When you have to wrap a long statement, put the operator at the end of the + line. i.e.: + +``` + if (x && + y == 10 && + b) +``` + +Not: + +``` + if (x + && y == 10 + && b) +``` + +## Comments + +* No C++ style comments allowed. + +* When using a single line comment, put it above the line of code that you +intend to comment, i.e., do: + +``` + /* check variable */ + if (a) { +``` + +Not: + +``` + if (a) { /* check variable */ +``` + + +* All public APIs should be commented with Doxygen style comments describing +purpose, parameters and return values. Private APIs need not be documented. + + +## Header files + +* Header files must contain the following structure: + * Apache License (see above) + * ```#ifdef``` aliasing, to prevent multiple includes + * ```#include``` directives for other required header files + * ```#ifdef __cplusplus``` wrappers to maintain C++ friendly APIs + * Contents of the header file + +* ```#ifdef``` aliasing, shall be in the following format, where +the package name is "os" and the file name is "callout.h": + +```no-highlight +#ifndef _OS_CALLOUT_H +#define _OS_CALLOUT_H +``` + +* ```#include``` directives must happen prior to the cplusplus +wrapper. + +* The cplusplus wrapper must have the following format, and precedes +any contents of the header file: + +```no-highlight +#ifdef __cplusplus +#extern "C" { +##endif +``` + +## Naming + +* Names of functions, structures and variables must be in all lowercase. + +* Names should be as short as possible, but no shorter. + +* Globally visible names must be prefixed with the name of the module, +followed by the '_' character, i.e.: + +``` + os_callout_init(&c) +``` + +Not: + +``` + callout_init(c) +``` + +## Functions + +* No spaces after function names when calling a function, i.e, do: + +``` + rc = function(a) +``` + +Not: + +``` + rc = function (a) +``` + + +* Arguments to function calls should have spaces between the comma, i.e. do: + +``` + rc = function(a, b) +``` + +Not: + +``` + rc = function(a,b) +``` + +* The function type must be on a line by itself preceding the function, i.e. do: + +``` + static void * + function(int var1, int var2) + { +``` + +Not: + +``` + static void *function(int var1, int var2) + { +``` + +* In general, for functions that return values that denote success or error, 0 +shall be success, and non-zero shall be the failure code. + +## Variables and Macros + +* Do not use typedefs for structures. This makes it impossible for +applications to use pointers to those structures opaquely. + +* typedef may be used for non-structure types, where it is beneficial to +hide or alias the underlying type used (e.g. ```os_time_t```.) Indicate +typedefs by applying the ```_t``` marker to them. + +* Place all function-local variable definitions at the top of the function body, before any statements. + +## Compiler Directives + +* Code must compile cleanly with -Wall enabled. + diff --git a/src/libs/mynewt-nimble/LICENSE b/src/libs/mynewt-nimble/LICENSE new file mode 100644 index 00000000..08b9b218 --- /dev/null +++ b/src/libs/mynewt-nimble/LICENSE @@ -0,0 +1,217 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +This product bundles queue.h 8.5, which is available under the "3-clause BSD" +license. For details, see porting/nimble/include/os/queue.h + +This product partly derives from FreeBSD, which is available under the +"3-clause BSD" license. For details, see: + * porting/nimble/src/os_mbuf.c + +This product bundles Gary S. Brown's CRC32 implementation, which is available +under the following license: + COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + code or tables extracted from it, as desired without restriction. + +This product bundles tinycrypt, which is available under the "3-clause BSD" +license. For details, and bundled files see: + * ext/tinycrypt/LICENSE diff --git a/src/libs/mynewt-nimble/NOTICE b/src/libs/mynewt-nimble/NOTICE new file mode 100644 index 00000000..84ecb14d --- /dev/null +++ b/src/libs/mynewt-nimble/NOTICE @@ -0,0 +1,8 @@ +Apache Mynewt NimBLE +Copyright 2015-2020 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +Portions of this software were developed at +Runtime Inc, copyright 2015. diff --git a/src/libs/mynewt-nimble/README.md b/src/libs/mynewt-nimble/README.md new file mode 100644 index 00000000..37103be0 --- /dev/null +++ b/src/libs/mynewt-nimble/README.md @@ -0,0 +1,169 @@ + + +Apache Mynewt + +## Overview + +Apache NimBLE is an open-source Bluetooth 5.1 stack (both Host & Controller) +that completely replaces the proprietary SoftDevice on Nordic chipsets. It is +part of [Apache Mynewt project](https://github.com/apache/mynewt-core). + +Features highlight: + - Support for 251 byte packet size + - Support for all 4 roles concurrently - Broadcaster, Observer, Peripheral and Central + - Support for up to 32 simultaneous connections. + - Legacy and SC (secure connections) SMP support (pairing and bonding). + - Advertising Extensions. + - Coded (aka Long Range) and 2M PHYs. + - Bluetooth Mesh. + +## Supported hardware + +Controller supports Nordic nRF51 and nRF52 chipsets. Host runs on any board +and architecture [supported](https://github.com/apache/mynewt-core#overview) +by Apache Mynewt OS. + + +## Browsing + +If you are browsing around the source tree, and want to see some of the +major functional chunks, here are a few pointers: + +- nimble/controller: Contains code for controller including Link Layer and HCI implementation +([controller](https://github.com/apache/mynewt-nimble/tree/master/nimble/controller)) + +- nimble/drivers: Contains drivers for supported radio transceivers (Nordic nRF51 and nRF52) +([drivers](https://github.com/apache/mynewt-nimble/tree/master/nimble/drivers)) + +- nimble/host: Contains code for host subsystem. This includes protocols like +L2CAP and ATT, support for HCI commands and events, Generic Access Profile (GAP), +Generic Attribute Profile (GATT) and Security Manager (SM). +([host](https://github.com/apache/mynewt-nimble/tree/master/nimble/host)) + +- nimble/host/mesh: Contains code for Bluetooth Mesh subsystem. +([mesh](https://github.com/apache/mynewt-nimble/tree/master/nimble/host/mesh)) + +- nimble/transport: Contains code for supported transport protocols between host +and controller. This includes UART, emSPI and RAM (used in combined build when +host and controller run on same CPU) +([transport](https://github.com/apache/mynewt-nimble/tree/master/nimble/transport)) + +- porting: Contains implementation of NimBLE Porting Layer (NPL) for supported +operating systems +([porting](https://github.com/apache/mynewt-nimble/tree/master/porting)) + +- ext: Contains external libraries used by NimBLE. Those are used if not +provided by OS +([ext](https://github.com/apache/mynewt-nimble/tree/master/ext)) + +- kernel: Contains the core of the RTOS ([kernel/os](https://github.com/apache/mynewt-core/tree/master/kernel/os)) + +## Sample Applications + +There are also some sample applications that show how to Apache Mynewt NimBLE +stack. These sample applications are located in the `apps/` directory of +Apache Mynewt [repo](https://github.com/apache/mynewt-core). Some examples: + +* [blecent](https://github.com/apache/mynewt-nimble/tree/master/apps/blecent): +A basic central device with no user interface. This application scans for +a peripheral that supports the alert notification service (ANS). Upon +discovering such a peripheral, blecent connects and performs a characteristic +read, characteristic write, and notification subscription. +* [blehci](https://github.com/apache/mynewt-nimble/tree/master/apps/blehci): +Implements a BLE controller-only application. A separate host-only +implementation, such as Linux's BlueZ, can interface with this application via +HCI over UART. +* [bleprph](https://github.com/apache/mynewt-nimble/tree/master/apps/bleprph): An + implementation of a minimal BLE peripheral. +* [btshell](https://github.com/apache/mynewt-nimble/tree/master/apps/btshell): A + shell-like application allowing to configure and use most of NimBLE + functionality from command line. +* [bleuart](https://github.com/apache/mynewt-core/tree/master/apps/bleuart): +Implements a simple BLE peripheral that supports the Nordic +UART / Serial Port Emulation service +(https://developer.nordicsemi.com/nRF5_SDK/nRF51_SDK_v8.x.x/doc/8.0.0/s110/html/a00072.html). + +# Getting Help + +If you are having trouble using or contributing to Apache Mynewt NimBLE, or just +want to talk to a human about what you're working on, you can contact us via the +[developers mailing list](mailto:dev@mynewt.apache.org). + +Although not a formal channel, you can also find a number of core developers +on the #mynewt channel on Freenode IRC or #general channel on [Mynewt Slack](https://mynewt.slack.com/join/shared_invite/enQtNjA1MTg0NzgyNzg3LTcyMmZiOGQzOGMxM2U4ODFmMTIwNjNmYTE5Y2UwYjQwZWIxNTE0MTUzY2JmMTEzOWFjYWZkNGM0YmM4MzAxNWQ) + +Also, be sure to checkout the [Frequently Asked Questions](https://mynewt.apache.org/faq/answers) +for some help troubleshooting first. + +# Contributing + +Anybody who works with Apache Mynewt can be a contributing member of the +community that develops and deploys it. The process of releasing an operating +system for microcontrollers is never done: and we welcome your contributions +to that effort. + +More information can be found at the Community section of the Apache Mynewt +website, located [here](https://mynewt.apache.org/community). + +## Pull Requests + +Apache Mynewt welcomes pull request via Github. Discussions are done on Github, +but depending on the topic, can also be relayed to the official Apache Mynewt +developer mailing list dev@mynewt.apache.org. + +If you are suggesting a new feature, please email the developer list directly, +with a description of the feature you are planning to work on. + +## Filing Bugs + +Bugs can be filed on the +[Apache Mynewt NimBLE Issues](https://github.com/apache/mynewt-nimble/issues). +Please label the issue as a "Bug". + +Where possible, please include a self-contained reproduction case! + +## Feature Requests + +Feature requests should also be filed on the +[Apache Mynewt NimBLE Bug Tracker](https://github.com/apache/mynewt-nimble/issues). +Please label the issue as a "Feature" or "Enhancement" depending on the scope. + +## Writing Tests + +We love getting newt tests! Apache Mynewt is a huge undertaking, and improving +code coverage is a win for every Apache Mynewt user. + + + +# License + +The code in this repository is all under either the Apache 2 license, or a +license compatible with the Apache 2 license. See the LICENSE file for more +information. diff --git a/src/libs/mynewt-nimble/RELEASE_NOTES.md b/src/libs/mynewt-nimble/RELEASE_NOTES.md new file mode 100644 index 00000000..3bdd31ad --- /dev/null +++ b/src/libs/mynewt-nimble/RELEASE_NOTES.md @@ -0,0 +1,33 @@ +# RELEASE NOTES + +18 March 2020 - Apache NimBLE v1.3.0 + +For full release notes, please visit the +[Apache Mynewt Wiki](https://cwiki.apache.org/confluence/display/MYNEWT/Release+Notes). + +Apache NimBLE is an open-source Bluetooth 5.1 stack (both Host & Controller) that completely +replaces the proprietary SoftDevice on Nordic chipsets. + +New features in this version of NimBLE include: + +* Support for Bluetooth Core Specification 5.1 +* New blestress test application +* Dialog DA1469x CMAC driver +* Support for LE Secure Connections out-of-band (OOB) association model +* Support for automated generation of syscfg for ports +* Qualification related bugfixes +* BLE Mesh improvements - fixes and resync with latest Zephyr code +* RIOT OS port fixes and improvements +* btshell sample application improvements +* improvements for bttester application +* Controller duplicates filtering improvements +* Multi PHY support improvements +* Memory and CPU usage optimizations +* Use of packed structs for HCI (code size reduction) +* Linux sample improvements +* PTS test instructions updates +* Clock managements improvements in controller + +If working on next-generation RTOS and Bluetooth protocol stack +sounds exciting to you, get in touch, by sending a mail to the Apache Mynewt +Developer's list, dev@mynewt.apache.org. diff --git a/src/libs/mynewt-nimble/apps/advertiser/pkg.yml b/src/libs/mynewt-nimble/apps/advertiser/pkg.yml new file mode 100644 index 00000000..662e282e --- /dev/null +++ b/src/libs/mynewt-nimble/apps/advertiser/pkg.yml @@ -0,0 +1,33 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: "apps/advertiser" +pkg.type: app +pkg.description: "Basic advertiser application" +pkg.author: "Krzysztof Kopyściński " + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-nimble/nimble/host/util" + - "@apache-mynewt-nimble/nimble/host/services/gap" + - "@apache-mynewt-nimble/nimble/transport" diff --git a/src/libs/mynewt-nimble/apps/advertiser/src/main.c b/src/libs/mynewt-nimble/apps/advertiser/src/main.c new file mode 100644 index 00000000..486d5c59 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/advertiser/src/main.c @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include + +#include "os/os.h" +#include "sysinit/sysinit.h" +#include "log/log.h" +#include "host/ble_hs.h" +#include "host/util/util.h" +#include "services/gap/ble_svc_gap.h" + +static const char *device_name = "Apache Mynewt"; + +/* adv_event() calls advertise(), so forward declaration is required */ +static void advertise(void); + +static void +set_ble_addr(void) +{ + int rc; + ble_addr_t addr; + + /* generate new non-resolvable private address */ + rc = ble_hs_id_gen_rnd(1, &addr); + assert(rc == 0); + + /* set generated address */ + rc = ble_hs_id_set_rnd(addr.val); + assert(rc == 0); +} + +static int +adv_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_ADV_COMPLETE: + MODLOG_DFLT(INFO, "Advertising completed, termination code: %d\n", + event->adv_complete.reason); + advertise(); + return 0; + default: + MODLOG_DFLT(ERROR, "Advertising event not handled\n"); + return 0; + } +} + +static void +advertise(void) +{ + int rc; + struct ble_gap_adv_params adv_params; + struct ble_hs_adv_fields fields; + + /* set adv parameters */ + memset(&adv_params, 0, sizeof(adv_params)); + adv_params.conn_mode = BLE_GAP_CONN_MODE_NON; + adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + + memset(&fields, 0, sizeof(fields)); + + /* Fill the fields with advertising data - flags, tx power level, name */ + fields.flags = BLE_HS_ADV_F_DISC_GEN; + fields.tx_pwr_lvl_is_present = 1; + fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + fields.name = (uint8_t *)device_name; + fields.name_len = strlen(device_name); + fields.name_is_complete = 1; + + rc = ble_gap_adv_set_fields(&fields); + assert(rc == 0); + + MODLOG_DFLT(INFO, "Starting advertising...\n"); + + /* As own address type we use hard-coded value, because we generate + NRPA and by definition it's random */ + rc = ble_gap_adv_start(BLE_OWN_ADDR_RANDOM, NULL, 10000, + &adv_params, adv_event, NULL); + assert(rc == 0); +} + +static void +on_sync(void) +{ + set_ble_addr(); + + /* begin advertising */ + advertise(); +} + +static void +on_reset(int reason) +{ + MODLOG_DFLT(INFO, "Resetting state; reason=%d\n", reason); +} + +int +main(int argc, char **argv) +{ + int rc; + + /* Initialize all packages. */ + sysinit(); + + ble_hs_cfg.sync_cb = on_sync; + ble_hs_cfg.reset_cb = on_reset; + + rc = ble_svc_gap_device_name_set(device_name); + assert(rc == 0); + + /* As the last thing, process events from default event queue. */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + + return 0; +} diff --git a/src/libs/mynewt-nimble/apps/blecent/pkg.yml b/src/libs/mynewt-nimble/apps/blecent/pkg.yml new file mode 100644 index 00000000..dd574395 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blecent/pkg.yml @@ -0,0 +1,36 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: apps/blecent +pkg.type: app +pkg.description: Simple BLE central application. +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-core/sys/stats/full" + - nimble/host + - nimble/host/util + - nimble/host/services/gap + - nimble/host/services/gatt + - nimble/host/store/ram + - nimble/transport diff --git a/src/libs/mynewt-nimble/apps/blecent/src/blecent.h b/src/libs/mynewt-nimble/apps/blecent/src/blecent.h new file mode 100644 index 00000000..a694f402 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blecent/src/blecent.h @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLECENT_ +#define H_BLECENT_ + +#include "os/mynewt.h" +#include "modlog/modlog.h" +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_hs_adv_fields; +struct ble_gap_conn_desc; +struct ble_hs_cfg; +union ble_store_value; +union ble_store_key; + +#define BLECENT_SVC_ALERT_UUID 0x1811 +#define BLECENT_CHR_SUP_NEW_ALERT_CAT_UUID 0x2A47 +#define BLECENT_CHR_NEW_ALERT 0x2A46 +#define BLECENT_CHR_SUP_UNR_ALERT_CAT_UUID 0x2A48 +#define BLECENT_CHR_UNR_ALERT_STAT_UUID 0x2A45 +#define BLECENT_CHR_ALERT_NOT_CTRL_PT 0x2A44 + +/** Misc. */ +void print_bytes(const uint8_t *bytes, int len); +void print_mbuf(const struct os_mbuf *om); +char *addr_str(const void *addr); +void print_uuid(const ble_uuid_t *uuid); +void print_conn_desc(const struct ble_gap_conn_desc *desc); +void print_adv_fields(const struct ble_hs_adv_fields *fields); + +/** Peer. */ +struct peer_dsc { + SLIST_ENTRY(peer_dsc) next; + struct ble_gatt_dsc dsc; +}; +SLIST_HEAD(peer_dsc_list, peer_dsc); + +struct peer_chr { + SLIST_ENTRY(peer_chr) next; + struct ble_gatt_chr chr; + + struct peer_dsc_list dscs; +}; +SLIST_HEAD(peer_chr_list, peer_chr); + +struct peer_svc { + SLIST_ENTRY(peer_svc) next; + struct ble_gatt_svc svc; + + struct peer_chr_list chrs; +}; +SLIST_HEAD(peer_svc_list, peer_svc); + +struct peer; +typedef void peer_disc_fn(const struct peer *peer, int status, void *arg); + +struct peer { + SLIST_ENTRY(peer) next; + + uint16_t conn_handle; + + /** List of discovered GATT services. */ + struct peer_svc_list svcs; + + /** Keeps track of where we are in the service discovery process. */ + uint16_t disc_prev_chr_val; + struct peer_svc *cur_svc; + + /** Callback that gets executed when service discovery completes. */ + peer_disc_fn *disc_cb; + void *disc_cb_arg; +}; + +int peer_disc_all(uint16_t conn_handle, peer_disc_fn *disc_cb, + void *disc_cb_arg); +const struct peer_dsc * +peer_dsc_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid, + const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid); +const struct peer_chr * +peer_chr_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid, + const ble_uuid_t *chr_uuid); +const struct peer_svc * +peer_svc_find_uuid(const struct peer *peer, const ble_uuid_t *uuid); +int peer_delete(uint16_t conn_handle); +int peer_add(uint16_t conn_handle); +int peer_init(int max_peers, int max_svcs, int max_chrs, int max_dscs); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libs/mynewt-nimble/apps/blecent/src/main.c b/src/libs/mynewt-nimble/apps/blecent/src/main.c new file mode 100644 index 00000000..788f2115 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blecent/src/main.c @@ -0,0 +1,525 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include "os/mynewt.h" +#include "bsp/bsp.h" + +/* BLE */ +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "host/util/util.h" + +/* Mandatory services. */ +#include "services/gap/ble_svc_gap.h" +#include "services/gatt/ble_svc_gatt.h" + +/* Application-specified header. */ +#include "blecent.h" + +static int blecent_gap_event(struct ble_gap_event *event, void *arg); + +/** + * Application callback. Called when the read of the ANS Supported New Alert + * Category characteristic has completed. + */ +static int +blecent_on_read(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) +{ + MODLOG_DFLT(INFO, "Read complete; status=%d conn_handle=%d", error->status, + conn_handle); + if (error->status == 0) { + MODLOG_DFLT(INFO, " attr_handle=%d value=", attr->handle); + print_mbuf(attr->om); + } + MODLOG_DFLT(INFO, "\n"); + + return 0; +} + +/** + * Application callback. Called when the write to the ANS Alert Notification + * Control Point characteristic has completed. + */ +static int +blecent_on_write(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) +{ + MODLOG_DFLT(INFO, + "Write complete; status=%d conn_handle=%d attr_handle=%d\n", + error->status, conn_handle, attr->handle); + + return 0; +} + +/** + * Application callback. Called when the attempt to subscribe to notifications + * for the ANS Unread Alert Status characteristic has completed. + */ +static int +blecent_on_subscribe(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) +{ + MODLOG_DFLT(INFO, "Subscribe complete; status=%d conn_handle=%d " + "attr_handle=%d\n", + error->status, conn_handle, attr->handle); + + return 0; +} + +/** + * Performs three concurrent GATT operations against the specified peer: + * 1. Reads the ANS Supported New Alert Category characteristic. + * 2. Writes the ANS Alert Notification Control Point characteristic. + * 3. Subscribes to notifications for the ANS Unread Alert Status + * characteristic. + * + * If the peer does not support a required service, characteristic, or + * descriptor, then the peer lied when it claimed support for the alert + * notification service! When this happens, or if a GATT procedure fails, + * this function immediately terminates the connection. + */ +static void +blecent_read_write_subscribe(const struct peer *peer) +{ + const struct peer_chr *chr; + const struct peer_dsc *dsc; + uint8_t value[2]; + int rc; + + /* Read the supported-new-alert-category characteristic. */ + chr = peer_chr_find_uuid(peer, + BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID), + BLE_UUID16_DECLARE(BLECENT_CHR_SUP_NEW_ALERT_CAT_UUID)); + if (chr == NULL) { + MODLOG_DFLT(ERROR, "Error: Peer doesn't support the Supported New " + "Alert Category characteristic\n"); + goto err; + } + + rc = ble_gattc_read(peer->conn_handle, chr->chr.val_handle, + blecent_on_read, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "Error: Failed to read characteristic; rc=%d\n", + rc); + goto err; + } + + /* Write two bytes (99, 100) to the alert-notification-control-point + * characteristic. + */ + chr = peer_chr_find_uuid(peer, + BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID), + BLE_UUID16_DECLARE(BLECENT_CHR_ALERT_NOT_CTRL_PT)); + if (chr == NULL) { + MODLOG_DFLT(ERROR, "Error: Peer doesn't support the Alert " + "Notification Control Point characteristic\n"); + goto err; + } + + value[0] = 99; + value[1] = 100; + rc = ble_gattc_write_flat(peer->conn_handle, chr->chr.val_handle, + value, sizeof value, blecent_on_write, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "Error: Failed to write characteristic; rc=%d\n", + rc); + } + + /* Subscribe to notifications for the Unread Alert Status characteristic. + * A central enables notifications by writing two bytes (1, 0) to the + * characteristic's client-characteristic-configuration-descriptor (CCCD). + */ + dsc = peer_dsc_find_uuid(peer, + BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID), + BLE_UUID16_DECLARE(BLECENT_CHR_UNR_ALERT_STAT_UUID), + BLE_UUID16_DECLARE(BLE_GATT_DSC_CLT_CFG_UUID16)); + if (dsc == NULL) { + MODLOG_DFLT(ERROR, "Error: Peer lacks a CCCD for the Unread Alert " + "Status characteristic\n"); + goto err; + } + + value[0] = 1; + value[1] = 0; + rc = ble_gattc_write_flat(peer->conn_handle, dsc->dsc.handle, + value, sizeof value, blecent_on_subscribe, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "Error: Failed to subscribe to characteristic; " + "rc=%d\n", rc); + goto err; + } + + return; + +err: + /* Terminate the connection. */ + ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); +} + +/** + * Called when service discovery of the specified peer has completed. + */ +static void +blecent_on_disc_complete(const struct peer *peer, int status, void *arg) +{ + + if (status != 0) { + /* Service discovery failed. Terminate the connection. */ + MODLOG_DFLT(ERROR, "Error: Service discovery failed; status=%d " + "conn_handle=%d\n", status, peer->conn_handle); + ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); + return; + } + + /* Service discovery has completed successfully. Now we have a complete + * list of services, characteristics, and descriptors that the peer + * supports. + */ + MODLOG_DFLT(ERROR, "Service discovery complete; status=%d " + "conn_handle=%d\n", status, peer->conn_handle); + + /* Now perform three concurrent GATT procedures against the peer: read, + * write, and subscribe to notifications. + */ + blecent_read_write_subscribe(peer); +} + +/** + * Initiates the GAP general discovery procedure. + */ +static void +blecent_scan(void) +{ + uint8_t own_addr_type; + struct ble_gap_disc_params disc_params; + int rc; + + /* Figure out address to use while advertising (no privacy for now) */ + rc = ble_hs_id_infer_auto(0, &own_addr_type); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); + return; + } + + /* Tell the controller to filter duplicates; we don't want to process + * repeated advertisements from the same device. + */ + disc_params.filter_duplicates = 1; + + /** + * Perform a passive scan. I.e., don't send follow-up scan requests to + * each advertiser. + */ + disc_params.passive = 1; + + /* Use defaults for the rest of the parameters. */ + disc_params.itvl = 0; + disc_params.window = 0; + disc_params.filter_policy = 0; + disc_params.limited = 0; + + rc = ble_gap_disc(own_addr_type, BLE_HS_FOREVER, &disc_params, + blecent_gap_event, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "Error initiating GAP discovery procedure; rc=%d\n", + rc); + } +} + +/** + * Indicates whether we should tre to connect to the sender of the specified + * advertisement. The function returns a positive result if the device + * advertises connectability and support for the Alert Notification service. + */ +static int +blecent_should_connect(const struct ble_gap_disc_desc *disc) +{ + struct ble_hs_adv_fields fields; + int rc; + int i; + + /* The device has to be advertising connectability. */ + if (disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND && + disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) { + + return 0; + } + + rc = ble_hs_adv_parse_fields(&fields, disc->data, disc->length_data); + if (rc != 0) { + return rc; + } + + /* The device has to advertise support for the Alert Notification + * service (0x1811). + */ + for (i = 0; i < fields.num_uuids16; i++) { + if (ble_uuid_u16(&fields.uuids16[i].u) == BLECENT_SVC_ALERT_UUID) { + return 1; + } + } + + return 0; +} + +/** + * Connects to the sender of the specified advertisement of it looks + * interesting. A device is "interesting" if it advertises connectability and + * support for the Alert Notification service. + */ +static void +blecent_connect_if_interesting(const struct ble_gap_disc_desc *disc) +{ + uint8_t own_addr_type; + int rc; + + /* Don't do anything if we don't care about this advertiser. */ + if (!blecent_should_connect(disc)) { + return; + } + + /* Scanning must be stopped before a connection can be initiated. */ + rc = ble_gap_disc_cancel(); + if (rc != 0) { + MODLOG_DFLT(DEBUG, "Failed to cancel scan; rc=%d\n", rc); + return; + } + + /* Figure out address to use for connect (no privacy for now) */ + rc = ble_hs_id_infer_auto(0, &own_addr_type); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); + return; + } + + /* Try to connect the the advertiser. Allow 30 seconds (30000 ms) for + * timeout. + */ + rc = ble_gap_connect(own_addr_type, &disc->addr, 30000, NULL, + blecent_gap_event, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d " + "addr=%s\n; rc=%d", + disc->addr.type, addr_str(disc->addr.val), rc); + return; + } +} + +/** + * The nimble host executes this callback when a GAP event occurs. The + * application associates a GAP event callback with each connection that is + * established. blecent uses the same callback for all connections. + * + * @param event The event being signalled. + * @param arg Application-specified argument; unused by + * blecent. + * + * @return 0 if the application successfully handled the + * event; nonzero on failure. The semantics + * of the return code is specific to the + * particular GAP event being signalled. + */ +static int +blecent_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + struct ble_hs_adv_fields fields; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_DISC: + rc = ble_hs_adv_parse_fields(&fields, event->disc.data, + event->disc.length_data); + if (rc != 0) { + return 0; + } + + /* An advertisment report was received during GAP discovery. */ + print_adv_fields(&fields); + + /* Try to connect to the advertiser if it looks interesting. */ + blecent_connect_if_interesting(&event->disc); + return 0; + + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + /* Connection successfully established. */ + MODLOG_DFLT(INFO, "Connection established "); + + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + MODLOG_DFLT(INFO, "\n"); + + /* Remember peer. */ + rc = peer_add(event->connect.conn_handle); + if (rc != 0) { + MODLOG_DFLT(ERROR, "Failed to add peer; rc=%d\n", rc); + return 0; + } + + /* Perform service discovery. */ + rc = peer_disc_all(event->connect.conn_handle, + blecent_on_disc_complete, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc); + return 0; + } + } else { + /* Connection attempt failed; resume scanning. */ + MODLOG_DFLT(ERROR, "Error: Connection failed; status=%d\n", + event->connect.status); + blecent_scan(); + } + + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + /* Connection terminated. */ + MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason); + print_conn_desc(&event->disconnect.conn); + MODLOG_DFLT(INFO, "\n"); + + /* Forget about peer. */ + peer_delete(event->disconnect.conn.conn_handle); + + /* Resume scanning. */ + blecent_scan(); + return 0; + + case BLE_GAP_EVENT_DISC_COMPLETE: + MODLOG_DFLT(INFO, "discovery complete; reason=%d\n", + event->disc_complete.reason); + return 0; + + case BLE_GAP_EVENT_ENC_CHANGE: + /* Encryption has been enabled or disabled for this connection. */ + MODLOG_DFLT(INFO, "encryption change event; status=%d ", + event->enc_change.status); + rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + return 0; + + case BLE_GAP_EVENT_NOTIFY_RX: + /* Peer sent us a notification or indication. */ + MODLOG_DFLT(INFO, "received %s; conn_handle=%d attr_handle=%d " + "attr_len=%d\n", + event->notify_rx.indication ? + "indication" : + "notification", + event->notify_rx.conn_handle, + event->notify_rx.attr_handle, + OS_MBUF_PKTLEN(event->notify_rx.om)); + + /* Attribute data is contained in event->notify_rx.attr_data. */ + return 0; + + case BLE_GAP_EVENT_MTU: + MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d cid=%d mtu=%d\n", + event->mtu.conn_handle, + event->mtu.channel_id, + event->mtu.value); + return 0; + + case BLE_GAP_EVENT_REPEAT_PAIRING: + /* We already have a bond with the peer, but it is attempting to + * establish a new secure link. This app sacrifices security for + * convenience: just throw away the old bond and accept the new link. + */ + + /* Delete the old bond. */ + rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc); + assert(rc == 0); + ble_store_util_delete_peer(&desc.peer_id_addr); + + /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should + * continue with the pairing operation. + */ + return BLE_GAP_REPEAT_PAIRING_RETRY; + + default: + return 0; + } +} + +static void +blecent_on_reset(int reason) +{ + MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason); +} + +static void +blecent_on_sync(void) +{ + int rc; + + /* Make sure we have proper identity address set (public preferred) */ + rc = ble_hs_util_ensure_addr(0); + assert(rc == 0); + + /* Begin scanning for a peripheral to connect to. */ + blecent_scan(); +} + +/** + * main + * + * All application logic and NimBLE host work is performed in default task. + * + * @return int NOTE: this function should never return! + */ +int +main(void) +{ + int rc; + + /* Initialize OS */ + sysinit(); + + /* Configure the host. */ + ble_hs_cfg.reset_cb = blecent_on_reset; + ble_hs_cfg.sync_cb = blecent_on_sync; + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + + /* Initialize data structures to track connected peers. */ + rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64); + assert(rc == 0); + + /* Set the default device name. */ + rc = ble_svc_gap_device_name_set("nimble-blecent"); + assert(rc == 0); + + /* os start should never return. If it does, this should be an error */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + + return 0; +} diff --git a/src/libs/mynewt-nimble/apps/blecent/src/misc.c b/src/libs/mynewt-nimble/apps/blecent/src/misc.c new file mode 100644 index 00000000..6813a122 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blecent/src/misc.c @@ -0,0 +1,209 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include "host/ble_hs.h" +#include "host/ble_uuid.h" +#include "blecent.h" + +/** + * Utility function to log an array of bytes. + */ +void +print_bytes(const uint8_t *bytes, int len) +{ + int i; + + for (i = 0; i < len; i++) { + MODLOG_DFLT(DEBUG, "%s0x%02x", i != 0 ? ":" : "", bytes[i]); + } +} + +void +print_mbuf(const struct os_mbuf *om) +{ + int colon; + + colon = 0; + while (om != NULL) { + if (colon) { + MODLOG_DFLT(DEBUG, ":"); + } else { + colon = 1; + } + print_bytes(om->om_data, om->om_len); + om = SLIST_NEXT(om, om_next); + } +} + +char * +addr_str(const void *addr) +{ + static char buf[6 * 2 + 5 + 1]; + const uint8_t *u8p; + + u8p = addr; + sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", + u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); + + return buf; +} + +void +print_uuid(const ble_uuid_t *uuid) +{ + char buf[BLE_UUID_STR_LEN]; + + MODLOG_DFLT(DEBUG, "%s", ble_uuid_to_str(uuid, buf)); +} + +/** + * Logs information about a connection to the console. + */ +void +print_conn_desc(const struct ble_gap_conn_desc *desc) +{ + MODLOG_DFLT(DEBUG, "handle=%d our_ota_addr_type=%d our_ota_addr=%s ", + desc->conn_handle, desc->our_ota_addr.type, + addr_str(desc->our_ota_addr.val)); + MODLOG_DFLT(DEBUG, "our_id_addr_type=%d our_id_addr=%s ", + desc->our_id_addr.type, addr_str(desc->our_id_addr.val)); + MODLOG_DFLT(DEBUG, "peer_ota_addr_type=%d peer_ota_addr=%s ", + desc->peer_ota_addr.type, addr_str(desc->peer_ota_addr.val)); + MODLOG_DFLT(DEBUG, "peer_id_addr_type=%d peer_id_addr=%s ", + desc->peer_id_addr.type, addr_str(desc->peer_id_addr.val)); + MODLOG_DFLT(DEBUG, "conn_itvl=%d conn_latency=%d supervision_timeout=%d " + "encrypted=%d authenticated=%d bonded=%d", + desc->conn_itvl, desc->conn_latency, + desc->supervision_timeout, + desc->sec_state.encrypted, + desc->sec_state.authenticated, + desc->sec_state.bonded); +} + + +void +print_adv_fields(const struct ble_hs_adv_fields *fields) +{ + char s[BLE_HS_ADV_MAX_SZ]; + const uint8_t *u8p; + int i; + + if (fields->flags != 0) { + MODLOG_DFLT(DEBUG, " flags=0x%02x\n", fields->flags); + } + + if (fields->uuids16 != NULL) { + MODLOG_DFLT(DEBUG, " uuids16(%scomplete)=", + fields->uuids16_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids16; i++) { + print_uuid(&fields->uuids16[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uuids32 != NULL) { + MODLOG_DFLT(DEBUG, " uuids32(%scomplete)=", + fields->uuids32_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids32; i++) { + print_uuid(&fields->uuids32[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uuids128 != NULL) { + MODLOG_DFLT(DEBUG, " uuids128(%scomplete)=", + fields->uuids128_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids128; i++) { + print_uuid(&fields->uuids128[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->name != NULL) { + assert(fields->name_len < sizeof s - 1); + memcpy(s, fields->name, fields->name_len); + s[fields->name_len] = '\0'; + MODLOG_DFLT(DEBUG, " name(%scomplete)=%s\n", + fields->name_is_complete ? "" : "in", s); + } + + if (fields->tx_pwr_lvl_is_present) { + MODLOG_DFLT(DEBUG, " tx_pwr_lvl=%d\n", fields->tx_pwr_lvl); + } + + if (fields->slave_itvl_range != NULL) { + MODLOG_DFLT(DEBUG, " slave_itvl_range="); + print_bytes(fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->svc_data_uuid16 != NULL) { + MODLOG_DFLT(DEBUG, " svc_data_uuid16="); + print_bytes(fields->svc_data_uuid16, fields->svc_data_uuid16_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->public_tgt_addr != NULL) { + MODLOG_DFLT(DEBUG, " public_tgt_addr="); + u8p = fields->public_tgt_addr; + for (i = 0; i < fields->num_public_tgt_addrs; i++) { + MODLOG_DFLT(DEBUG, "public_tgt_addr=%s ", addr_str(u8p)); + u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN; + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->appearance_is_present) { + MODLOG_DFLT(DEBUG, " appearance=0x%04x\n", fields->appearance); + } + + if (fields->adv_itvl_is_present) { + MODLOG_DFLT(DEBUG, " adv_itvl=0x%04x\n", fields->adv_itvl); + } + + if (fields->svc_data_uuid32 != NULL) { + MODLOG_DFLT(DEBUG, " svc_data_uuid32="); + print_bytes(fields->svc_data_uuid32, fields->svc_data_uuid32_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->svc_data_uuid128 != NULL) { + MODLOG_DFLT(DEBUG, " svc_data_uuid128="); + print_bytes(fields->svc_data_uuid128, fields->svc_data_uuid128_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uri != NULL) { + MODLOG_DFLT(DEBUG, " uri="); + print_bytes(fields->uri, fields->uri_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->mfg_data != NULL) { + MODLOG_DFLT(DEBUG, " mfg_data="); + print_bytes(fields->mfg_data, fields->mfg_data_len); + MODLOG_DFLT(DEBUG, "\n"); + } +} diff --git a/src/libs/mynewt-nimble/apps/blecent/src/peer.c b/src/libs/mynewt-nimble/apps/blecent/src/peer.c new file mode 100644 index 00000000..aeca7d90 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blecent/src/peer.c @@ -0,0 +1,807 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include "host/ble_hs.h" +#include "blecent.h" + +static void *peer_svc_mem; +static struct os_mempool peer_svc_pool; + +static void *peer_chr_mem; +static struct os_mempool peer_chr_pool; + +static void *peer_dsc_mem; +static struct os_mempool peer_dsc_pool; + +static void *peer_mem; +static struct os_mempool peer_pool; +static SLIST_HEAD(, peer) peers; + +static struct peer_svc * +peer_svc_find_range(struct peer *peer, uint16_t attr_handle); +static struct peer_svc * +peer_svc_find(struct peer *peer, uint16_t svc_start_handle, + struct peer_svc **out_prev); +int +peer_svc_is_empty(const struct peer_svc *svc); + +uint16_t +chr_end_handle(const struct peer_svc *svc, const struct peer_chr *chr); +int +chr_is_empty(const struct peer_svc *svc, const struct peer_chr *chr); +static struct peer_chr * +peer_chr_find(const struct peer_svc *svc, uint16_t chr_def_handle, + struct peer_chr **out_prev); +static void +peer_disc_chrs(struct peer *peer); + +static int +peer_dsc_disced(uint16_t conn_handle, const struct ble_gatt_error *error, + uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc, + void *arg); + +static struct peer * +peer_find(uint16_t conn_handle) +{ + struct peer *peer; + + SLIST_FOREACH(peer, &peers, next) { + if (peer->conn_handle == conn_handle) { + return peer; + } + } + + return NULL; +} + +static void +peer_disc_complete(struct peer *peer, int rc) +{ + peer->disc_prev_chr_val = 0; + + /* Notify caller that discovery has completed. */ + if (peer->disc_cb != NULL) { + peer->disc_cb(peer, rc, peer->disc_cb_arg); + } +} + +static struct peer_dsc * +peer_dsc_find_prev(const struct peer_chr *chr, uint16_t dsc_handle) +{ + struct peer_dsc *prev; + struct peer_dsc *dsc; + + prev = NULL; + SLIST_FOREACH(dsc, &chr->dscs, next) { + if (dsc->dsc.handle >= dsc_handle) { + break; + } + + prev = dsc; + } + + return prev; +} + +static struct peer_dsc * +peer_dsc_find(const struct peer_chr *chr, uint16_t dsc_handle, + struct peer_dsc **out_prev) +{ + struct peer_dsc *prev; + struct peer_dsc *dsc; + + prev = peer_dsc_find_prev(chr, dsc_handle); + if (prev == NULL) { + dsc = SLIST_FIRST(&chr->dscs); + } else { + dsc = SLIST_NEXT(prev, next); + } + + if (dsc != NULL && dsc->dsc.handle != dsc_handle) { + dsc = NULL; + } + + if (out_prev != NULL) { + *out_prev = prev; + } + return dsc; +} + +static int +peer_dsc_add(struct peer *peer, uint16_t chr_val_handle, + const struct ble_gatt_dsc *gatt_dsc) +{ + struct peer_dsc *prev; + struct peer_dsc *dsc; + struct peer_svc *svc; + struct peer_chr *chr; + + svc = peer_svc_find_range(peer, chr_val_handle); + if (svc == NULL) { + /* Can't find service for discovered descriptor; this shouldn't + * happen. + */ + assert(0); + return BLE_HS_EUNKNOWN; + } + + chr = peer_chr_find(svc, chr_val_handle, NULL); + if (chr == NULL) { + /* Can't find characteristic for discovered descriptor; this shouldn't + * happen. + */ + assert(0); + return BLE_HS_EUNKNOWN; + } + + dsc = peer_dsc_find(chr, gatt_dsc->handle, &prev); + if (dsc != NULL) { + /* Descriptor already discovered. */ + return 0; + } + + dsc = os_memblock_get(&peer_dsc_pool); + if (dsc == NULL) { + /* Out of memory. */ + return BLE_HS_ENOMEM; + } + memset(dsc, 0, sizeof *dsc); + + dsc->dsc = *gatt_dsc; + + if (prev == NULL) { + SLIST_INSERT_HEAD(&chr->dscs, dsc, next); + } else { + SLIST_NEXT(prev, next) = dsc; + } + + return 0; +} + +static void +peer_disc_dscs(struct peer *peer) +{ + struct peer_chr *chr; + struct peer_svc *svc; + int rc; + + /* Search through the list of discovered characteristics for the first + * characteristic that contains undiscovered descriptors. Then, discover + * all descriptors belonging to that characteristic. + */ + SLIST_FOREACH(svc, &peer->svcs, next) { + SLIST_FOREACH(chr, &svc->chrs, next) { + if (!chr_is_empty(svc, chr) && + SLIST_EMPTY(&chr->dscs) && + peer->disc_prev_chr_val <= chr->chr.def_handle) { + + rc = ble_gattc_disc_all_dscs(peer->conn_handle, + chr->chr.val_handle, + chr_end_handle(svc, chr), + peer_dsc_disced, peer); + if (rc != 0) { + peer_disc_complete(peer, rc); + } + + peer->disc_prev_chr_val = chr->chr.val_handle; + return; + } + } + } + + /* All descriptors discovered. */ + peer_disc_complete(peer, 0); +} + +static int +peer_dsc_disced(uint16_t conn_handle, const struct ble_gatt_error *error, + uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc, + void *arg) +{ + struct peer *peer; + int rc; + + peer = arg; + assert(peer->conn_handle == conn_handle); + + switch (error->status) { + case 0: + rc = peer_dsc_add(peer, chr_val_handle, dsc); + break; + + case BLE_HS_EDONE: + /* All descriptors in this characteristic discovered; start discovering + * descriptors in the next characteristic. + */ + if (peer->disc_prev_chr_val > 0) { + peer_disc_dscs(peer); + } + rc = 0; + break; + + default: + /* Error; abort discovery. */ + rc = error->status; + break; + } + + if (rc != 0) { + /* Error; abort discovery. */ + peer_disc_complete(peer, rc); + } + + return rc; +} + +uint16_t +chr_end_handle(const struct peer_svc *svc, const struct peer_chr *chr) +{ + const struct peer_chr *next_chr; + + next_chr = SLIST_NEXT(chr, next); + if (next_chr != NULL) { + return next_chr->chr.def_handle - 1; + } else { + return svc->svc.end_handle; + } +} + +int +chr_is_empty(const struct peer_svc *svc, const struct peer_chr *chr) +{ + return chr_end_handle(svc, chr) <= chr->chr.val_handle; +} + +static struct peer_chr * +peer_chr_find_prev(const struct peer_svc *svc, uint16_t chr_val_handle) +{ + struct peer_chr *prev; + struct peer_chr *chr; + + prev = NULL; + SLIST_FOREACH(chr, &svc->chrs, next) { + if (chr->chr.val_handle >= chr_val_handle) { + break; + } + + prev = chr; + } + + return prev; +} + +static struct peer_chr * +peer_chr_find(const struct peer_svc *svc, uint16_t chr_val_handle, + struct peer_chr **out_prev) +{ + struct peer_chr *prev; + struct peer_chr *chr; + + prev = peer_chr_find_prev(svc, chr_val_handle); + if (prev == NULL) { + chr = SLIST_FIRST(&svc->chrs); + } else { + chr = SLIST_NEXT(prev, next); + } + + if (chr != NULL && chr->chr.val_handle != chr_val_handle) { + chr = NULL; + } + + if (out_prev != NULL) { + *out_prev = prev; + } + return chr; +} + +static void +peer_chr_delete(struct peer_chr *chr) +{ + struct peer_dsc *dsc; + + while ((dsc = SLIST_FIRST(&chr->dscs)) != NULL) { + SLIST_REMOVE_HEAD(&chr->dscs, next); + os_memblock_put(&peer_dsc_pool, dsc); + } + + os_memblock_put(&peer_chr_pool, chr); +} + +static int +peer_chr_add(struct peer *peer, uint16_t svc_start_handle, + const struct ble_gatt_chr *gatt_chr) +{ + struct peer_chr *prev; + struct peer_chr *chr; + struct peer_svc *svc; + + svc = peer_svc_find(peer, svc_start_handle, NULL); + if (svc == NULL) { + /* Can't find service for discovered characteristic; this shouldn't + * happen. + */ + assert(0); + return BLE_HS_EUNKNOWN; + } + + chr = peer_chr_find(svc, gatt_chr->def_handle, &prev); + if (chr != NULL) { + /* Characteristic already discovered. */ + return 0; + } + + chr = os_memblock_get(&peer_chr_pool); + if (chr == NULL) { + /* Out of memory. */ + return BLE_HS_ENOMEM; + } + memset(chr, 0, sizeof *chr); + + chr->chr = *gatt_chr; + + if (prev == NULL) { + SLIST_INSERT_HEAD(&svc->chrs, chr, next); + } else { + SLIST_NEXT(prev, next) = chr; + } + + return 0; +} + +static int +peer_chr_disced(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_chr *chr, void *arg) +{ + struct peer *peer; + int rc; + + peer = arg; + assert(peer->conn_handle == conn_handle); + + switch (error->status) { + case 0: + rc = peer_chr_add(peer, peer->cur_svc->svc.start_handle, chr); + break; + + case BLE_HS_EDONE: + /* All characteristics in this service discovered; start discovering + * characteristics in the next service. + */ + if (peer->disc_prev_chr_val > 0) { + peer_disc_chrs(peer); + } + rc = 0; + break; + + default: + rc = error->status; + break; + } + + if (rc != 0) { + /* Error; abort discovery. */ + peer_disc_complete(peer, rc); + } + + return rc; +} + +static void +peer_disc_chrs(struct peer *peer) +{ + struct peer_svc *svc; + int rc; + + /* Search through the list of discovered service for the first service that + * contains undiscovered characteristics. Then, discover all + * characteristics belonging to that service. + */ + SLIST_FOREACH(svc, &peer->svcs, next) { + if (!peer_svc_is_empty(svc) && SLIST_EMPTY(&svc->chrs)) { + peer->cur_svc = svc; + rc = ble_gattc_disc_all_chrs(peer->conn_handle, + svc->svc.start_handle, + svc->svc.end_handle, + peer_chr_disced, peer); + if (rc != 0) { + peer_disc_complete(peer, rc); + } + return; + } + } + + /* All characteristics discovered. */ + peer_disc_dscs(peer); +} + +int +peer_svc_is_empty(const struct peer_svc *svc) +{ + return svc->svc.end_handle <= svc->svc.start_handle; +} + +static struct peer_svc * +peer_svc_find_prev(struct peer *peer, uint16_t svc_start_handle) +{ + struct peer_svc *prev; + struct peer_svc *svc; + + prev = NULL; + SLIST_FOREACH(svc, &peer->svcs, next) { + if (svc->svc.start_handle >= svc_start_handle) { + break; + } + + prev = svc; + } + + return prev; +} + +static struct peer_svc * +peer_svc_find(struct peer *peer, uint16_t svc_start_handle, + struct peer_svc **out_prev) +{ + struct peer_svc *prev; + struct peer_svc *svc; + + prev = peer_svc_find_prev(peer, svc_start_handle); + if (prev == NULL) { + svc = SLIST_FIRST(&peer->svcs); + } else { + svc = SLIST_NEXT(prev, next); + } + + if (svc != NULL && svc->svc.start_handle != svc_start_handle) { + svc = NULL; + } + + if (out_prev != NULL) { + *out_prev = prev; + } + return svc; +} + +static struct peer_svc * +peer_svc_find_range(struct peer *peer, uint16_t attr_handle) +{ + struct peer_svc *svc; + + SLIST_FOREACH(svc, &peer->svcs, next) { + if (svc->svc.start_handle <= attr_handle && + svc->svc.end_handle >= attr_handle) { + + return svc; + } + } + + return NULL; +} + +const struct peer_svc * +peer_svc_find_uuid(const struct peer *peer, const ble_uuid_t *uuid) +{ + const struct peer_svc *svc; + + SLIST_FOREACH(svc, &peer->svcs, next) { + if (ble_uuid_cmp(&svc->svc.uuid.u, uuid) == 0) { + return svc; + } + } + + return NULL; +} + +const struct peer_chr * +peer_chr_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid, + const ble_uuid_t *chr_uuid) +{ + const struct peer_svc *svc; + const struct peer_chr *chr; + + svc = peer_svc_find_uuid(peer, svc_uuid); + if (svc == NULL) { + return NULL; + } + + SLIST_FOREACH(chr, &svc->chrs, next) { + if (ble_uuid_cmp(&chr->chr.uuid.u, chr_uuid) == 0) { + return chr; + } + } + + return NULL; +} + +const struct peer_dsc * +peer_dsc_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid, + const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid) +{ + const struct peer_chr *chr; + const struct peer_dsc *dsc; + + chr = peer_chr_find_uuid(peer, svc_uuid, chr_uuid); + if (chr == NULL) { + return NULL; + } + + SLIST_FOREACH(dsc, &chr->dscs, next) { + if (ble_uuid_cmp(&dsc->dsc.uuid.u, dsc_uuid) == 0) { + return dsc; + } + } + + return NULL; +} + +static int +peer_svc_add(struct peer *peer, const struct ble_gatt_svc *gatt_svc) +{ + struct peer_svc *prev; + struct peer_svc *svc; + + svc = peer_svc_find(peer, gatt_svc->start_handle, &prev); + if (svc != NULL) { + /* Service already discovered. */ + return 0; + } + + svc = os_memblock_get(&peer_svc_pool); + if (svc == NULL) { + /* Out of memory. */ + return BLE_HS_ENOMEM; + } + memset(svc, 0, sizeof *svc); + + svc->svc = *gatt_svc; + SLIST_INIT(&svc->chrs); + + if (prev == NULL) { + SLIST_INSERT_HEAD(&peer->svcs, svc, next); + } else { + SLIST_INSERT_AFTER(prev, svc, next); + } + + return 0; +} + +static void +peer_svc_delete(struct peer_svc *svc) +{ + struct peer_chr *chr; + + while ((chr = SLIST_FIRST(&svc->chrs)) != NULL) { + SLIST_REMOVE_HEAD(&svc->chrs, next); + peer_chr_delete(chr); + } + + os_memblock_put(&peer_svc_pool, svc); +} + +static int +peer_svc_disced(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_svc *service, void *arg) +{ + struct peer *peer; + int rc; + + peer = arg; + assert(peer->conn_handle == conn_handle); + + switch (error->status) { + case 0: + rc = peer_svc_add(peer, service); + break; + + case BLE_HS_EDONE: + /* All services discovered; start discovering characteristics. */ + if (peer->disc_prev_chr_val > 0) { + peer_disc_chrs(peer); + } + rc = 0; + break; + + default: + rc = error->status; + break; + } + + if (rc != 0) { + /* Error; abort discovery. */ + peer_disc_complete(peer, rc); + } + + return rc; +} + + +int +peer_disc_all(uint16_t conn_handle, peer_disc_fn *disc_cb, void *disc_cb_arg) +{ + struct peer_svc *svc; + struct peer *peer; + int rc; + + peer = peer_find(conn_handle); + if (peer == NULL) { + return BLE_HS_ENOTCONN; + } + + /* Undiscover everything first. */ + while ((svc = SLIST_FIRST(&peer->svcs)) != NULL) { + SLIST_REMOVE_HEAD(&peer->svcs, next); + peer_svc_delete(svc); + } + + peer->disc_prev_chr_val = 1; + peer->disc_cb = disc_cb; + peer->disc_cb_arg = disc_cb_arg; + + rc = ble_gattc_disc_all_svcs(conn_handle, peer_svc_disced, peer); + if (rc != 0) { + return rc; + } + + return 0; +} + +int +peer_delete(uint16_t conn_handle) +{ + struct peer_svc *svc; + struct peer *peer; + int rc; + + peer = peer_find(conn_handle); + if (peer == NULL) { + return BLE_HS_ENOTCONN; + } + + SLIST_REMOVE(&peers, peer, peer, next); + + while ((svc = SLIST_FIRST(&peer->svcs)) != NULL) { + SLIST_REMOVE_HEAD(&peer->svcs, next); + peer_svc_delete(svc); + } + + rc = os_memblock_put(&peer_pool, peer); + if (rc != 0) { + return BLE_HS_EOS; + } + + return 0; +} + +int +peer_add(uint16_t conn_handle) +{ + struct peer *peer; + + /* Make sure the connection handle is unique. */ + peer = peer_find(conn_handle); + if (peer != NULL) { + return BLE_HS_EALREADY; + } + + peer = os_memblock_get(&peer_pool); + if (peer == NULL) { + /* Out of memory. */ + return BLE_HS_ENOMEM; + } + + memset(peer, 0, sizeof *peer); + peer->conn_handle = conn_handle; + + SLIST_INSERT_HEAD(&peers, peer, next); + + return 0; +} + +static void +peer_free_mem(void) +{ + free(peer_mem); + peer_mem = NULL; + + free(peer_svc_mem); + peer_svc_mem = NULL; + + free(peer_chr_mem); + peer_chr_mem = NULL; + + free(peer_dsc_mem); + peer_dsc_mem = NULL; +} + +int +peer_init(int max_peers, int max_svcs, int max_chrs, int max_dscs) +{ + int rc; + + /* Free memory first in case this function gets called more than once. */ + peer_free_mem(); + + peer_mem = malloc( + OS_MEMPOOL_BYTES(max_peers, sizeof (struct peer))); + if (peer_mem == NULL) { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = os_mempool_init(&peer_pool, max_peers, + sizeof (struct peer), peer_mem, + "peer_pool"); + if (rc != 0) { + rc = BLE_HS_EOS; + goto err; + } + + peer_svc_mem = malloc( + OS_MEMPOOL_BYTES(max_svcs, sizeof (struct peer_svc))); + if (peer_svc_mem == NULL) { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = os_mempool_init(&peer_svc_pool, max_svcs, + sizeof (struct peer_svc), peer_svc_mem, + "peer_svc_pool"); + if (rc != 0) { + rc = BLE_HS_EOS; + goto err; + } + + peer_chr_mem = malloc( + OS_MEMPOOL_BYTES(max_chrs, sizeof (struct peer_chr))); + if (peer_chr_mem == NULL) { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = os_mempool_init(&peer_chr_pool, max_chrs, + sizeof (struct peer_chr), peer_chr_mem, + "peer_chr_pool"); + if (rc != 0) { + rc = BLE_HS_EOS; + goto err; + } + + peer_dsc_mem = malloc( + OS_MEMPOOL_BYTES(max_dscs, sizeof (struct peer_dsc))); + if (peer_dsc_mem == NULL) { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = os_mempool_init(&peer_dsc_pool, max_dscs, + sizeof (struct peer_dsc), peer_dsc_mem, + "peer_dsc_pool"); + if (rc != 0) { + rc = BLE_HS_EOS; + goto err; + } + + return 0; + +err: + peer_free_mem(); + return rc; +} diff --git a/src/libs/mynewt-nimble/apps/blecent/syscfg.yml b/src/libs/mynewt-nimble/apps/blecent/syscfg.yml new file mode 100644 index 00000000..fee5bf06 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blecent/syscfg.yml @@ -0,0 +1,30 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.vals: + # DEBUG logging is a bit noisy; use INFO. + LOG_LEVEL: 1 + + # Default task settings + OS_MAIN_STACK_SIZE: 336 + +# Disable peripheral and broadcaster roles. + BLE_ROLE_BROADCASTER: 0 + BLE_ROLE_CENTRAL: 1 + BLE_ROLE_OBSERVER: 1 + BLE_ROLE_PERIPHERAL: 0 diff --git a/src/libs/mynewt-nimble/apps/blecsc/README.md b/src/libs/mynewt-nimble/apps/blecsc/README.md new file mode 100644 index 00000000..bccf176a --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blecsc/README.md @@ -0,0 +1,9 @@ +# BLE Cycling Speed and Cadence peripheral app. + +The source files are located in the src/ directory. + +pkg.yml contains the base definition of the app. + +syscfg.yml contains setting definitions and overrides. + + diff --git a/src/libs/mynewt-nimble/apps/blecsc/pkg.yml b/src/libs/mynewt-nimble/apps/blecsc/pkg.yml new file mode 100644 index 00000000..828ff301 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blecsc/pkg.yml @@ -0,0 +1,40 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: apps/blecsc +pkg.type: app +pkg.description: BLE peripheral cycling speed and cadence sensor. +pkg.author: "Maciej Jurczak" +pkg.email: "mjurczak@gmail.com" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/sysinit" + - "@apache-mynewt-core/sys/id" + - nimble/controller + - nimble/host + - nimble/host/services/gap + - nimble/host/services/gatt + - nimble/host/store/config + - nimble/transport diff --git a/src/libs/mynewt-nimble/apps/blecsc/src/blecsc_sens.h b/src/libs/mynewt-nimble/apps/blecsc/src/blecsc_sens.h new file mode 100644 index 00000000..99addc0f --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blecsc/src/blecsc_sens.h @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLECSC_SENSOR_ +#define H_BLECSC_SENSOR_ + +#include "modlog/modlog.h" +#include "nimble/ble.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Cycling Speed and Cadence configuration */ +#define GATT_CSC_UUID 0x1816 +#define GATT_CSC_MEASUREMENT_UUID 0x2A5B +#define GATT_CSC_FEATURE_UUID 0x2A5C +#define GATT_SENSOR_LOCATION_UUID 0x2A5D +#define GATT_SC_CONTROL_POINT_UUID 0x2A55 +/* Device Information configuration */ +#define GATT_DEVICE_INFO_UUID 0x180A +#define GATT_MANUFACTURER_NAME_UUID 0x2A29 +#define GATT_MODEL_NUMBER_UUID 0x2A24 + +/*CSC Measurement flags*/ +#define CSC_MEASUREMENT_WHEEL_REV_PRESENT 0x01 +#define CSC_MEASUREMENT_CRANK_REV_PRESENT 0x02 + +/* CSC feature flags */ +#define CSC_FEATURE_WHEEL_REV_DATA 0x01 +#define CSC_FEATURE_CRANK_REV_DATA 0x02 +#define CSC_FEATURE_MULTIPLE_SENSOR_LOC 0x04 + +/* Sensor location enum */ +#define SENSOR_LOCATION_OTHER 0 +#define SENSOR_LOCATION_TOP_OF_SHOE 1 +#define SENSOR_LOCATION_IN_SHOE 2 +#define SENSOR_LOCATION_HIP 3 +#define SENSOR_LOCATION_FRONT_WHEEL 4 +#define SENSOR_LOCATION_LEFT_CRANK 5 +#define SENSOR_LOCATION_RIGHT_CRANK 6 +#define SENSOR_LOCATION_LEFT_PEDAL 7 +#define SENSOR_LOCATION_RIGHT_PEDAL 8 +#define SENSOR_LOCATION_FROT_HUB 9 +#define SENSOR_LOCATION_REAR_DROPOUT 10 +#define SENSOR_LOCATION_CHAINSTAY 11 +#define SENSOR_LOCATION_REAR_WHEEL 12 +#define SENSOR_LOCATION_REAR_HUB 13 +#define SENSOR_LOCATION_CHEST 14 +#define SENSOR_LOCATION_SPIDER 15 +#define SENSOR_LOCATION_CHAIN_RING 16 + +/* SC Control Point op codes */ +#define SC_CP_OP_SET_CUMULATIVE_VALUE 1 +#define SC_CP_OP_START_SENSOR_CALIBRATION 2 +#define SC_CP_OP_UPDATE_SENSOR_LOCATION 3 +#define SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS 4 +#define SC_CP_OP_RESPONSE 16 + +/*SC Control Point response values */ +#define SC_CP_RESPONSE_SUCCESS 1 +#define SC_CP_RESPONSE_OP_NOT_SUPPORTED 2 +#define SC_CP_RESPONSE_INVALID_PARAM 3 +#define SC_CP_RESPONSE_OP_FAILED 4 + +/* CSC simulation configuration */ +#define CSC_FEATURES (CSC_FEATURE_WHEEL_REV_DATA | \ + CSC_FEATURE_CRANK_REV_DATA |\ + CSC_FEATURE_MULTIPLE_SENSOR_LOC) + +struct ble_csc_measurement_state { + uint32_t cumulative_wheel_rev; + uint16_t last_wheel_evt_time; + uint16_t cumulative_crank_rev; + uint16_t last_crank_evt_time; +}; + +extern uint16_t csc_measurement_handle; +extern uint16_t csc_control_point_handle; + +int gatt_svr_init(struct ble_csc_measurement_state * csc_measurement_state); +int gatt_svr_chr_notify_csc_measurement(uint16_t conn_handle); +void gatt_svr_set_cp_indicate(uint8_t indication_status); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libs/mynewt-nimble/apps/blecsc/src/gatt_svr.c b/src/libs/mynewt-nimble/apps/blecsc/src/gatt_svr.c new file mode 100644 index 00000000..e66aa9a8 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blecsc/src/gatt_svr.c @@ -0,0 +1,385 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include "os/mynewt.h" +#include "host/ble_hs.h" +#include "host/ble_uuid.h" +#include "blecsc_sens.h" + +#define CSC_ERR_CCC_DESC_IMPROPERLY_CONFIGURED 0x81 + +static const char *manuf_name = "Apache Mynewt"; +static const char *model_num = "Mynewt CSC Sensor"; + +static const uint8_t csc_supported_sensor_locations[] = { + SENSOR_LOCATION_FRONT_WHEEL, + SENSOR_LOCATION_REAR_DROPOUT, + SENSOR_LOCATION_CHAINSTAY, + SENSOR_LOCATION_REAR_WHEEL +}; + +static uint8_t sensor_location = SENSOR_LOCATION_REAR_DROPOUT; +static struct ble_csc_measurement_state * measurement_state; +uint16_t csc_measurement_handle; +uint16_t csc_control_point_handle; +uint8_t csc_cp_indication_status; + +static int +gatt_svr_chr_access_csc_measurement(uint16_t conn_handle, + uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_chr_access_csc_feature(uint16_t conn_handle, + uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_chr_access_sensor_location(uint16_t conn_handle, + uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_chr_access_sc_control_point(uint16_t conn_handle, + uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_chr_access_device_info(uint16_t conn_handle, + uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static const struct ble_gatt_svc_def gatt_svr_svcs[] = { + { + /* Service: Cycling Speed and Cadence */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(GATT_CSC_UUID), + .characteristics = (struct ble_gatt_chr_def[]) { { + /* Characteristic: Cycling Speed and Cadence Measurement */ + .uuid = BLE_UUID16_DECLARE(GATT_CSC_MEASUREMENT_UUID), + .access_cb = gatt_svr_chr_access_csc_measurement, + .val_handle = &csc_measurement_handle, + .flags = BLE_GATT_CHR_F_NOTIFY, + }, { + /* Characteristic: Cycling Speed and Cadence features */ + .uuid = BLE_UUID16_DECLARE(GATT_CSC_FEATURE_UUID), + .access_cb = gatt_svr_chr_access_csc_feature, + .flags = BLE_GATT_CHR_F_READ, + }, { + /* Characteristic: Sensor Location */ + .uuid = BLE_UUID16_DECLARE(GATT_SENSOR_LOCATION_UUID), + .access_cb = gatt_svr_chr_access_sensor_location, + .flags = BLE_GATT_CHR_F_READ, + }, { + /* Characteristic: SC Control Point*/ + .uuid = BLE_UUID16_DECLARE(GATT_SC_CONTROL_POINT_UUID), + .access_cb = gatt_svr_chr_access_sc_control_point, + .val_handle = &csc_control_point_handle, + .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_INDICATE, + }, { + 0, /* No more characteristics in this service */ + }, } + }, + + { + /* Service: Device Information */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(GATT_DEVICE_INFO_UUID), + .characteristics = (struct ble_gatt_chr_def[]) { { + /* Characteristic: * Manufacturer name */ + .uuid = BLE_UUID16_DECLARE(GATT_MANUFACTURER_NAME_UUID), + .access_cb = gatt_svr_chr_access_device_info, + .flags = BLE_GATT_CHR_F_READ, + }, { + /* Characteristic: Model number string */ + .uuid = BLE_UUID16_DECLARE(GATT_MODEL_NUMBER_UUID), + .access_cb = gatt_svr_chr_access_device_info, + .flags = BLE_GATT_CHR_F_READ, + }, { + 0, /* No more characteristics in this service */ + }, } + }, + + { + 0, /* No more services */ + }, +}; + +static int +gatt_svr_chr_access_csc_measurement(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + return BLE_ATT_ERR_READ_NOT_PERMITTED; +} + +static int +gatt_svr_chr_access_csc_feature(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + static const uint16_t csc_feature = CSC_FEATURES; + int rc; + + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + rc = os_mbuf_append(ctxt->om, &csc_feature, sizeof(csc_feature)); + + return (rc == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; +} + +static int +gatt_svr_chr_access_sensor_location(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + int rc; + + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + rc = os_mbuf_append(ctxt->om, &sensor_location, sizeof(sensor_location)); + + return (rc == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; +} + +static int +gatt_svr_chr_access_sc_control_point(uint16_t conn_handle, + uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint8_t op_code; + uint8_t new_sensor_location; + uint8_t new_cumulative_wheel_rev_arr[4]; + struct os_mbuf *om_indication; + uint8_t response = SC_CP_RESPONSE_OP_NOT_SUPPORTED; + int ii; + int rc; + + assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR); + + if (!csc_cp_indication_status) { + MODLOG_DFLT(INFO, "SC Control Point; CCC descriptor " + "improperly configured"); + return CSC_ERR_CCC_DESC_IMPROPERLY_CONFIGURED; + } + + /* Read control point op code*/ + rc = os_mbuf_copydata(ctxt->om, 0, sizeof(op_code), &op_code); + if (rc != 0){ + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + MODLOG_DFLT(INFO, "SC Control Point; opcode=%d\n", op_code); + + /* Allocate response buffer */ + om_indication = ble_hs_mbuf_att_pkt(); + + switch(op_code){ +#if (CSC_FEATURES & CSC_FEATURE_WHEEL_REV_DATA) + case SC_CP_OP_SET_CUMULATIVE_VALUE: + /* Read new cumulative wheel revolutions value*/ + rc = os_mbuf_copydata(ctxt->om, 1, + sizeof(new_cumulative_wheel_rev_arr), + new_cumulative_wheel_rev_arr); + if (rc != 0){ + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + measurement_state->cumulative_wheel_rev = + get_le32(new_cumulative_wheel_rev_arr); + + MODLOG_DFLT(INFO, "SC Control Point; Set cumulative value = %d\n", + measurement_state->cumulative_wheel_rev); + + response = SC_CP_RESPONSE_SUCCESS; + break; +#endif + +#if (CSC_FEATURES & CSC_FEATURE_MULTIPLE_SENSOR_LOC) + case SC_CP_OP_UPDATE_SENSOR_LOCATION: + /* Read new sensor location value*/ + rc = os_mbuf_copydata(ctxt->om, 1, 1, &new_sensor_location); + if (rc != 0){ + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + MODLOG_DFLT(INFO, "SC Control Point; Sensor location update = %d\n", + new_sensor_location); + + /* Verify if requested new location is on supported locations list */ + response = SC_CP_RESPONSE_INVALID_PARAM; + for (ii = 0; ii < sizeof(csc_supported_sensor_locations); ii++){ + if (new_sensor_location == csc_supported_sensor_locations[ii]){ + sensor_location = new_sensor_location; + response = SC_CP_RESPONSE_SUCCESS; + break; + } + } + break; + + case SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS: + response = SC_CP_RESPONSE_SUCCESS; + break; +#endif + + default: + break; + } + + /* Append response value */ + rc = os_mbuf_append(om_indication, &response, sizeof(response)); + + if (rc != 0){ + return BLE_ATT_ERR_INSUFFICIENT_RES; + } + +#if (CSC_FEATURES & CSC_FEATURE_MULTIPLE_SENSOR_LOC) + /* In case of supported locations request append locations list */ + if (op_code == SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS){ + rc = os_mbuf_append(om_indication, &csc_supported_sensor_locations, + sizeof(csc_supported_sensor_locations)); + } + + if (rc != 0){ + return BLE_ATT_ERR_INSUFFICIENT_RES; + } +#endif + + rc = ble_gattc_indicate_custom(conn_handle, csc_control_point_handle, + om_indication); + + return rc; +} + +static int +gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + uint16_t uuid; + int rc; + + uuid = ble_uuid_u16(ctxt->chr->uuid); + + if (uuid == GATT_MODEL_NUMBER_UUID) { + rc = os_mbuf_append(ctxt->om, model_num, strlen(model_num)); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + if (uuid == GATT_MANUFACTURER_NAME_UUID) { + rc = os_mbuf_append(ctxt->om, manuf_name, strlen(manuf_name)); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + assert(0); + return BLE_ATT_ERR_UNLIKELY; +} + +int +gatt_svr_chr_notify_csc_measurement(uint16_t conn_handle) +{ + int rc; + struct os_mbuf *om; + uint8_t data_buf[11]; + uint8_t data_offset = 1; + + memset(data_buf, 0, sizeof(data_buf)); + +#if (CSC_FEATURES & CSC_FEATURE_WHEEL_REV_DATA) + data_buf[0] |= CSC_MEASUREMENT_WHEEL_REV_PRESENT; + put_le16(&(data_buf[5]), measurement_state->last_wheel_evt_time); + put_le32(&(data_buf[1]), measurement_state->cumulative_wheel_rev); + data_offset += 6; +#endif + +#if (CSC_FEATURES & CSC_FEATURE_CRANK_REV_DATA) + data_buf[0] |= CSC_MEASUREMENT_CRANK_REV_PRESENT; + put_le16(&(data_buf[data_offset]), + measurement_state->cumulative_crank_rev); + put_le16(&(data_buf[data_offset + 2]), + measurement_state->last_crank_evt_time); + data_offset += 4; +#endif + + om = ble_hs_mbuf_from_flat(data_buf, data_offset); + + rc = ble_gattc_notify_custom(conn_handle, csc_measurement_handle, om); + return rc; +} + +void +gatt_svr_set_cp_indicate(uint8_t indication_status) +{ + csc_cp_indication_status = indication_status; +} + +void +gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) +{ + char buf[BLE_UUID_STR_LEN]; + + switch (ctxt->op) { + case BLE_GATT_REGISTER_OP_SVC: + MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n", + ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf), + ctxt->svc.handle); + break; + + case BLE_GATT_REGISTER_OP_CHR: + MODLOG_DFLT(DEBUG, "registering characteristic %s with " + "def_handle=%d val_handle=%d\n", + ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf), + ctxt->chr.def_handle, + ctxt->chr.val_handle); + break; + + case BLE_GATT_REGISTER_OP_DSC: + MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n", + ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf), + ctxt->dsc.handle); + break; + + default: + assert(0); + break; + } +} + +int +gatt_svr_init(struct ble_csc_measurement_state * csc_measurement_state) +{ + int rc; + + rc = ble_gatts_count_cfg(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_add_svcs(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + measurement_state = csc_measurement_state; + + return 0; +} + diff --git a/src/libs/mynewt-nimble/apps/blecsc/src/main.c b/src/libs/mynewt-nimble/apps/blecsc/src/main.c new file mode 100644 index 00000000..60f0b3d8 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blecsc/src/main.c @@ -0,0 +1,310 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include + +#include "os/mynewt.h" +#include "console/console.h" +#include "config/config.h" +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "services/gap/ble_svc_gap.h" +#include "blecsc_sens.h" + +/* Wheel size for simulation calculations */ +#define CSC_SIM_WHEEL_CIRCUMFERENCE_MM 2000 +/* Simulated cadence lower limit */ +#define CSC_SIM_CRANK_RPM_MIN 20 +/* Simulated cadence upper limit */ +#define CSC_SIM_CRANK_RPM_MAX 100 +/* Simulated speed lower limit */ +#define CSC_SIM_SPEED_KPH_MIN 0 +/* Simulated speed upper limit */ +#define CSC_SIM_SPEED_KPH_MAX 35 + +/* Noticication status */ +static bool notify_state = false; + +/* Connection handle */ +static uint16_t conn_handle; + +static uint8_t blecsc_addr_type; + +/* Advertised device name */ +static const char *device_name = "blecsc_sensor"; + +/* Measurement and notification timer */ +static struct os_callout blecsc_measure_timer; + +/* Variable holds current CSC measurement state */ +static struct ble_csc_measurement_state csc_measurement_state; + +/* Variable holds simulted speed (kilometers per hour) */ +static uint16_t csc_sim_speed_kph = CSC_SIM_SPEED_KPH_MIN; + +/* Variable holds simulated cadence (RPM) */ +static uint8_t csc_sim_crank_rpm = CSC_SIM_CRANK_RPM_MIN; + +static int blecsc_gap_event(struct ble_gap_event *event, void *arg); + + +/* + * Enables advertising with parameters: + * o General discoverable mode + * o Undirected connectable mode + */ +static void +blecsc_advertise(void) +{ + struct ble_gap_adv_params adv_params; + struct ble_hs_adv_fields fields; + int rc; + + /* + * Set the advertisement data included in our advertisements: + * o Flags (indicates advertisement type and other general info) + * o Advertising tx power + * o Device name + */ + memset(&fields, 0, sizeof(fields)); + + /* + * Advertise two flags: + * o Discoverability in forthcoming advertisement (general) + * o BLE-only (BR/EDR unsupported) + */ + fields.flags = BLE_HS_ADV_F_DISC_GEN | + BLE_HS_ADV_F_BREDR_UNSUP; + + /* + * Indicate that the TX power level field should be included; have the + * stack fill this value automatically. This is done by assigning the + * special value BLE_HS_ADV_TX_PWR_LVL_AUTO. + */ + fields.tx_pwr_lvl_is_present = 1; + fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + + fields.name = (uint8_t *)device_name; + fields.name_len = strlen(device_name); + fields.name_is_complete = 1; + + /* + * Set appearance. + */ + fields.appearance = ble_svc_gap_device_appearance(); + fields.appearance_is_present = 1; + + rc = ble_gap_adv_set_fields(&fields); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc); + return; + } + + /* Begin advertising */ + memset(&adv_params, 0, sizeof(adv_params)); + adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; + adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + rc = ble_gap_adv_start(blecsc_addr_type, NULL, BLE_HS_FOREVER, + &adv_params, blecsc_gap_event, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc); + return; + } +} + + +/* Update simulated CSC measurements. + * Each call increments wheel and crank revolution counters by one and + * computes last event time in order to match simulated candence and speed. + * Last event time is expressedd in 1/1024th of second units. + * + * 60 * 1024 + * crank_dt = -------------- + * cadence[RPM] + * + * + * circumference[mm] * 1024 * 60 * 60 + * wheel_dt = ------------------------------------- + * 10^6 * speed [kph] + */ +static void +blecsc_simulate_speed_and_cadence(void) +{ + uint16_t wheel_rev_period; + uint16_t crank_rev_period; + + /* Update simulated crank and wheel rotation speed */ + csc_sim_speed_kph++; + if (csc_sim_speed_kph >= CSC_SIM_SPEED_KPH_MAX) { + csc_sim_speed_kph = CSC_SIM_SPEED_KPH_MIN; + } + + csc_sim_crank_rpm++; + if (csc_sim_crank_rpm >= CSC_SIM_CRANK_RPM_MAX) { + csc_sim_crank_rpm = CSC_SIM_CRANK_RPM_MIN; + } + + /* Calculate simulated measurement values */ + if (csc_sim_speed_kph > 0){ + wheel_rev_period = (36*64*CSC_SIM_WHEEL_CIRCUMFERENCE_MM) / + (625*csc_sim_speed_kph); + csc_measurement_state.cumulative_wheel_rev++; + csc_measurement_state.last_wheel_evt_time += wheel_rev_period; + } + + if (csc_sim_crank_rpm > 0){ + crank_rev_period = (60*1024) / csc_sim_crank_rpm; + csc_measurement_state.cumulative_crank_rev++; + csc_measurement_state.last_crank_evt_time += crank_rev_period; + } + + MODLOG_DFLT(INFO, "CSC simulated values: speed = %d kph, cadence = %d \n", + csc_sim_speed_kph, csc_sim_crank_rpm); +} + +/* Run CSC measurement simulation and notify it to the client */ +static void +blecsc_measurement(struct os_event *ev) +{ + int rc; + + rc = os_callout_reset(&blecsc_measure_timer, OS_TICKS_PER_SEC); + assert(rc == 0); + + blecsc_simulate_speed_and_cadence(); + + if (notify_state) { + rc = gatt_svr_chr_notify_csc_measurement(conn_handle); + assert(rc == 0); + } +} + +static int +blecsc_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed */ + MODLOG_DFLT(INFO, "connection %s; status=%d\n", + event->connect.status == 0 ? "established" : "failed", + event->connect.status); + + if (event->connect.status != 0) { + /* Connection failed; resume advertising */ + blecsc_advertise(); + conn_handle = 0; + } + else { + conn_handle = event->connect.conn_handle; + } + break; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "disconnect; reason=%d\n", event->disconnect.reason); + conn_handle = 0; + /* Connection terminated; resume advertising */ + blecsc_advertise(); + break; + + case BLE_GAP_EVENT_ADV_COMPLETE: + MODLOG_DFLT(INFO, "adv complete\n"); + break; + + case BLE_GAP_EVENT_SUBSCRIBE: + MODLOG_DFLT(INFO, "subscribe event attr_handle=%d\n", + event->subscribe.attr_handle); + + if (event->subscribe.attr_handle == csc_measurement_handle) { + notify_state = event->subscribe.cur_notify; + MODLOG_DFLT(INFO, "csc measurement notify state = %d\n", + notify_state); + } + else if (event->subscribe.attr_handle == csc_control_point_handle) { + gatt_svr_set_cp_indicate(event->subscribe.cur_indicate); + MODLOG_DFLT(INFO, "csc control point indicate state = %d\n", + event->subscribe.cur_indicate); + } + break; + + case BLE_GAP_EVENT_MTU: + MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d mtu=%d\n", + event->mtu.conn_handle, + event->mtu.value); + break; + + } + + return 0; +} + +static void +blecsc_on_sync(void) +{ + int rc; + + /* Figure out address to use while advertising (no privacy) */ + rc = ble_hs_id_infer_auto(0, &blecsc_addr_type); + assert(rc == 0); + + /* Begin advertising */ + blecsc_advertise(); +} + +/* + * main + * + * The main task for the project. This function initializes the packages, + * then starts serving events from default event queue. + * + * @return int NOTE: this function should never return! + */ +int +main(void) +{ + int rc; + + /* Initialize OS */ + sysinit(); + + /* Initialize the NimBLE host configuration */ + ble_hs_cfg.sync_cb = blecsc_on_sync; + + /* Initialize measurement and notification timer */ + os_callout_init(&blecsc_measure_timer, os_eventq_dflt_get(), + blecsc_measurement, NULL); + rc = os_callout_reset(&blecsc_measure_timer, OS_TICKS_PER_SEC); + assert(rc == 0); + + rc = gatt_svr_init(&csc_measurement_state); + assert(rc == 0); + + /* Set the default device name */ + rc = ble_svc_gap_device_name_set(device_name); + assert(rc == 0); + + /* As the last thing, process events from default event queue */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} + diff --git a/src/libs/mynewt-nimble/apps/blecsc/syscfg.yml b/src/libs/mynewt-nimble/apps/blecsc/syscfg.yml new file mode 100644 index 00000000..2f41785b --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blecsc/syscfg.yml @@ -0,0 +1,38 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +syscfg.vals: + # Disable central and observer roles. + BLE_ROLE_BROADCASTER: 1 + BLE_ROLE_CENTRAL: 0 + BLE_ROLE_OBSERVER: 0 + BLE_ROLE_PERIPHERAL: 1 + + # Disable unused eddystone feature. + BLE_EDDYSTONE: 0 + + # Log reboot messages to a flash circular buffer. + REBOOT_LOG_FCB: 1 + LOG_FCB: 1 + CONFIG_FCB: 1 + + # Set public device address. + BLE_PUBLIC_DEV_ADDR: ((uint8_t[6]){0xcc, 0xbb, 0xaa, 0x33, 0x22, 0x11}) + + # Set device appearance to Cycling Speed and Cadence Sensor + BLE_SVC_GAP_APPEARANCE: BLE_SVC_GAP_APPEARANCE_CYC_SPEED_AND_CADENCE_SENSOR diff --git a/src/libs/mynewt-nimble/apps/blehci/pkg.yml b/src/libs/mynewt-nimble/apps/blehci/pkg.yml new file mode 100644 index 00000000..06e61894 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blehci/pkg.yml @@ -0,0 +1,34 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: apps/blehci +pkg.type: app +pkg.description: BLE controller application exposing HCI over external interface +pkg.author: "Johan Hedberg " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/sys/console/stub" + - "@apache-mynewt-core/sys/log/stub" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/kernel/os" + - nimble/controller + - nimble/transport + +pkg.req_apis: + - ble_transport diff --git a/src/libs/mynewt-nimble/apps/blehci/src/main.c b/src/libs/mynewt-nimble/apps/blehci/src/main.c new file mode 100644 index 00000000..040c1572 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blehci/src/main.c @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include "os/mynewt.h" + +int +main(void) +{ + /* Initialize OS */ + sysinit(); + + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} diff --git a/src/libs/mynewt-nimble/apps/blehci/syscfg.yml b/src/libs/mynewt-nimble/apps/blehci/syscfg.yml new file mode 100644 index 00000000..48a02005 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blehci/syscfg.yml @@ -0,0 +1,23 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.vals: + # Default task settings + OS_MAIN_STACK_SIZE: 64 + # Use UART transport by default + BLE_HCI_TRANSPORT: uart diff --git a/src/libs/mynewt-nimble/apps/blehr/README.md b/src/libs/mynewt-nimble/apps/blehr/README.md new file mode 100644 index 00000000..d06d95de --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blehr/README.md @@ -0,0 +1,9 @@ +# BLE Heart Rate peripheral app. + +The source files are located in the src/ directory. + +pkg.yml contains the base definition of the app. + +syscfg.yml contains setting definitions and overrides. + + diff --git a/src/libs/mynewt-nimble/apps/blehr/pkg.yml b/src/libs/mynewt-nimble/apps/blehr/pkg.yml new file mode 100644 index 00000000..b91ba377 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blehr/pkg.yml @@ -0,0 +1,40 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: apps/blehr +pkg.type: app +pkg.description: BLE peripheral heartrate sensor. +pkg.author: "Szymon Czapracki" +pkg.email: "szymon.czapracki@codecoup.pl" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/sysinit" + - "@apache-mynewt-core/sys/id" + - nimble/controller + - nimble/host + - nimble/host/services/gap + - nimble/host/services/gatt + - nimble/host/store/config + - nimble/transport/ram diff --git a/src/libs/mynewt-nimble/apps/blehr/src/blehr_sens.h b/src/libs/mynewt-nimble/apps/blehr/src/blehr_sens.h new file mode 100644 index 00000000..a3f59ca9 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blehr/src/blehr_sens.h @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLEHR_SENSOR_ +#define H_BLEHR_SENSOR_ + +#include "nimble/ble.h" +#include "modlog/modlog.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Heart-rate configuration */ +#define GATT_HRS_UUID 0x180D +#define GATT_HRS_MEASUREMENT_UUID 0x2A37 +#define GATT_HRS_BODY_SENSOR_LOC_UUID 0x2A38 +#define GATT_DEVICE_INFO_UUID 0x180A +#define GATT_MANUFACTURER_NAME_UUID 0x2A29 +#define GATT_MODEL_NUMBER_UUID 0x2A24 + +extern uint16_t hrs_hrm_handle; + +int gatt_svr_init(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libs/mynewt-nimble/apps/blehr/src/gatt_svr.c b/src/libs/mynewt-nimble/apps/blehr/src/gatt_svr.c new file mode 100644 index 00000000..b2e9b4e0 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blehr/src/gatt_svr.c @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include "host/ble_hs.h" +#include "host/ble_uuid.h" +#include "blehr_sens.h" + +static const char *manuf_name = "Apache Mynewt"; +static const char *model_num = "Mynewt HR Sensor"; +uint16_t hrs_hrm_handle; + +static int +gatt_svr_chr_access_heart_rate(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +static int +gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +static const struct ble_gatt_svc_def gatt_svr_svcs[] = { + { + /* Service: Heart-rate */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(GATT_HRS_UUID), + .characteristics = (struct ble_gatt_chr_def[]) { { + /* Characteristic: Heart-rate measurement */ + .uuid = BLE_UUID16_DECLARE(GATT_HRS_MEASUREMENT_UUID), + .access_cb = gatt_svr_chr_access_heart_rate, + .val_handle = &hrs_hrm_handle, + .flags = BLE_GATT_CHR_F_NOTIFY, + }, { + /* Characteristic: Body sensor location */ + .uuid = BLE_UUID16_DECLARE(GATT_HRS_BODY_SENSOR_LOC_UUID), + .access_cb = gatt_svr_chr_access_heart_rate, + .flags = BLE_GATT_CHR_F_READ, + }, { + 0, /* No more characteristics in this service */ + }, } + }, + + { + /* Service: Device Information */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(GATT_DEVICE_INFO_UUID), + .characteristics = (struct ble_gatt_chr_def[]) { { + /* Characteristic: * Manufacturer name */ + .uuid = BLE_UUID16_DECLARE(GATT_MANUFACTURER_NAME_UUID), + .access_cb = gatt_svr_chr_access_device_info, + .flags = BLE_GATT_CHR_F_READ, + }, { + /* Characteristic: Model number string */ + .uuid = BLE_UUID16_DECLARE(GATT_MODEL_NUMBER_UUID), + .access_cb = gatt_svr_chr_access_device_info, + .flags = BLE_GATT_CHR_F_READ, + }, { + 0, /* No more characteristics in this service */ + }, } + }, + + { + 0, /* No more services */ + }, +}; + +static int +gatt_svr_chr_access_heart_rate(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + /* Sensor location, set to "Chest" */ + static uint8_t body_sens_loc = 0x01; + uint16_t uuid; + int rc; + + uuid = ble_uuid_u16(ctxt->chr->uuid); + + if (uuid == GATT_HRS_BODY_SENSOR_LOC_UUID) { + rc = os_mbuf_append(ctxt->om, &body_sens_loc, sizeof(body_sens_loc)); + + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + assert(0); + return BLE_ATT_ERR_UNLIKELY; +} + +static int +gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + uint16_t uuid; + int rc; + + uuid = ble_uuid_u16(ctxt->chr->uuid); + + if (uuid == GATT_MODEL_NUMBER_UUID) { + rc = os_mbuf_append(ctxt->om, model_num, strlen(model_num)); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + if (uuid == GATT_MANUFACTURER_NAME_UUID) { + rc = os_mbuf_append(ctxt->om, manuf_name, strlen(manuf_name)); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + assert(0); + return BLE_ATT_ERR_UNLIKELY; +} + +void +gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) +{ + char buf[BLE_UUID_STR_LEN]; + + switch (ctxt->op) { + case BLE_GATT_REGISTER_OP_SVC: + MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n", + ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf), + ctxt->svc.handle); + break; + + case BLE_GATT_REGISTER_OP_CHR: + MODLOG_DFLT(DEBUG, "registering characteristic %s with " + "def_handle=%d val_handle=%d\n", + ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf), + ctxt->chr.def_handle, + ctxt->chr.val_handle); + break; + + case BLE_GATT_REGISTER_OP_DSC: + MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n", + ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf), + ctxt->dsc.handle); + break; + + default: + assert(0); + break; + } +} + +int +gatt_svr_init(void) +{ + int rc; + + rc = ble_gatts_count_cfg(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_add_svcs(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + return 0; +} + diff --git a/src/libs/mynewt-nimble/apps/blehr/src/main.c b/src/libs/mynewt-nimble/apps/blehr/src/main.c new file mode 100644 index 00000000..bf0f3f30 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blehr/src/main.c @@ -0,0 +1,261 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include + +#include "os/mynewt.h" +#include "console/console.h" +#include "config/config.h" +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "services/gap/ble_svc_gap.h" +#include "blehr_sens.h" + +static bool notify_state; + +/* Connection handle */ +static uint16_t conn_handle; + +static const char *device_name = "blehr_sensor"; + +static int blehr_gap_event(struct ble_gap_event *event, void *arg); + +static uint8_t blehr_addr_type; + +/* Sending notify data timer */ +static struct os_callout blehr_tx_timer; + +/* Variable to simulate heart beats */ +static uint8_t heartrate = 90; + +/* + * Enables advertising with parameters: + * o General discoverable mode + * o Undirected connectable mode + */ +static void +blehr_advertise(void) +{ + struct ble_gap_adv_params adv_params; + struct ble_hs_adv_fields fields; + int rc; + + /* + * Set the advertisement data included in our advertisements: + * o Flags (indicates advertisement type and other general info) + * o Advertising tx power + * o Device name + */ + memset(&fields, 0, sizeof(fields)); + + /* + * Advertise two flags: + * o Discoverability in forthcoming advertisement (general) + * o BLE-only (BR/EDR unsupported) + */ + fields.flags = BLE_HS_ADV_F_DISC_GEN | + BLE_HS_ADV_F_BREDR_UNSUP; + + /* + * Indicate that the TX power level field should be included; have the + * stack fill this value automatically. This is done by assigning the + * special value BLE_HS_ADV_TX_PWR_LVL_AUTO. + */ + fields.tx_pwr_lvl_is_present = 1; + fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + + fields.name = (uint8_t *)device_name; + fields.name_len = strlen(device_name); + fields.name_is_complete = 1; + + rc = ble_gap_adv_set_fields(&fields); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc); + return; + } + + /* Begin advertising */ + memset(&adv_params, 0, sizeof(adv_params)); + adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; + adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + rc = ble_gap_adv_start(blehr_addr_type, NULL, BLE_HS_FOREVER, + &adv_params, blehr_gap_event, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc); + return; + } +} + +static void +blehr_tx_hrate_stop(void) +{ + os_callout_stop(&blehr_tx_timer); +} + +/* Reset heartrate measurment */ +static void +blehr_tx_hrate_reset(void) +{ + int rc; + + rc = os_callout_reset(&blehr_tx_timer, OS_TICKS_PER_SEC); + assert(rc == 0); +} + +/* This functions simulates heart beat and notifies it to the client */ +static void +blehr_tx_hrate(struct os_event *ev) +{ + static uint8_t hrm[2]; + int rc; + struct os_mbuf *om; + + if (!notify_state) { + blehr_tx_hrate_stop(); + heartrate = 90; + return; + } + + hrm[0] = 0x06; /* contact of a sensor */ + hrm[1] = heartrate; /* storing dummy data */ + + /* Simulation of heart beats */ + heartrate++; + if (heartrate == 160) { + heartrate = 90; + } + + om = ble_hs_mbuf_from_flat(hrm, sizeof(hrm)); + + rc = ble_gattc_notify_custom(conn_handle, hrs_hrm_handle, om); + + assert(rc == 0); + blehr_tx_hrate_reset(); +} + +static int +blehr_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed */ + MODLOG_DFLT(INFO, "connection %s; status=%d\n", + event->connect.status == 0 ? "established" : "failed", + event->connect.status); + + if (event->connect.status != 0) { + /* Connection failed; resume advertising */ + blehr_advertise(); + conn_handle = 0; + } + else { + conn_handle = event->connect.conn_handle; + } + + break; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "disconnect; reason=%d\n", event->disconnect.reason); + conn_handle = BLE_HS_CONN_HANDLE_NONE; /* reset conn_handle */ + + /* Connection terminated; resume advertising */ + blehr_advertise(); + break; + + case BLE_GAP_EVENT_ADV_COMPLETE: + MODLOG_DFLT(INFO, "adv complete\n"); + blehr_advertise(); + break; + + case BLE_GAP_EVENT_SUBSCRIBE: + MODLOG_DFLT(INFO, "subscribe event; cur_notify=%d\n value handle; " + "val_handle=%d\n", + event->subscribe.cur_notify, hrs_hrm_handle); + if (event->subscribe.attr_handle == hrs_hrm_handle) { + notify_state = event->subscribe.cur_notify; + blehr_tx_hrate_reset(); + } else if (event->subscribe.attr_handle != hrs_hrm_handle) { + notify_state = event->subscribe.cur_notify; + blehr_tx_hrate_stop(); + } + break; + + case BLE_GAP_EVENT_MTU: + MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d mtu=%d\n", + event->mtu.conn_handle, + event->mtu.value); + break; + + } + + return 0; +} + +static void +blehr_on_sync(void) +{ + int rc; + + /* Use privacy */ + rc = ble_hs_id_infer_auto(0, &blehr_addr_type); + assert(rc == 0); + + /* Begin advertising */ + blehr_advertise(); +} + +/* + * main + * + * The main task for the project. This function initializes the packages, + * then starts serving events from default event queue. + * + * @return int NOTE: this function should never return! + */ +int +main(void) +{ + int rc; + + /* Initialize OS */ + sysinit(); + + /* Initialize the NimBLE host configuration */ + ble_hs_cfg.sync_cb = blehr_on_sync; + + os_callout_init(&blehr_tx_timer, os_eventq_dflt_get(), + blehr_tx_hrate, NULL); + + rc = gatt_svr_init(); + assert(rc == 0); + + /* Set the default device name */ + rc = ble_svc_gap_device_name_set(device_name); + assert(rc == 0); + + /* As the last thing, process events from default event queue */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} + diff --git a/src/libs/mynewt-nimble/apps/blehr/syscfg.yml b/src/libs/mynewt-nimble/apps/blehr/syscfg.yml new file mode 100644 index 00000000..a6be2e29 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blehr/syscfg.yml @@ -0,0 +1,35 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +syscfg.vals: + # Disable central and observer roles. + BLE_ROLE_BROADCASTER: 1 + BLE_ROLE_CENTRAL: 0 + BLE_ROLE_OBSERVER: 0 + BLE_ROLE_PERIPHERAL: 1 + + # Disable unused eddystone feature. + BLE_EDDYSTONE: 0 + + # Log reboot messages to a flash circular buffer. + REBOOT_LOG_FCB: 1 + LOG_FCB: 1 + CONFIG_FCB: 1 + + # Set public device address. + BLE_PUBLIC_DEV_ADDR: ((uint8_t[6]){0xcc, 0xbb, 0xaa, 0x33, 0x22, 0x11}) diff --git a/src/libs/mynewt-nimble/apps/blemesh/pkg.yml b/src/libs/mynewt-nimble/apps/blemesh/pkg.yml new file mode 100644 index 00000000..21cbb3d1 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh/pkg.yml @@ -0,0 +1,37 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: apps/blemesh +pkg.type: app +pkg.description: Sample application for BLE Mesh node with on/off model +pkg.author: "Łukasz Rymanowski " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/shell" + - nimble/controller + - nimble/host + - nimble/host/services/gap + - nimble/host/services/gatt + - nimble/host/store/ram + - nimble/transport/ram diff --git a/src/libs/mynewt-nimble/apps/blemesh/src/main.c b/src/libs/mynewt-nimble/apps/blemesh/src/main.c new file mode 100644 index 00000000..24c9950e --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh/src/main.c @@ -0,0 +1,468 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include "os/mynewt.h" +#include "mesh/mesh.h" +#include "console/console.h" +#include "hal/hal_system.h" +#include "hal/hal_gpio.h" +#include "bsp/bsp.h" +#include "shell/shell.h" + +/* BLE */ +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "services/gap/ble_svc_gap.h" +#include "mesh/glue.h" + +/* Company ID */ +#define CID_VENDOR 0x05C3 +#define STANDARD_TEST_ID 0x00 +#define TEST_ID 0x01 +static int recent_test_id = STANDARD_TEST_ID; + +#define FAULT_ARR_SIZE 2 + +static bool has_reg_fault = true; + +static struct bt_mesh_cfg_srv cfg_srv = { + .relay = BT_MESH_RELAY_DISABLED, + .beacon = BT_MESH_BEACON_ENABLED, +#if MYNEWT_VAL(BLE_MESH_FRIEND) + .frnd = BT_MESH_FRIEND_ENABLED, +#else + .gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED, +#endif +#if MYNEWT_VAL(BLE_MESH_GATT_PROXY) + .gatt_proxy = BT_MESH_GATT_PROXY_ENABLED, +#else + .gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED, +#endif + .default_ttl = 7, + + /* 3 transmissions with 20ms interval */ + .net_transmit = BT_MESH_TRANSMIT(2, 20), + .relay_retransmit = BT_MESH_TRANSMIT(2, 20), +}; + +static int +fault_get_cur(struct bt_mesh_model *model, + uint8_t *test_id, + uint16_t *company_id, + uint8_t *faults, + uint8_t *fault_count) +{ + uint8_t reg_faults[FAULT_ARR_SIZE] = { [0 ... FAULT_ARR_SIZE-1] = 0xff }; + + console_printf("fault_get_cur() has_reg_fault %u\n", has_reg_fault); + + *test_id = recent_test_id; + *company_id = CID_VENDOR; + + *fault_count = min(*fault_count, sizeof(reg_faults)); + memcpy(faults, reg_faults, *fault_count); + + return 0; +} + +static int +fault_get_reg(struct bt_mesh_model *model, + uint16_t company_id, + uint8_t *test_id, + uint8_t *faults, + uint8_t *fault_count) +{ + if (company_id != CID_VENDOR) { + return -BLE_HS_EINVAL; + } + + console_printf("fault_get_reg() has_reg_fault %u\n", has_reg_fault); + + *test_id = recent_test_id; + + if (has_reg_fault) { + uint8_t reg_faults[FAULT_ARR_SIZE] = { [0 ... FAULT_ARR_SIZE-1] = 0xff }; + + *fault_count = min(*fault_count, sizeof(reg_faults)); + memcpy(faults, reg_faults, *fault_count); + } else { + *fault_count = 0; + } + + return 0; +} + +static int +fault_clear(struct bt_mesh_model *model, uint16_t company_id) +{ + if (company_id != CID_VENDOR) { + return -BLE_HS_EINVAL; + } + + has_reg_fault = false; + + return 0; +} + +static int +fault_test(struct bt_mesh_model *model, uint8_t test_id, uint16_t company_id) +{ + if (company_id != CID_VENDOR) { + return -BLE_HS_EINVAL; + } + + if (test_id != STANDARD_TEST_ID && test_id != TEST_ID) { + return -BLE_HS_EINVAL; + } + + recent_test_id = test_id; + has_reg_fault = true; + bt_mesh_fault_update(bt_mesh_model_elem(model)); + + return 0; +} + +static const struct bt_mesh_health_srv_cb health_srv_cb = { + .fault_get_cur = &fault_get_cur, + .fault_get_reg = &fault_get_reg, + .fault_clear = &fault_clear, + .fault_test = &fault_test, +}; + +static struct bt_mesh_health_srv health_srv = { + .cb = &health_srv_cb, +}; + +static struct bt_mesh_model_pub health_pub; + +static void +health_pub_init(void) +{ + health_pub.msg = BT_MESH_HEALTH_FAULT_MSG(0); +} + +static struct bt_mesh_model_pub gen_level_pub; +static struct bt_mesh_model_pub gen_onoff_pub; + +static uint8_t gen_on_off_state; +static int16_t gen_level_state; + +static void gen_onoff_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(3); + uint8_t *status; + + console_printf("#mesh-onoff STATUS\n"); + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x04)); + status = net_buf_simple_add(msg, 1); + *status = gen_on_off_state; + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + console_printf("#mesh-onoff STATUS: send status failed\n"); + } + + os_mbuf_free_chain(msg); +} + +static void gen_onoff_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + console_printf("#mesh-onoff GET\n"); + + gen_onoff_status(model, ctx); +} + +static void gen_onoff_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + console_printf("#mesh-onoff SET\n"); + + gen_on_off_state = buf->om_data[0]; + hal_gpio_write(LED_2, !gen_on_off_state); + + gen_onoff_status(model, ctx); +} + +static void gen_onoff_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + console_printf("#mesh-onoff SET-UNACK\n"); + + gen_on_off_state = buf->om_data[0]; + hal_gpio_write(LED_2, !gen_on_off_state); +} + +static const struct bt_mesh_model_op gen_onoff_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x01), 0, gen_onoff_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x02), 2, gen_onoff_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x03), 2, gen_onoff_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +static void gen_level_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(4); + + console_printf("#mesh-level STATUS\n"); + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x08)); + net_buf_simple_add_le16(msg, gen_level_state); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + console_printf("#mesh-level STATUS: send status failed\n"); + } + + os_mbuf_free_chain(msg); +} + +static void gen_level_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + console_printf("#mesh-level GET\n"); + + gen_level_status(model, ctx); +} + +static void gen_level_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + int16_t level; + + level = (int16_t) net_buf_simple_pull_le16(buf); + console_printf("#mesh-level SET: level=%d\n", level); + + gen_level_status(model, ctx); + + gen_level_state = level; + console_printf("#mesh-level: level=%d\n", gen_level_state); +} + +static void gen_level_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + int16_t level; + + level = (int16_t) net_buf_simple_pull_le16(buf); + console_printf("#mesh-level SET-UNACK: level=%d\n", level); + + gen_level_state = level; + console_printf("#mesh-level: level=%d\n", gen_level_state); +} + +static void gen_delta_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + int16_t delta_level; + + delta_level = (int16_t) net_buf_simple_pull_le16(buf); + console_printf("#mesh-level DELTA-SET: delta_level=%d\n", delta_level); + + gen_level_status(model, ctx); + + gen_level_state += delta_level; + console_printf("#mesh-level: level=%d\n", gen_level_state); +} + +static void gen_delta_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + int16_t delta_level; + + delta_level = (int16_t) net_buf_simple_pull_le16(buf); + console_printf("#mesh-level DELTA-SET: delta_level=%d\n", delta_level); + + gen_level_state += delta_level; + console_printf("#mesh-level: level=%d\n", gen_level_state); +} + +static void gen_move_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ +} + +static void gen_move_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ +} + +static const struct bt_mesh_model_op gen_level_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x05), 0, gen_level_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x06), 3, gen_level_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x07), 3, gen_level_set_unack }, + { BT_MESH_MODEL_OP_2(0x82, 0x09), 5, gen_delta_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x0a), 5, gen_delta_set_unack }, + { BT_MESH_MODEL_OP_2(0x82, 0x0b), 3, gen_move_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x0c), 3, gen_move_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +static struct bt_mesh_model root_models[] = { + BT_MESH_MODEL_CFG_SRV(&cfg_srv), + BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_op, + &gen_onoff_pub, NULL), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_SRV, gen_level_op, + &gen_level_pub, NULL), +}; + +static struct bt_mesh_model_pub vnd_model_pub; + +static void vnd_model_recv(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(3); + + console_printf("#vendor-model-recv\n"); + + console_printf("data:%s len:%d\n", bt_hex(buf->om_data, buf->om_len), + buf->om_len); + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_3(0x01, CID_VENDOR)); + os_mbuf_append(msg, buf->om_data, buf->om_len); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + console_printf("#vendor-model-recv: send rsp failed\n"); + } + + os_mbuf_free_chain(msg); +} + +static const struct bt_mesh_model_op vnd_model_op[] = { + { BT_MESH_MODEL_OP_3(0x01, CID_VENDOR), 0, vnd_model_recv }, + BT_MESH_MODEL_OP_END, +}; + +static struct bt_mesh_model vnd_models[] = { + BT_MESH_MODEL_VND(CID_VENDOR, BT_MESH_MODEL_ID_GEN_ONOFF_SRV, vnd_model_op, + &vnd_model_pub, NULL), +}; + +static struct bt_mesh_elem elements[] = { + BT_MESH_ELEM(0, root_models, vnd_models), +}; + +static const struct bt_mesh_comp comp = { + .cid = CID_VENDOR, + .elem = elements, + .elem_count = ARRAY_SIZE(elements), +}; + +static int output_number(bt_mesh_output_action_t action, uint32_t number) +{ + console_printf("OOB Number: %lu\n", number); + + return 0; +} + +static void prov_complete(u16_t net_idx, u16_t addr) +{ + console_printf("Local node provisioned, primary address 0x%04x\n", addr); +} + +static const uint8_t dev_uuid[16] = MYNEWT_VAL(BLE_MESH_DEV_UUID); + +static const struct bt_mesh_prov prov = { + .uuid = dev_uuid, + .output_size = 4, + .output_actions = BT_MESH_DISPLAY_NUMBER | BT_MESH_BEEP | BT_MESH_VIBRATE | BT_MESH_BLINK, + .output_number = output_number, + .complete = prov_complete, +}; + +static void +blemesh_on_reset(int reason) +{ + BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason); +} + +static void +blemesh_on_sync(void) +{ + int err; + ble_addr_t addr; + + console_printf("Bluetooth initialized\n"); + + /* Use NRPA */ + err = ble_hs_id_gen_rnd(1, &addr); + assert(err == 0); + err = ble_hs_id_set_rnd(addr.val); + assert(err == 0); + + err = bt_mesh_init(addr.type, &prov, &comp); + if (err) { + console_printf("Initializing mesh failed (err %d)\n", err); + return; + } + +#if (MYNEWT_VAL(BLE_MESH_SHELL)) + shell_register_default_module("mesh"); +#endif + + console_printf("Mesh initialized\n"); + + if (IS_ENABLED(CONFIG_SETTINGS)) { + settings_load(); + } + + if (bt_mesh_is_provisioned()) { + printk("Mesh network restored from flash\n"); + } +} + +int +main(int argc, char **argv) +{ + +#ifdef ARCH_sim + mcu_sim_parse_args(argc, argv); +#endif + + /* Initialize OS */ + sysinit(); + + /* Initialize the NimBLE host configuration. */ + ble_hs_cfg.reset_cb = blemesh_on_reset; + ble_hs_cfg.sync_cb = blemesh_on_sync; + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + + hal_gpio_init_out(LED_2, 0); + + health_pub_init(); + + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} diff --git a/src/libs/mynewt-nimble/apps/blemesh/syscfg.yml b/src/libs/mynewt-nimble/apps/blemesh/syscfg.yml new file mode 100644 index 00000000..383f1923 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh/syscfg.yml @@ -0,0 +1,39 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.vals: + # Enable the shell task. + SHELL_TASK: 1 + + # Set log level to info (disable debug logging). + LOG_LEVEL: 1 + + # Default task settings + OS_MAIN_STACK_SIZE: 768 + + # MCUmgr SMP is not supported in this app, so disable smp-over-shell. + SHELL_MGMT: 0 + + BLE_MESH: 1 + MSYS_1_BLOCK_COUNT: 48 + + BLE_MESH_ADV_BUF_COUNT: 20 + BLE_MESH_TX_SEG_MAX: 6 + + BLE_MESH_SETTINGS: 0 + CONFIG_NFFS: 0 diff --git a/src/libs/mynewt-nimble/apps/blemesh_light/pkg.yml b/src/libs/mynewt-nimble/apps/blemesh_light/pkg.yml new file mode 100644 index 00000000..9694f18b --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_light/pkg.yml @@ -0,0 +1,37 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: apps/blemesh_light +pkg.type: app +pkg.description: Sample application for BLE Mesh node with Light model +pkg.author: "Michał Narajowski " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/shell" + - nimble/controller + - nimble/host + - nimble/host/services/gap + - nimble/host/services/gatt + - nimble/host/store/ram + - nimble/transport/ram diff --git a/src/libs/mynewt-nimble/apps/blemesh_light/src/light_model.c b/src/libs/mynewt-nimble/apps/blemesh_light/src/light_model.c new file mode 100644 index 00000000..8b00e2c0 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_light/src/light_model.c @@ -0,0 +1,242 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "syscfg/syscfg.h" + +#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS) + +#include "mesh/mesh.h" +#include "bsp.h" +#include "pwm/pwm.h" +#include "light_model.h" +#include "ws2812.h" + +#if (!MYNEWT_VAL(USE_NEOPIXEL)) +#if MYNEWT_VAL(PWM_0) +struct pwm_dev *pwm0; +#endif +#if MYNEWT_VAL(PWM_1) +struct pwm_dev *pwm1; +#endif +#if MYNEWT_VAL(PWM_2) +struct pwm_dev *pwm2; +#endif +#if MYNEWT_VAL(PWM_3) +struct pwm_dev *pwm3; +#endif + +static uint16_t top_val; +#endif + +#if (MYNEWT_VAL(USE_NEOPIXEL)) +static uint32_t neopixel[WS2812_NUM_LED]; +#endif + +static u8_t gen_onoff_state; +static s16_t gen_level_state; + +static void light_set_lightness(u8_t percentage) +{ +#if (!MYNEWT_VAL(USE_NEOPIXEL)) + int rc; + + uint16_t pwm_val = (uint16_t) (percentage * top_val / 100); + +#if MYNEWT_VAL(PWM_0) + rc = pwm_set_duty_cycle(pwm0, 0, pwm_val); + assert(rc == 0); +#endif +#if MYNEWT_VAL(PWM_1) + rc = pwm_set_duty_cycle(pwm1, 0, pwm_val); + assert(rc == 0); +#endif +#if MYNEWT_VAL(PWM_2) + rc = pwm_set_duty_cycle(pwm2, 0, pwm_val); + assert(rc == 0); +#endif +#if MYNEWT_VAL(PWM_3) + rc = pwm_set_duty_cycle(pwm3, 0, pwm_val); + assert(rc == 0); +#endif +#else + int i; + u32_t lightness; + u8_t max_lightness = 0x1f; + + lightness = (u8_t) (percentage * max_lightness / 100); + + for (i = 0; i < WS2812_NUM_LED; i++) { + neopixel[i] = (lightness | lightness << 8 | lightness << 16); + } + ws2812_write(neopixel); +#endif +} + +static void update_light_state(void) +{ + u16_t level = (u16_t)gen_level_state; + int percent = 100 * level / 0xffff; + + if (gen_onoff_state == 0) { + percent = 0; + } + light_set_lightness((uint8_t) percent); +} + +int light_model_gen_onoff_get(struct bt_mesh_model *model, u8_t *state) +{ + *state = gen_onoff_state; + return 0; +} + +int light_model_gen_onoff_set(struct bt_mesh_model *model, u8_t state) +{ + gen_onoff_state = state; + update_light_state(); + return 0; +} + +int light_model_gen_level_get(struct bt_mesh_model *model, s16_t *level) +{ + *level = gen_level_state; + return 0; +} + +int light_model_gen_level_set(struct bt_mesh_model *model, s16_t level) +{ + gen_level_state = level; + if ((u16_t)gen_level_state > 0x0000) { + gen_onoff_state = 1; + } + if ((u16_t)gen_level_state == 0x0000) { + gen_onoff_state = 0; + } + update_light_state(); + return 0; +} + +int light_model_light_lightness_get(struct bt_mesh_model *model, s16_t *lightness) +{ + return light_model_gen_level_get(model, lightness); +} + +int light_model_light_lightness_set(struct bt_mesh_model *model, s16_t lightness) +{ + return light_model_gen_level_set(model, lightness); +} + +#if (!MYNEWT_VAL(USE_NEOPIXEL)) +struct pwm_dev_cfg dev_conf = { + .n_cycles = 0, + .int_prio = 3, +}; + +#if MYNEWT_VAL(PWM_0) +static struct pwm_chan_cfg led1_conf = { + .pin = LED_1, + .inverted = true, +}; +#endif + +#if MYNEWT_VAL(PWM_1) +static struct pwm_chan_cfg led2_conf = { + .pin = LED_2, + .inverted = true, +}; +#endif + +#if MYNEWT_VAL(PWM_2) +static struct pwm_chan_cfg led3_conf = { + .pin = LED_3, + .inverted = true, +}; +#endif +#endif + +#if MYNEWT_VAL(PWM_3) +static struct pwm_chan_cfg led4_conf = { + .pin = LED_4, + .inverted = true, +}; +#endif + +#if (!MYNEWT_VAL(USE_NEOPIXEL)) +void init_pwm_dev(struct pwm_dev **pwm, char *dev_name, struct pwm_chan_cfg *chan_cfg) +{ + int rc = 0; + + *pwm = (struct pwm_dev *) os_dev_open(dev_name, 0, NULL); + assert(pwm); + rc = pwm_configure_device(*pwm, &dev_conf); + assert(rc == 0); + rc = pwm_configure_channel(*pwm, 0, chan_cfg); + assert(rc == 0); + rc = pwm_enable(*pwm); + assert(rc == 0); +} + +int pwm_init(void) +{ + +#if MYNEWT_VAL(PWM_0) + init_pwm_dev(&pwm0, "pwm0", &led1_conf); +#endif + +#if MYNEWT_VAL(PWM_1) + init_pwm_dev(&pwm1, "pwm1", &led2_conf); +#endif + +#if MYNEWT_VAL(PWM_2) + init_pwm_dev(&pwm2, "pwm2", &led3_conf); +#endif + +#if MYNEWT_VAL(PWM_3) + init_pwm_dev(&pwm3, "pwm3", &led4_conf); +#endif + + if (!pwm0) { + return 0; + } + + top_val = (uint16_t) pwm_get_top_value(pwm0); + update_light_state(); + + return 0; +} +#endif +#endif + +int light_model_init(void) +{ +#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS) + int rc; +#if (!MYNEWT_VAL(USE_NEOPIXEL)) + rc = pwm_init(); + assert(rc == 0); +#else + rc = ws2812_init(); + assert(rc == 0); + update_light_state(); +#endif + return rc; +#else + return 0; +#endif +} + diff --git a/src/libs/mynewt-nimble/apps/blemesh_light/src/light_model.h b/src/libs/mynewt-nimble/apps/blemesh_light/src/light_model.h new file mode 100644 index 00000000..7fcdd0c3 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_light/src/light_model.h @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * Copyright (c) 2017 Intel Corporation + * + */ + +#ifndef __BT_MESH_LIGHT_MODEL_H +#define __BT_MESH_LIGHT_MODEL_H + +#include "syscfg/syscfg.h" +#include "mesh/mesh.h" + +int light_model_gen_onoff_get(struct bt_mesh_model *model, u8_t *state); +int light_model_gen_onoff_set(struct bt_mesh_model *model, u8_t state); +int light_model_gen_level_get(struct bt_mesh_model *model, s16_t *level); +int light_model_gen_level_set(struct bt_mesh_model *model, s16_t level); +int light_model_light_lightness_get(struct bt_mesh_model *model, s16_t *lightness); +int light_model_light_lightness_set(struct bt_mesh_model *model, s16_t lightness); +int light_model_init(void); + +#endif diff --git a/src/libs/mynewt-nimble/apps/blemesh_light/src/main.c b/src/libs/mynewt-nimble/apps/blemesh_light/src/main.c new file mode 100644 index 00000000..51d86eb5 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_light/src/main.c @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include +#include "sysinit/sysinit.h" +#include "os/os.h" +#include "mesh/mesh.h" +#include "console/console.h" +#include "bsp/bsp.h" +#include "shell/shell.h" + +#include "host/ble_hs.h" +#include "mesh/glue.h" +#include "mesh/testing.h" +#include "mesh/model_srv.h" +#include "light_model.h" + + +static void model_bound_cb(u16_t addr, struct bt_mesh_model *model, + u16_t key_idx) +{ + int rc; + + console_printf("Model bound: remote addr 0x%04x key_idx 0x%04x model %p\n", + addr, key_idx, model); + + if (model->id != BT_MESH_MODEL_ID_GEN_LEVEL_SRV) { + return; + } + + /* Hack for demo purposes */ + rc = bt_test_bind_app_key_to_model(model, key_idx, + BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV); + + if (rc) { + console_printf("Failed to bind light lightness srv model to app_key"); + } else { + console_printf("Successfuly bound light lightness srv model to app_key"); + } +} + +static struct bt_test_cb bt_test_cb = { + .mesh_model_bound = model_bound_cb, +}; + +static void +blemesh_on_reset(int reason) +{ + BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason); +} + +static void +blemesh_on_sync(void) +{ + console_printf("Bluetooth initialized\n"); + + shell_register_default_module("mesh"); + + bt_test_cb_register(&bt_test_cb); + + light_model_init(); + bt_mesh_set_gen_onoff_srv_cb(light_model_gen_onoff_get, + light_model_gen_onoff_set); + bt_mesh_set_gen_level_srv_cb(light_model_gen_level_get, + light_model_gen_level_set); + bt_mesh_set_light_lightness_srv_cb(light_model_light_lightness_get, + light_model_light_lightness_set); + + console_printf("Mesh initialized\n"); + + if (IS_ENABLED(CONFIG_SETTINGS)) { + settings_load(); + } + + if (bt_mesh_is_provisioned()) { + printk("Mesh network restored from flash\n"); + } + + /* Hack for demo purposes */ + bt_test_shell_init(); +} + +int +main(void) +{ + /* Initialize OS */ + sysinit(); + + /* Initialize the NimBLE host configuration. */ + ble_hs_cfg.reset_cb = blemesh_on_reset; + ble_hs_cfg.sync_cb = blemesh_on_sync; + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} diff --git a/src/libs/mynewt-nimble/apps/blemesh_light/src/ws2812.c b/src/libs/mynewt-nimble/apps/blemesh_light/src/ws2812.c new file mode 100644 index 00000000..f27a182b --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_light/src/ws2812.c @@ -0,0 +1,138 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "syscfg/syscfg.h" + +#if (MYNEWT_VAL(USE_NEOPIXEL)) + +#include +#include +#include +#include "sysinit/sysinit.h" +#include "os/os.h" +#include "bsp/bsp.h" +#include "pwm/pwm.h" +#include "nrfx.h" +#include "nrfx_pwm.h" +#include "ws2812.h" + +#define BITS_PER_SEQ (24) +#define BIT0 (0x8000 | 6) +#define BIT1 (0x8000 | 11) + +static const nrfx_pwm_t pwm = NRFX_PWM_INSTANCE(WS2812_PWM); + +static const nrfx_pwm_config_t pwm_config = { + .output_pins = { WS2812_GPIO, NRFX_PWM_PIN_NOT_USED, NRFX_PWM_PIN_NOT_USED, NRFX_PWM_PIN_NOT_USED }, + .irq_priority = 3, + .base_clock = NRF_PWM_CLK_16MHz, + .count_mode = NRF_PWM_MODE_UP, + .top_value = 20, + .load_mode = NRF_PWM_LOAD_COMMON, + .step_mode = NRF_PWM_STEP_AUTO, +}; + +static uint16_t pwm_seq_values[2][BITS_PER_SEQ]; + +static const nrf_pwm_sequence_t pwm_seq[2] = { + { + .values.p_raw = pwm_seq_values[0], + .length = BITS_PER_SEQ, + .repeats = 0, + .end_delay = 0, + }, { + .values.p_raw = pwm_seq_values[1], + .length = BITS_PER_SEQ, + .repeats = 0, + .end_delay = 0, + }, +}; + +static uint32_t led_color[WS2812_NUM_LED]; +static int led_idx; + +static void +load_pixel(void) +{ + uint16_t *seq_values; + uint32_t grb; + int i; + + seq_values = pwm_seq_values[led_idx & 1]; + grb = led_color[led_idx]; + + for (i = 0; i < BITS_PER_SEQ; i++) { + *seq_values = grb & 0x800000 ? BIT1 : BIT0; + grb <<= 1; + seq_values++; + } + + led_idx++; +} + +static void +pwm_handler_func(nrfx_pwm_evt_type_t event_type) +{ + switch (event_type) { + case NRFX_PWM_EVT_END_SEQ0: + case NRFX_PWM_EVT_END_SEQ1: + load_pixel(); + break; + default: + break; + } +} + +int +ws2812_init(void) +{ + nrfx_err_t err; + + err = nrfx_pwm_init(&pwm, &pwm_config, pwm_handler_func); + + return err != NRFX_SUCCESS; +} + +int +ws2812_write(const uint32_t *rgb) +{ + uint32_t grb; + int i; + + for (i = 0; i < WS2812_NUM_LED; i++) { + grb = 0; + grb |= (rgb[i] & 0x00FF00) << 8; + grb |= (rgb[i] & 0xFF0000) >> 8; + grb |= (rgb[i] & 0x0000FF); + + led_color[i] = grb; + } + + led_idx = 0; + + load_pixel(); + load_pixel(); + nrfx_pwm_complex_playback(&pwm, &pwm_seq[0], &pwm_seq[1], WS2812_NUM_LED, + NRFX_PWM_FLAG_SIGNAL_END_SEQ0 | + NRFX_PWM_FLAG_SIGNAL_END_SEQ1 | + NRFX_PWM_FLAG_STOP); + + return 0; +} +#endif diff --git a/src/libs/mynewt-nimble/apps/blemesh_light/src/ws2812.h b/src/libs/mynewt-nimble/apps/blemesh_light/src/ws2812.h new file mode 100644 index 00000000..93b8faf8 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_light/src/ws2812.h @@ -0,0 +1,42 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef __WS2812_H__ +#define __WS2812_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define WS2812_PWM 0 +#define WS2812_GPIO 30 +#define WS2812_NUM_LED 32 + +int ws2812_init(void); + +int ws2812_write(const uint32_t *rgb); + +#ifdef __cplusplus +} +#endif + +#endif /* __WS2812_H__ */ diff --git a/src/libs/mynewt-nimble/apps/blemesh_light/syscfg.yml b/src/libs/mynewt-nimble/apps/blemesh_light/syscfg.yml new file mode 100644 index 00000000..82c70dc2 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_light/syscfg.yml @@ -0,0 +1,63 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.defs: + USE_NEOPIXEL: + value: 0 + +syscfg.vals: + # Enable the shell task. + SHELL_TASK: 1 + + # Set log level to info (disable debug logging). + LOG_LEVEL: 1 + + # Default task settings + OS_MAIN_STACK_SIZE: 768 + + # SMP is not supported in this app, so disable smp-over-shell. + SHELL_MGMT: 0 + + MSYS_1_BLOCK_COUNT: 80 + + BLE_MESH_ADV_BUF_COUNT: 20 + BLE_MESH_TX_SEG_MAX: 6 + + BLE_MESH: 1 + BLE_MESH_SHELL: 1 + BLE_MESH_PROV: 1 + BLE_MESH_PB_ADV: 1 + BLE_MESH_PB_GATT: 1 + BLE_MESH_GATT_PROXY: 1 + BLE_MESH_TESTING: 1 + BLE_MESH_FRIEND: 0 + BLE_MESH_CFG_CLI: 1 + BLE_MESH_HEALTH_CLI: 0 + BLE_MESH_SHELL_MODELS: 1 + BLE_MESH_OOB_OUTPUT_ACTIONS: 0 + BLE_MESH_SETTINGS: 0 + CONFIG_NFFS: 0 + + USE_NEOPIXEL: 0 + +syscfg.vals.BLE_MESH_SHELL_MODELS: + PWM_0: 1 + PWM_1: 1 + PWM_2: 1 + PWM_3: 1 + diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_1/README.md b/src/libs/mynewt-nimble/apps/blemesh_models_example_1/README.md new file mode 100644 index 00000000..fde49536 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_1/README.md @@ -0,0 +1,79 @@ +### Bluetooth: Mesh OnOff Model + + +#### Overview + +This is a simple application demonstrating a Bluetooth mesh multi-element node. +Each element has a mesh onoff client and server +model which controls one of the 4 sets of buttons and LEDs . + +Prior to provisioning, an unprovisioned beacon is broadcast that contains +a UUID. Each button controls the state of its +corresponding LED and does not initiate any mesh activity. + +The models for button 1 and LED 1 are in the node's root element. +The 3 remaining button/LED pairs are in elements 1 through 3. +Assuming the provisioner assigns 0x100 to the root element, +the secondary elements will appear at 0x101, 0x102 and 0x103. + +After provisioning, the button clients must +be configured to publish and the LED servers to subscribe. + +If a LED server is provided with a publish address, it will +also publish its status on an onoff state change. + +If a button is pressed only once within a 1 second interval, it sends an +"on" message. If it is pressed more than once, it +sends an "off" message. The buttons are quite noisy and are debounced in +the button_pressed() interrupt handler. An interrupt within 250ms of the +previous is discarded. This might seem a little clumsy, but the alternative of +using one button for "on" and another for "off" would reduce the number +of onoff clients from 4 to 2. + +#### Requirements +************ + +This sample has been tested on the Nordic nRF52840-PDK board, but would +likely also run on the nrf52_pca10040 board. + +#### Building and Running +******************** + +Prior to provisioning, each button controls its corresponding LED as one +would expect with an actual switch. + +Provisioning is done using the BlueZ meshctl utility. Below is an example that +binds button 2 and LED 1 to application key 1. It then configures button 2 +to publish to group 0xc000 and LED 1 to subscribe to that group. + +``` +discover-unprovisioned on +provision +menu config +target 0100 +appkey-add 1 +bind 0 1 1000 # bind appkey 1 to LED server on element 0 (unicast 0100) +sub-add 0100 c000 1000 # add subscription to group address c000 to the LED server +bind 1 1 1001 # bind appkey 1 to button 2 on element 1 (unicast 0101) +pub-set 0101 c000 1 0 0 1001 # publish button 2 to group address c000 +``` + +The meshctl utility maintains a persistent JSON database containing +the mesh configuration. As additional nodes (boards) are provisioned, it +assigns sequential unicast addresses based on the number of elements +supported by the node. This example supports 4 elements per node. + +The first or root element of the node contains models for configuration, +health, and onoff. The secondary elements only +have models for onoff. The meshctl target for configuration must be the +root element's unicast address as it is the only one that has a +configuration server model. + +If meshctl is gracefully exited, it can be restarted and reconnected to +network 0x0. + +The meshctl utility also supports a onoff model client that can be used to +change the state of any LED that is bound to application key 0x1. +This is done by setting the target to the unicast address of the element +that has that LED's model and issuing the onoff command. +Group addresses are not supported. diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_1/pkg.yml b/src/libs/mynewt-nimble/apps/blemesh_models_example_1/pkg.yml new file mode 100644 index 00000000..451f37a1 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_1/pkg.yml @@ -0,0 +1,34 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: apps/blemesh_models_example_1 +pkg.type: app +pkg.description: Sample application for BLE Mesh node with on/off model on nRF52840pdk +pkg.author: "Michał Narajowski " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/stats/full" + - nimble/controller + - nimble/host + - nimble/host/services/gap + - nimble/host/services/gatt + - nimble/transport/ram diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_1/src/main.c b/src/libs/mynewt-nimble/apps/blemesh_models_example_1/src/main.c new file mode 100644 index 00000000..ef398c9f --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_1/src/main.c @@ -0,0 +1,709 @@ +/* main.c - Application main entry point */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * This application is specific to the Nordic nRF52840-PDK board. + * + * It supports the 4 buttons and 4 LEDs as mesh clients and servers. + * + * Prior to provisioning, a button inverts the state of the + * corresponding LED. + * + * Button and LED 1 are in the root node. + * The 3 remaining button/LED pairs are in element 1 through 3. + * Assuming the provisioner assigns 0x100 to the root node, + * the secondary elements will appear at 0x101, 0x102 and 0x103. + * + * It's anticipated that after provisioning, the button clients would + * be configured to publish and the LED servers to subscribe. + * + * If a LED server is provided with a publish address, it will + * also publish its status on a state change. + * + * Messages from a button to its corresponding LED are ignored as + * the LED's state has already been changed locally by the button client. + * + * The buttons are debounced at a nominal 250ms. That value can be + * changed as needed. + * + */ + +#include "os/mynewt.h" +#include "bsp/bsp.h" +#include "console/console.h" +#include "hal/hal_gpio.h" +#include "host/ble_hs.h" +#include "mesh/glue.h" +#include "mesh/mesh.h" + +#define CID_RUNTIME 0x05C3 + +/* Model Operation Codes */ +#define BT_MESH_MODEL_OP_GEN_ONOFF_GET BT_MESH_MODEL_OP_2(0x82, 0x01) +#define BT_MESH_MODEL_OP_GEN_ONOFF_SET BT_MESH_MODEL_OP_2(0x82, 0x02) +#define BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x03) +#define BT_MESH_MODEL_OP_GEN_ONOFF_STATUS BT_MESH_MODEL_OP_2(0x82, 0x04) + +static void gen_onoff_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf); + +static void gen_onoff_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf); + +static void gen_onoff_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf); + +static void gen_onoff_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf); + +/* + * Server Configuration Declaration + */ + +static struct bt_mesh_cfg_srv cfg_srv = { + .relay = BT_MESH_RELAY_DISABLED, + .beacon = BT_MESH_BEACON_ENABLED, +#if defined(CONFIG_BT_MESH_FRIEND) + .frnd = BT_MESH_FRIEND_ENABLED, +#else + .frnd = BT_MESH_FRIEND_NOT_SUPPORTED, +#endif +#if defined(CONFIG_BT_MESH_GATT_PROXY) + .gatt_proxy = BT_MESH_GATT_PROXY_ENABLED, +#else + .gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED, +#endif + .default_ttl = 7, + + /* 3 transmissions with 20ms interval */ + .net_transmit = BT_MESH_TRANSMIT(2, 20), + .relay_retransmit = BT_MESH_TRANSMIT(2, 20), +}; + +/* + * Client Configuration Declaration + */ + +static struct bt_mesh_cfg_cli cfg_cli = { +}; + +/* + * Health Server Declaration + */ + +static struct bt_mesh_health_srv health_srv = { +}; + +/* + * Publication Declarations + * + * The publication messages are initialized to the + * the size of the opcode + content + * + * For publication, the message must be in static or global as + * it is re-transmitted several times. This occurs + * after the function that called bt_mesh_model_publish() has + * exited and the stack is no longer valid. + * + * Note that the additional 4 bytes for the AppMIC is not needed + * because it is added to a stack variable at the time a + * transmission occurs. + * + */ + +static struct bt_mesh_model_pub health_pub; +static struct bt_mesh_model_pub gen_onoff_pub_srv; +static struct bt_mesh_model_pub gen_onoff_pub_cli; +static struct bt_mesh_model_pub gen_onoff_pub_srv_s_0; +static struct bt_mesh_model_pub gen_onoff_pub_cli_s_0; +static struct bt_mesh_model_pub gen_onoff_pub_srv_s_1; +static struct bt_mesh_model_pub gen_onoff_pub_cli_s_1; +static struct bt_mesh_model_pub gen_onoff_pub_srv_s_2; +static struct bt_mesh_model_pub gen_onoff_pub_cli_s_2; + +static struct os_mbuf *bt_mesh_pub_msg_health_pub; +static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_srv; +static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_cli; +static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_srv_s_0; +static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_cli_s_0; +static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_srv_s_1; +static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_cli_s_1; +static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_srv_s_2; +static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_cli_s_2; + +void init_pub(void) +{ + bt_mesh_pub_msg_health_pub = NET_BUF_SIMPLE(1 + 3 + 0); + bt_mesh_pub_msg_gen_onoff_pub_srv = NET_BUF_SIMPLE(2 + 2); + bt_mesh_pub_msg_gen_onoff_pub_cli = NET_BUF_SIMPLE(2 + 2); + bt_mesh_pub_msg_gen_onoff_pub_srv_s_0 = NET_BUF_SIMPLE(2 + 2); + bt_mesh_pub_msg_gen_onoff_pub_cli_s_0 = NET_BUF_SIMPLE(2 + 2); + bt_mesh_pub_msg_gen_onoff_pub_srv_s_1 = NET_BUF_SIMPLE(2 + 2); + bt_mesh_pub_msg_gen_onoff_pub_cli_s_1 = NET_BUF_SIMPLE(2 + 2); + bt_mesh_pub_msg_gen_onoff_pub_srv_s_2 = NET_BUF_SIMPLE(2 + 2); + bt_mesh_pub_msg_gen_onoff_pub_cli_s_2 = NET_BUF_SIMPLE(2 + 2); + + health_pub.msg = bt_mesh_pub_msg_health_pub; + gen_onoff_pub_srv.msg = bt_mesh_pub_msg_gen_onoff_pub_srv; + gen_onoff_pub_cli.msg = bt_mesh_pub_msg_gen_onoff_pub_cli; + gen_onoff_pub_srv_s_0.msg = bt_mesh_pub_msg_gen_onoff_pub_srv_s_0; + gen_onoff_pub_cli_s_0.msg = bt_mesh_pub_msg_gen_onoff_pub_cli_s_0; + gen_onoff_pub_srv_s_1.msg = bt_mesh_pub_msg_gen_onoff_pub_srv_s_1; + gen_onoff_pub_cli_s_1.msg = bt_mesh_pub_msg_gen_onoff_pub_cli_s_1; + gen_onoff_pub_srv_s_2.msg = bt_mesh_pub_msg_gen_onoff_pub_srv_s_2; + gen_onoff_pub_cli_s_2.msg = bt_mesh_pub_msg_gen_onoff_pub_cli_s_2; +} + +/* + * Models in an element must have unique op codes. + * + * The mesh stack dispatches a message to the first model in an element + * that is also bound to an app key and supports the op code in the + * received message. + * + */ + +/* + * OnOff Model Server Op Dispatch Table + * + */ + +static const struct bt_mesh_model_op gen_onoff_srv_op[] = { + { BT_MESH_MODEL_OP_GEN_ONOFF_GET, 0, gen_onoff_get }, + { BT_MESH_MODEL_OP_GEN_ONOFF_SET, 2, gen_onoff_set }, + { BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK, 2, gen_onoff_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +/* + * OnOff Model Client Op Dispatch Table + */ + +static const struct bt_mesh_model_op gen_onoff_cli_op[] = { + { BT_MESH_MODEL_OP_GEN_ONOFF_STATUS, 1, gen_onoff_status }, + BT_MESH_MODEL_OP_END, +}; + +struct onoff_state { + u8_t current; + u8_t previous; + u8_t led_gpio_pin; +}; + +/* + * Declare and Initialize Element Contexts + * Change to select different GPIO output pins + */ + +static struct onoff_state onoff_state_arr[] = { + { .led_gpio_pin = LED_1 }, + { .led_gpio_pin = LED_2 }, + { .led_gpio_pin = LED_3 }, + { .led_gpio_pin = LED_4 }, +}; + +/* + * + * Element Model Declarations + * + * Element 0 Root Models + */ + +static struct bt_mesh_model root_models[] = { + BT_MESH_MODEL_CFG_SRV(&cfg_srv), + BT_MESH_MODEL_CFG_CLI(&cfg_cli), + BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op, + &gen_onoff_pub_srv, &onoff_state_arr[0]), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op, + &gen_onoff_pub_cli, &onoff_state_arr[0]), +}; + +/* + * Element 1 Models + */ + +static struct bt_mesh_model secondary_0_models[] = { + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op, + &gen_onoff_pub_srv_s_0, &onoff_state_arr[1]), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op, + &gen_onoff_pub_cli_s_0, &onoff_state_arr[1]), +}; + +/* + * Element 2 Models + */ + +static struct bt_mesh_model secondary_1_models[] = { + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op, + &gen_onoff_pub_srv_s_1, &onoff_state_arr[2]), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op, + &gen_onoff_pub_cli_s_1, &onoff_state_arr[2]), +}; + +/* + * Element 3 Models + */ + +static struct bt_mesh_model secondary_2_models[] = { + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op, + &gen_onoff_pub_srv_s_2, &onoff_state_arr[3]), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op, + &gen_onoff_pub_cli_s_2, &onoff_state_arr[3]), +}; + +/* + * Button to Client Model Assignments + */ + +struct bt_mesh_model *mod_cli_sw[] = { + &root_models[4], + &secondary_0_models[1], + &secondary_1_models[1], + &secondary_2_models[1], +}; + +/* + * LED to Server Model Assigmnents + */ + +struct bt_mesh_model *mod_srv_sw[] = { + &root_models[3], + &secondary_0_models[0], + &secondary_1_models[0], + &secondary_2_models[0], +}; + +/* + * Root and Secondary Element Declarations + */ + +static struct bt_mesh_elem elements[] = { + BT_MESH_ELEM(0, root_models, BT_MESH_MODEL_NONE), + BT_MESH_ELEM(0, secondary_0_models, BT_MESH_MODEL_NONE), + BT_MESH_ELEM(0, secondary_1_models, BT_MESH_MODEL_NONE), + BT_MESH_ELEM(0, secondary_2_models, BT_MESH_MODEL_NONE), +}; + +static const struct bt_mesh_comp comp = { + .cid = CID_RUNTIME, + .elem = elements, + .elem_count = ARRAY_SIZE(elements), +}; + +struct sw { + u8_t sw_num; + u8_t onoff_state; + struct os_callout button_work; + struct os_callout button_timer; +}; + + +static u8_t button_press_cnt; +static struct sw sw; + +static u8_t trans_id; +static u32_t time, last_time; +static u16_t primary_addr; +static u16_t primary_net_idx; + +/* + * Generic OnOff Model Server Message Handlers + * + * Mesh Model Specification 3.1.1 + * + */ + +static void gen_onoff_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); + struct onoff_state *state = model->user_data; + + BT_INFO("addr 0x%04x onoff 0x%02x", + bt_mesh_model_elem(model)->addr, state->current); + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_ONOFF_STATUS); + net_buf_simple_add_u8(msg, state->current); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + BT_ERR("Unable to send On Off Status response"); + } + + os_mbuf_free_chain(msg); +} + +static void gen_onoff_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = model->pub->msg; + struct onoff_state *state = model->user_data; + int err; + + state->current = net_buf_simple_pull_u8(buf); + BT_INFO("addr 0x%02x state 0x%02x", + bt_mesh_model_elem(model)->addr, state->current); + + /* Pin set low turns on LED's on the nrf52840-pca10056 board */ + hal_gpio_write(state->led_gpio_pin, + state->current ? 0 : 1); + + /* + * If a server has a publish address, it is required to + * publish status on a state change + * + * See Mesh Profile Specification 3.7.6.1.2 + * + * Only publish if there is an assigned address + */ + + if (state->previous != state->current && + model->pub->addr != BT_MESH_ADDR_UNASSIGNED) { + BT_INFO("publish last 0x%02x cur 0x%02x", + state->previous, + state->current); + state->previous = state->current; + bt_mesh_model_msg_init(msg, + BT_MESH_MODEL_OP_GEN_ONOFF_STATUS); + net_buf_simple_add_u8(msg, state->current); + err = bt_mesh_model_publish(model); + if (err) { + BT_ERR("bt_mesh_model_publish err %d", err); + } + } +} + +static void gen_onoff_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + BT_INFO(""); + + gen_onoff_set_unack(model, ctx, buf); + gen_onoff_get(model, ctx, buf); +} + +static void gen_onoff_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t state; + + state = net_buf_simple_pull_u8(buf); + + BT_INFO("Node 0x%04x OnOff status from 0x%04x with state 0x%02x", + bt_mesh_model_elem(model)->addr, ctx->addr, state); +} + +static int output_number(bt_mesh_output_action_t action, u32_t number) +{ + BT_INFO("OOB Number %u", number); + return 0; +} + +static int output_string(const char *str) +{ + BT_INFO("OOB String %s", str); + return 0; +} + +static void prov_complete(u16_t net_idx, u16_t addr) +{ + BT_INFO("provisioning complete for net_idx 0x%04x addr 0x%04x", + net_idx, addr); + primary_addr = addr; + primary_net_idx = net_idx; +} + +static void prov_reset(void) +{ + bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT); +} + +static u8_t dev_uuid[16] = MYNEWT_VAL(BLE_MESH_DEV_UUID); + +#define BUTTON_DEBOUNCE_DELAY_MS 250 + +/* + * Map GPIO pins to button number + * Change to select different GPIO input pins + */ + +static uint8_t pin_to_sw(int pin_pos) +{ + switch (pin_pos) { + case BUTTON_1: return 0; + case BUTTON_2: return 1; + case BUTTON_3: return 2; + case BUTTON_4: return 3; + default:break; + } + + BT_ERR("No match for GPIO pin 0x%08x", pin_pos); + return 0; +} + +static void button_pressed(struct os_event *ev) +{ + int pin_pos = (int ) ev->ev_arg; + /* + * One button press within a 1 second interval sends an on message + * More than one button press sends an off message + */ + + time = k_uptime_get_32(); + + /* debounce the switch */ + if (time < last_time + BUTTON_DEBOUNCE_DELAY_MS) { + last_time = time; + return; + } + + if (button_press_cnt == 0) { + os_callout_reset(&sw.button_timer, os_time_ms_to_ticks32(K_SECONDS(1))); + } + + BT_INFO("button_press_cnt 0x%02x", button_press_cnt); + button_press_cnt++; + + /* The variable pin_pos is the pin position in the GPIO register, + * not the pin number. It's assumed that only one bit is set. + */ + + sw.sw_num = pin_to_sw(pin_pos); + last_time = time; +} + +/* + * Button Count Timer Worker + */ + +static void button_cnt_timer(struct os_event *work) +{ + struct sw *button_sw = work->ev_arg; + + button_sw->onoff_state = button_press_cnt == 1 ? 1 : 0; + BT_INFO("button_press_cnt 0x%02x onoff_state 0x%02x", + button_press_cnt, button_sw->onoff_state); + button_press_cnt = 0; + os_callout_reset(&sw.button_work, 0); +} + +/* + * Button Pressed Worker Task + */ + +static void button_pressed_worker(struct os_event *work) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(1); + struct bt_mesh_model *mod_cli, *mod_srv; + struct bt_mesh_model_pub *pub_cli, *pub_srv; + struct sw *sw = work->ev_arg; + u8_t sw_idx = sw->sw_num; + int err; + + mod_cli = mod_cli_sw[sw_idx]; + pub_cli = mod_cli->pub; + + mod_srv = mod_srv_sw[sw_idx]; + pub_srv = mod_srv->pub; + (void)pub_srv; + + /* If unprovisioned, just call the set function. + * The intent is to have switch-like behavior + * prior to provisioning. Once provisioned, + * the button and its corresponding led are no longer + * associated and act independently. So, if a button is to + * control its associated led after provisioning, the button + * must be configured to either publish to the led's unicast + * address or a group to which the led is subscribed. + */ + + if (primary_addr == BT_MESH_ADDR_UNASSIGNED) { + struct bt_mesh_msg_ctx ctx = { + .addr = sw_idx + primary_addr, + }; + + /* This is a dummy message sufficient + * for the led server + */ + + net_buf_simple_add_u8(msg, sw->onoff_state); + gen_onoff_set_unack(mod_srv, &ctx, msg); + goto done; + } + + if (pub_cli->addr == BT_MESH_ADDR_UNASSIGNED) { + goto done; + } + + BT_INFO("publish to 0x%04x onoff 0x%04x sw_idx 0x%04x", + pub_cli->addr, sw->onoff_state, sw_idx); + bt_mesh_model_msg_init(pub_cli->msg, + BT_MESH_MODEL_OP_GEN_ONOFF_SET); + net_buf_simple_add_u8(pub_cli->msg, sw->onoff_state); + net_buf_simple_add_u8(pub_cli->msg, trans_id++); + err = bt_mesh_model_publish(mod_cli); + if (err) { + BT_ERR("bt_mesh_model_publish err %d", err); + } + +done: + os_mbuf_free_chain(msg); +} + +/* Disable OOB security for SILabs Android app */ + +static const struct bt_mesh_prov prov = { + .uuid = dev_uuid, +#if 1 + .output_size = 6, + .output_actions = (BT_MESH_DISPLAY_NUMBER | BT_MESH_DISPLAY_STRING), + .output_number = output_number, + .output_string = output_string, +#else +.output_size = 0, + .output_actions = 0, + .output_number = 0, +#endif + .complete = prov_complete, + .reset = prov_reset, +}; + +void init_led(u8_t dev) +{ + hal_gpio_init_out(onoff_state_arr[dev].led_gpio_pin, 1); +} + +static struct os_event button_event; + +static void +gpio_irq_handler(void *arg) +{ + button_event.ev_arg = arg; + os_eventq_put(os_eventq_dflt_get(), &button_event); +} + +void init_button(int button) +{ + button_event.ev_cb = button_pressed; + + hal_gpio_irq_init(button, gpio_irq_handler, (void *)button, + HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP); + hal_gpio_irq_enable(button); +} + +static void +blemesh_on_reset(int reason) +{ + BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason); +} + +static void +blemesh_on_sync(void) +{ + int err; + ble_addr_t addr; + + console_printf("Bluetooth initialized\n"); + + /* Use NRPA */ + err = ble_hs_id_gen_rnd(1, &addr); + assert(err == 0); + err = ble_hs_id_set_rnd(addr.val); + assert(err == 0); + + err = bt_mesh_init(addr.type, &prov, &comp); + if (err) { + console_printf("Initializing mesh failed (err %d)\n", err); + return; + } + + if (IS_ENABLED(CONFIG_SETTINGS)) { + settings_load(); + } + + if (bt_mesh_is_provisioned()) { + console_printf("Mesh network restored from flash\n"); + } + + bt_mesh_prov_enable(BT_MESH_PROV_GATT | BT_MESH_PROV_ADV); + + console_printf("Mesh initialized\n"); +} + +int main(void) +{ +#ifdef ARCH_sim + mcu_sim_parse_args(argc, argv); +#endif + + /* Initialize OS */ + sysinit(); + + BT_INFO("Initializing..."); + + /* Initialize the button debouncer */ + last_time = k_uptime_get_32(); + + /* Initialize button worker task*/ + os_callout_init(&sw.button_work, os_eventq_dflt_get(), + button_pressed_worker, &sw); + + /* Initialize button count timer */ + os_callout_init(&sw.button_timer, os_eventq_dflt_get(), + button_cnt_timer, &sw); + + /* Initialize LED's */ + init_led(0); + init_led(1); + init_led(2); + init_led(3); + + init_button(BUTTON_1); + init_button(BUTTON_2); + init_button(BUTTON_3); + init_button(BUTTON_4); + + init_pub(); + + /* Initialize the NimBLE host configuration. */ + ble_hs_cfg.reset_cb = blemesh_on_reset; + ble_hs_cfg.sync_cb = blemesh_on_sync; + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + + return 0; +} diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_1/syscfg.yml b/src/libs/mynewt-nimble/apps/blemesh_models_example_1/syscfg.yml new file mode 100644 index 00000000..969753fe --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_1/syscfg.yml @@ -0,0 +1,38 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.vals: + # Set log level to info (disable debug logging). + LOG_LEVEL: 1 + + # Default task settings + OS_MAIN_STACK_SIZE: 768 + + # SMP is not supported in this app, so disable smp-over-shell. + SHELL_MGMT: 0 + + MSYS_1_BLOCK_COUNT: 48 + + BLE_MESH: 1 + BLE_MESH_CFG_CLI: 1 + BLE_MESH_DEV_UUID: "((uint8_t[16]){0xdd, 0xdd, 0})" + BLE_MESH_SETTINGS: 0 + CONFIG_NFFS: 0 + + BLE_MESH_ADV_BUF_COUNT: 20 + BLE_MESH_TX_SEG_MAX: 6 diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/README.md b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/README.md new file mode 100644 index 00000000..7bec909e --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/README.md @@ -0,0 +1,101 @@ +#### Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + +##### Overview +******** + +This is a application demonstrating a Bluetooth mesh node in +which Root element has following models + +- Generic OnOff Server +- Generic OnOff Client +- Generic Level Server +- Generic Level Client +- Generic Default Transition Time Server +- Generic Default Transition Time Client +- Generic Power OnOff Server +- Generic Power OnOff Setup Server +- Generic Power OnOff Client +- Light Lightness Server +- Light Lightness Setup Server +- Light Lightness Client +- Light CTL Server +- Light CTL Setup Server +- Light CTL Client +- Vendor Model + +And Secondary element has following models + +- Generic Level Server +- Generic Level Client +- Light CTL Temperature Server + +Prior to provisioning, an unprovisioned beacon is broadcast that contains +a unique UUID. Each button controls the state of its +corresponding LED and does not initiate any mesh activity + +##### Associations of Models with hardware +************************************ +For the nRF52840-PDK board, these are the model associations: + +* LED1 is associated with generic OnOff Server's state which is part of Root element +* LED2 is associated with Vendor Model which is part of Root element +* LED3 is associated with generic Level (ROOT) / Light Lightness Actual value +* LED4 is associated with generic Level (Secondary) / Light CTL Temperature value +* Button1 and Button2 are associated with gen. OnOff Client or Vendor Model which is part of Root element +* Button3 and Button4 are associated with gen. Level Client / Light Lightness Client / Light CTL Client which is part of Root element + +States of Servers are bounded as per Bluetooth SIG Mesh Model Specification v1.0 + +After provisioning, the button clients must +be configured to publish and the LED servers to subscribe. +If a server is provided with a publish address, it will +also publish its relevant status. + +##### Requirements +************ +This sample has been tested on the Nordic nRF52840-PDK board, but would +likely also run on the nrf52_pca10040 board. + + +##### Running +************ + +Provisioning is done using the BlueZ meshctl utility. In this example, we'll use meshctl commands to bind: + +- Button1, Button2, and LED1 to application key 1. It then configures Button1 and Button2 + to publish to group 0xC000 and LED1 to subscribe to that group. +- Button3, Button4, and LED3 to application key 1. It then configures Button3 and Button4 + to publish to group 0xC000 and LED3 to subscribe to that group. + +``` +discover-unprovisioned on +provision +menu config +target 0100 +appkey-add 1 +bind 0 1 1000 +bind 0 1 1001 +bind 0 1 1002 +bind 0 1 1003 +sub-add 0100 c000 1000 +sub-add 0100 c000 1002 +pub-set 0100 c000 1 0 5 1001 +pub-set 0100 c000 1 0 5 1003 +``` + +The meshctl utility maintains a persistent JSON database containing +the mesh configuration. As additional nodes (boards) are provisioned, it +assigns sequential unicast addresses based on the number of elements +supported by the node. This example supports 2 elements per node. + +The meshctl target for configuration must be the root element's unicast +address as it is the only one that has a configuration server model. If +meshctl is gracefully exited, it can be restarted and reconnected to +network 0x0. + +The meshctl utility also supports a onoff model client that can be used to +change the state of any LED that is bound to application key 0x1. +This is done by setting the target to the unicast address of the element +that has that LED's model and issuing the onoff command. +Group addresses are not supported. + diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/pkg.yml b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/pkg.yml new file mode 100644 index 00000000..ee2be6da --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/pkg.yml @@ -0,0 +1,40 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: apps/blemesh_models_example_2 +pkg.type: app +pkg.description: Sample application for BLE Mesh node with on/off, level, light and vendor models on nRF52840pdk +pkg.author: "Michał Narajowski " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/encoding/base64" + - "@apache-mynewt-core/sys/config" + - nimble/controller + - nimble/host + - nimble/host/services/gap + - nimble/host/services/gatt + - nimble/transport/ram + +pkg.lflags: + - -DFLOAT_SUPPORT + - -lm diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/app_gpio.c b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/app_gpio.c new file mode 100644 index 00000000..76e361af --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/app_gpio.c @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "bsp/bsp.h" +#include "console/console.h" +#include "hal/hal_gpio.h" +#include "mesh/mesh.h" + +#include "app_gpio.h" +#include "publisher.h" + +int button_device[] = { + BUTTON_1, + BUTTON_2, + BUTTON_3, + BUTTON_4, +}; + +int led_device[] = { + LED_1, + LED_2, + LED_3, + LED_4, +}; + +static struct os_callout button_work; + +static void button_pressed(struct os_event *ev) +{ + os_callout_reset(&button_work, 0); +} + +static struct os_event button_event; + +static void gpio_irq_handler(void *arg) +{ + button_event.ev_arg = arg; + os_eventq_put(os_eventq_dflt_get(), &button_event); +} + +void app_gpio_init(void) +{ + /* LEDs configiuratin & setting */ + + hal_gpio_init_out(led_device[0], 1); + hal_gpio_init_out(led_device[1], 1); + hal_gpio_init_out(led_device[2], 1); + hal_gpio_init_out(led_device[3], 1); + + /* Buttons configiuratin & setting */ + + os_callout_init(&button_work, os_eventq_dflt_get(), publish, NULL); + + button_event.ev_cb = button_pressed; + + hal_gpio_irq_init(button_device[0], gpio_irq_handler, NULL, + HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP); + hal_gpio_irq_enable(button_device[0]); + + hal_gpio_irq_init(button_device[1], gpio_irq_handler, NULL, + HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP); + hal_gpio_irq_enable(button_device[1]); + + hal_gpio_irq_init(button_device[2], gpio_irq_handler, NULL, + HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP); + hal_gpio_irq_enable(button_device[2]); + + hal_gpio_irq_init(button_device[3], gpio_irq_handler, NULL, + HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP); + hal_gpio_irq_enable(button_device[3]); +} + diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/app_gpio.h b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/app_gpio.h new file mode 100644 index 00000000..6eabc1ce --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/app_gpio.h @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _APP_GPIO_H +#define _APP_GPIO_H + +/* GPIO */ +extern int button_device[]; +extern int led_device[]; + +void app_gpio_init(void); + +#endif diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/ble_mesh.c b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/ble_mesh.c new file mode 100644 index 00000000..86d4c515 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/ble_mesh.c @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "console/console.h" + +#include "common.h" +#include "ble_mesh.h" +#include "device_composition.h" + +#define OOB_AUTH_ENABLE 1 + +#ifdef OOB_AUTH_ENABLE + +static int output_number(bt_mesh_output_action_t action, u32_t number) +{ + printk("OOB Number: %lu\n", number); + return 0; +} + +static int output_string(const char *str) +{ + printk("OOB String: %s\n", str); + return 0; +} + +#endif + +static void prov_complete(u16_t net_idx, u16_t addr) +{ + printk("Local node provisioned, primary address 0x%04x\n", addr); +} + +static void prov_reset(void) +{ + bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT); +} + +static u8_t dev_uuid[16] = MYNEWT_VAL(BLE_MESH_DEV_UUID); + +static const struct bt_mesh_prov prov = { + .uuid = dev_uuid, + +#ifdef OOB_AUTH_ENABLE + + .output_size = 6, + .output_actions = BT_MESH_DISPLAY_NUMBER | BT_MESH_DISPLAY_STRING, + .output_number = output_number, + .output_string = output_string, + +#endif + + .complete = prov_complete, + .reset = prov_reset, +}; + +void blemesh_on_reset(int reason) +{ + BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason); +} + +void blemesh_on_sync(void) +{ + int err; + ble_addr_t addr; + + console_printf("Bluetooth initialized\n"); + + /* Use NRPA */ + err = ble_hs_id_gen_rnd(1, &addr); + assert(err == 0); + err = ble_hs_id_set_rnd(addr.val); + assert(err == 0); + + err = bt_mesh_init(addr.type, &prov, &comp); + if (err) { + console_printf("Initializing mesh failed (err %d)\n", err); + return; + } + + if (IS_ENABLED(CONFIG_SETTINGS)) { + settings_load(); + } + + if (bt_mesh_is_provisioned()) { + console_printf("Mesh network restored from flash\n"); + } + + bt_mesh_prov_enable(BT_MESH_PROV_GATT | BT_MESH_PROV_ADV); + + console_printf("Mesh initialized\n"); + + bt_initialized(); +} diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/ble_mesh.h b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/ble_mesh.h new file mode 100644 index 00000000..c02af243 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/ble_mesh.h @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _BLE_MESH_H +#define _BLE_MESH_H + +#include "mesh/mesh.h" +#include "mesh/glue.h" + +/* Model Operation Codes */ +#define BT_MESH_MODEL_OP_GEN_ONOFF_GET BT_MESH_MODEL_OP_2(0x82, 0x01) +#define BT_MESH_MODEL_OP_GEN_ONOFF_SET BT_MESH_MODEL_OP_2(0x82, 0x02) +#define BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x03) +#define BT_MESH_MODEL_OP_GEN_ONOFF_STATUS BT_MESH_MODEL_OP_2(0x82, 0x04) + +#define BT_MESH_MODEL_OP_GEN_LEVEL_GET BT_MESH_MODEL_OP_2(0x82, 0x05) +#define BT_MESH_MODEL_OP_GEN_LEVEL_SET BT_MESH_MODEL_OP_2(0x82, 0x06) +#define BT_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x07) +#define BT_MESH_MODEL_OP_GEN_LEVEL_STATUS BT_MESH_MODEL_OP_2(0x82, 0x08) +#define BT_MESH_MODEL_OP_GEN_DELTA_SET BT_MESH_MODEL_OP_2(0x82, 0x09) +#define BT_MESH_MODEL_OP_GEN_DELTA_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x0A) +#define BT_MESH_MODEL_OP_GEN_MOVE_SET BT_MESH_MODEL_OP_2(0x82, 0x0B) +#define BT_MESH_MODEL_OP_GEN_MOVE_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x0C) + +#define BT_MESH_MODEL_GEN_DEF_TRANS_TIME_STATUS BT_MESH_MODEL_OP_2(0x82, 0x10) + +#define BT_MESH_MODEL_GEN_ONPOWERUP_STATUS BT_MESH_MODEL_OP_2(0x82, 0x12) + +#define BT_MESH_MODEL_LIGHT_LIGHTNESS_STATUS BT_MESH_MODEL_OP_2(0x82, 0x4E) +#define BT_MESH_MODEL_LIGHT_LIGHTNESS_LINEAR_STATUS \ + BT_MESH_MODEL_OP_2(0x82, 0x52) +#define BT_MESH_MODEL_LIGHT_LIGHTNESS_LAST_STATUS \ + BT_MESH_MODEL_OP_2(0x82, 0x54) +#define BT_MESH_MODEL_LIGHT_LIGHTNESS_DEFAULT_STATUS \ + BT_MESH_MODEL_OP_2(0x82, 0x56) +#define BT_MESH_MODEL_LIGHT_LIGHTNESS_RANGE_STATUS \ + BT_MESH_MODEL_OP_2(0x82, 0x58) + +#define BT_MESH_MODEL_LIGHT_CTL_STATUS BT_MESH_MODEL_OP_2(0x82, 0x60) +#define BT_MESH_MODEL_LIGHT_CTL_TEMP_RANGE_STATUS \ + BT_MESH_MODEL_OP_2(0x82, 0x63) +#define BT_MESH_MODEL_LIGHT_CTL_TEMP_STATUS BT_MESH_MODEL_OP_2(0x82, 0x66) +#define BT_MESH_MODEL_LIGHT_CTL_DEFAULT_STATUS BT_MESH_MODEL_OP_2(0x82, 0x68) + +void blemesh_on_reset(int reason); +void blemesh_on_sync(void); +void init_pub(void); + +#endif + diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/common.h b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/common.h new file mode 100644 index 00000000..57cd401a --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/common.h @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _COMMON_H +#define _COMMON_H + +void update_light_state(void); +void bt_initialized(void); + +#endif diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/device_composition.c b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/device_composition.c new file mode 100644 index 00000000..b638b861 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/device_composition.c @@ -0,0 +1,2779 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "console/console.h" +#include "hal/hal_gpio.h" +#include "mesh/mesh.h" + +#include "app_gpio.h" +#include "storage.h" + +#include "ble_mesh.h" +#include "device_composition.h" +#include "state_binding.h" +#include "transition.h" + +static struct bt_mesh_cfg_srv cfg_srv = { + .relay = BT_MESH_RELAY_ENABLED, + .beacon = BT_MESH_BEACON_ENABLED, + +#if defined(CONFIG_BT_MESH_FRIEND) + .frnd = BT_MESH_FRIEND_ENABLED, +#else + .frnd = BT_MESH_FRIEND_NOT_SUPPORTED, +#endif + +#if defined(CONFIG_BT_MESH_GATT_PROXY) + .gatt_proxy = BT_MESH_GATT_PROXY_ENABLED, +#else + .gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED, +#endif + + .default_ttl = 7, + + /* 2 transmissions with 20ms interval */ + .net_transmit = BT_MESH_TRANSMIT(2, 20), + + /* 3 transmissions with 20ms interval */ + .relay_retransmit = BT_MESH_TRANSMIT(3, 20), +}; + +static struct bt_mesh_health_srv health_srv = { +}; + +static struct bt_mesh_model_pub health_pub; + +static struct bt_mesh_model_pub gen_onoff_srv_pub_root; +static struct bt_mesh_model_pub gen_onoff_cli_pub_root; +static struct bt_mesh_model_pub gen_level_srv_pub_root; +static struct bt_mesh_model_pub gen_level_cli_pub_root; +static struct bt_mesh_model_pub gen_def_trans_time_srv_pub; +static struct bt_mesh_model_pub gen_def_trans_time_cli_pub; +static struct bt_mesh_model_pub gen_power_onoff_srv_pub; +static struct bt_mesh_model_pub gen_power_onoff_cli_pub; +static struct bt_mesh_model_pub light_lightness_srv_pub; +static struct bt_mesh_model_pub light_lightness_cli_pub; +static struct bt_mesh_model_pub light_ctl_srv_pub; +static struct bt_mesh_model_pub light_ctl_cli_pub; +static struct bt_mesh_model_pub vnd_pub; +static struct bt_mesh_model_pub gen_level_srv_pub_s0; +static struct bt_mesh_model_pub gen_level_cli_pub_s0; + + +static struct os_mbuf *bt_mesh_pub_msg_health_pub; +static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_srv_pub_root; +static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_cli_pub_root; +static struct os_mbuf *bt_mesh_pub_msg_gen_level_srv_pub_root; +static struct os_mbuf *bt_mesh_pub_msg_gen_level_cli_pub_root; +static struct os_mbuf *bt_mesh_pub_msg_gen_def_trans_time_srv_pub; +static struct os_mbuf *bt_mesh_pub_msg_gen_def_trans_time_cli_pub; +static struct os_mbuf *bt_mesh_pub_msg_gen_power_onoff_srv_pub; +static struct os_mbuf *bt_mesh_pub_msg_gen_power_onoff_cli_pub; +static struct os_mbuf *bt_mesh_pub_msg_light_lightness_srv_pub; +static struct os_mbuf *bt_mesh_pub_msg_light_lightness_cli_pub; +static struct os_mbuf *bt_mesh_pub_msg_light_ctl_srv_pub; +static struct os_mbuf *bt_mesh_pub_msg_light_ctl_cli_pub; +static struct os_mbuf *bt_mesh_pub_msg_vnd_pub; +static struct os_mbuf *bt_mesh_pub_msg_gen_level_srv_pub_s0; +static struct os_mbuf *bt_mesh_pub_msg_gen_level_cli_pub_s0; + + +void init_pub(void) +{ + bt_mesh_pub_msg_health_pub = NET_BUF_SIMPLE(1 + 3 + 0); + bt_mesh_pub_msg_gen_onoff_srv_pub_root = NET_BUF_SIMPLE(2 + 3); + bt_mesh_pub_msg_gen_onoff_cli_pub_root = NET_BUF_SIMPLE(2 + 4); + bt_mesh_pub_msg_gen_level_srv_pub_root = NET_BUF_SIMPLE(2 + 5); + bt_mesh_pub_msg_gen_level_cli_pub_root = NET_BUF_SIMPLE(2 + 7); + bt_mesh_pub_msg_gen_power_onoff_srv_pub = NET_BUF_SIMPLE(2 + 1); + bt_mesh_pub_msg_gen_power_onoff_cli_pub = NET_BUF_SIMPLE(2 + 1); + bt_mesh_pub_msg_gen_def_trans_time_srv_pub = NET_BUF_SIMPLE(2 + 1); + bt_mesh_pub_msg_gen_def_trans_time_cli_pub = NET_BUF_SIMPLE(2 + 1); + bt_mesh_pub_msg_light_lightness_srv_pub = NET_BUF_SIMPLE(2 + 5); + bt_mesh_pub_msg_light_lightness_cli_pub = NET_BUF_SIMPLE(2 + 5); + bt_mesh_pub_msg_light_ctl_srv_pub = NET_BUF_SIMPLE(2 + 9); + bt_mesh_pub_msg_light_ctl_cli_pub = NET_BUF_SIMPLE(2 + 9); + bt_mesh_pub_msg_vnd_pub = NET_BUF_SIMPLE(3 + 6); + bt_mesh_pub_msg_gen_level_srv_pub_s0 = NET_BUF_SIMPLE(2 + 5); + bt_mesh_pub_msg_gen_level_cli_pub_s0 = NET_BUF_SIMPLE(2 + 7); + + + health_pub.msg = bt_mesh_pub_msg_health_pub; + gen_onoff_srv_pub_root.msg = bt_mesh_pub_msg_gen_onoff_srv_pub_root; + gen_onoff_cli_pub_root.msg = bt_mesh_pub_msg_gen_onoff_cli_pub_root; + gen_level_srv_pub_root.msg = bt_mesh_pub_msg_gen_level_srv_pub_root; + gen_level_cli_pub_root.msg = bt_mesh_pub_msg_gen_level_cli_pub_root; + gen_power_onoff_srv_pub.msg = bt_mesh_pub_msg_gen_power_onoff_srv_pub; + gen_power_onoff_cli_pub.msg = bt_mesh_pub_msg_gen_power_onoff_cli_pub; + gen_def_trans_time_srv_pub.msg = bt_mesh_pub_msg_gen_def_trans_time_srv_pub; + gen_def_trans_time_cli_pub.msg = bt_mesh_pub_msg_gen_def_trans_time_cli_pub; + light_lightness_srv_pub.msg = bt_mesh_pub_msg_light_lightness_srv_pub; + light_lightness_cli_pub.msg = bt_mesh_pub_msg_light_lightness_cli_pub; + light_ctl_srv_pub.msg = bt_mesh_pub_msg_light_ctl_srv_pub; + light_ctl_cli_pub.msg = bt_mesh_pub_msg_light_ctl_cli_pub; + vnd_pub.msg = bt_mesh_pub_msg_vnd_pub; + gen_level_srv_pub_s0.msg = bt_mesh_pub_msg_gen_level_srv_pub_s0; + gen_level_cli_pub_s0.msg = bt_mesh_pub_msg_gen_level_cli_pub_s0; +} + +/* Definitions of models user data (Start) */ +struct generic_onoff_state gen_onoff_srv_root_user_data = { + .transition = &lightness_transition, +}; + +struct generic_level_state gen_level_srv_root_user_data = { + .transition = &lightness_transition, +}; + +struct gen_def_trans_time_state gen_def_trans_time_srv_user_data; + +struct generic_onpowerup_state gen_power_onoff_srv_user_data; + +struct light_lightness_state light_lightness_srv_user_data = { + .transition = &lightness_transition, +}; + +struct light_ctl_state light_ctl_srv_user_data = { + .transition = &lightness_transition, +}; + +struct vendor_state vnd_user_data; + +struct generic_level_state gen_level_srv_s0_user_data = { + .transition = &temp_transition, +}; +/* Definitions of models user data (End) */ + +static struct bt_mesh_elem elements[]; + +/* message handlers (Start) */ + +/* Generic OnOff Server message handlers */ +static void gen_onoff_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 3 + 4); + struct generic_onoff_state *state = model->user_data; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_ONOFF_STATUS); + net_buf_simple_add_u8(msg, state->onoff); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_u8(msg, state->target_onoff); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send GEN_ONOFF_SRV Status response\n"); + } + + os_mbuf_free_chain(msg); +} + +void gen_onoff_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct generic_onoff_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_ONOFF_STATUS); + net_buf_simple_add_u8(msg, state->onoff); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_u8(msg, state->target_onoff); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } +} + +static void gen_onoff_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t tid, onoff, tt, delay; + s64_t now; + struct generic_onoff_state *state = model->user_data; + + onoff = net_buf_simple_pull_u8(buf); + tid = net_buf_simple_pull_u8(buf); + + if (onoff > STATE_ON) { + return; + } + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + return; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->target_onoff = onoff; + + if (state->target_onoff != state->onoff) { + onoff_tt_values(state, tt, delay); + } else { + gen_onoff_publish(model); + return; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->onoff = state->target_onoff; + } + + state->transition->just_started = true; + gen_onoff_publish(model); + onoff_handler(state); +} + +static void gen_onoff_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t tid, onoff, tt, delay; + s64_t now; + struct generic_onoff_state *state = model->user_data; + + onoff = net_buf_simple_pull_u8(buf); + tid = net_buf_simple_pull_u8(buf); + + if (onoff > STATE_ON) { + return; + } + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + gen_onoff_get(model, ctx, buf); + return; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->target_onoff = onoff; + + if (state->target_onoff != state->onoff) { + onoff_tt_values(state, tt, delay); + } else { + gen_onoff_get(model, ctx, buf); + gen_onoff_publish(model); + return; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->onoff = state->target_onoff; + } + + state->transition->just_started = true; + gen_onoff_get(model, ctx, buf); + gen_onoff_publish(model); + onoff_handler(state); +} + +/* Generic OnOff Client message handlers */ +static void gen_onoff_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from GEN_ONOFF_SRV\n"); + printk("Present OnOff = %02x\n", net_buf_simple_pull_u8(buf)); + + if (buf->om_len == 2) { + printk("Target OnOff = %02x\n", net_buf_simple_pull_u8(buf)); + printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf)); + } +} + +/* Generic Level Server message handlers */ +static void gen_level_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4); + struct generic_level_state *state = model->user_data; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS); + net_buf_simple_add_le16(msg, state->level); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_le16(msg, state->target_level); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send GEN_LEVEL_SRV Status response\n"); + } + + os_mbuf_free_chain(msg); +} + +void gen_level_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct generic_level_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS); + net_buf_simple_add_le16(msg, state->level); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_le16(msg, state->target_level); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } +} + +static void gen_level_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t tid, tt, delay; + s16_t level; + s64_t now; + struct generic_level_state *state = model->user_data; + + level = (s16_t) net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + return; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->target_level = level; + + if (state->target_level != state->level) { + level_tt_values(state, tt, delay); + } else { + gen_level_publish(model); + return; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->level = state->target_level; + } + + state->transition->just_started = true; + gen_level_publish(model); + + if (bt_mesh_model_elem(model)->addr == elements[0].addr) { + /* Root element */ + transition_type = LEVEL_TT; + level_lightness_handler(state); + } else if (bt_mesh_model_elem(model)->addr == elements[1].addr) { + /* Secondary element */ + transition_type = LEVEL_TEMP_TT; + level_temp_handler(state); + } +} + +static void gen_level_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t tid, tt, delay; + s16_t level; + s64_t now; + struct generic_level_state *state = model->user_data; + + level = (s16_t) net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + gen_level_get(model, ctx, buf); + return; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->target_level = level; + + if (state->target_level != state->level) { + level_tt_values(state, tt, delay); + } else { + gen_level_get(model, ctx, buf); + gen_level_publish(model); + return; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->level = state->target_level; + } + + state->transition->just_started = true; + gen_level_get(model, ctx, buf); + gen_level_publish(model); + + if (bt_mesh_model_elem(model)->addr == elements[0].addr) { + /* Root element */ + transition_type = LEVEL_TT; + level_lightness_handler(state); + } else if (bt_mesh_model_elem(model)->addr == elements[1].addr) { + /* Secondary element */ + transition_type = LEVEL_TEMP_TT; + level_temp_handler(state); + } +} + +static void gen_delta_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t tid, tt, delay; + s32_t tmp32, delta; + s64_t now; + struct generic_level_state *state = model->user_data; + + delta = (s32_t) net_buf_simple_pull_le32(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + + if (state->last_delta == delta) { + return; + } + tmp32 = state->last_level + delta; + + } else { + state->last_level = state->level; + tmp32 = state->level + delta; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->last_delta = delta; + + if (tmp32 < INT16_MIN) { + tmp32 = INT16_MIN; + } else if (tmp32 > INT16_MAX) { + tmp32 = INT16_MAX; + } + + state->target_level = tmp32; + + if (state->target_level != state->level) { + level_tt_values(state, tt, delay); + } else { + gen_level_publish(model); + return; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->level = state->target_level; + } + + state->transition->just_started = true; + gen_level_publish(model); + + if (bt_mesh_model_elem(model)->addr == elements[0].addr) { + /* Root element */ + transition_type = LEVEL_TT_DELTA; + level_lightness_handler(state); + } else if (bt_mesh_model_elem(model)->addr == + elements[1].addr) { + /* Secondary element */ + transition_type = LEVEL_TEMP_TT_DELTA; + level_temp_handler(state); + } +} + +static void gen_delta_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t tid, tt, delay; + s32_t tmp32, delta; + s64_t now; + struct generic_level_state *state = model->user_data; + + delta = (s32_t) net_buf_simple_pull_le32(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + + if (state->last_delta == delta) { + gen_level_get(model, ctx, buf); + return; + } + tmp32 = state->last_level + delta; + + } else { + state->last_level = state->level; + tmp32 = state->level + delta; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->last_delta = delta; + + if (tmp32 < INT16_MIN) { + tmp32 = INT16_MIN; + } else if (tmp32 > INT16_MAX) { + tmp32 = INT16_MAX; + } + + state->target_level = tmp32; + + if (state->target_level != state->level) { + level_tt_values(state, tt, delay); + } else { + gen_level_get(model, ctx, buf); + gen_level_publish(model); + return; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->level = state->target_level; + } + + state->transition->just_started = true; + gen_level_get(model, ctx, buf); + gen_level_publish(model); + + if (bt_mesh_model_elem(model)->addr == elements[0].addr) { + /* Root element */ + transition_type = LEVEL_TT_DELTA; + level_lightness_handler(state); + } else if (bt_mesh_model_elem(model)->addr == elements[1].addr) { + /* Secondary element */ + transition_type = LEVEL_TEMP_TT_DELTA; + level_temp_handler(state); + } +} + +static void gen_level_move_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4); + struct generic_level_state *state = model->user_data; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS); + net_buf_simple_add_le16(msg, state->level); + + if (state->transition->counter) { + if (state->last_delta < 0) { + net_buf_simple_add_le16(msg, INT16_MIN); + } else { /* 0 should not be possible */ + net_buf_simple_add_le16(msg, INT16_MAX); + } + + net_buf_simple_add_u8(msg, UNKNOWN_VALUE); + } + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send GEN_LEVEL_SRV Status response\n"); + } + + os_mbuf_free_chain(msg); +} + +static void gen_level_move_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct generic_level_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS); + net_buf_simple_add_le16(msg, state->level); + + if (state->transition->counter) { + if (state->last_delta < 0) { + net_buf_simple_add_le16(msg, INT16_MIN); + } else { /* 0 should not be possible */ + net_buf_simple_add_le16(msg, INT16_MAX); + } + + net_buf_simple_add_u8(msg, UNKNOWN_VALUE); + } + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } +} + +static void gen_move_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t tid, tt, delay; + s16_t delta; + s32_t tmp32; + s64_t now; + struct generic_level_state *state = model->user_data; + + delta = (s16_t) net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + return; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->last_delta = delta; + + tmp32 = state->level + delta; + if (tmp32 < INT16_MIN) { + tmp32 = INT16_MIN; + } else if (tmp32 > INT16_MAX) { + tmp32 = INT16_MAX; + } + + state->target_level = tmp32; + + if (state->target_level != state->level) { + level_tt_values(state, tt, delay); + } else { + gen_level_move_publish(model); + return; + } + + if (state->transition->counter == 0) { + return; + } + + state->transition->just_started = true; + gen_level_move_publish(model); + + if (bt_mesh_model_elem(model)->addr == elements[0].addr) { + /* Root element */ + transition_type = LEVEL_TT_MOVE; + level_lightness_handler(state); + } else if (bt_mesh_model_elem(model)->addr == elements[1].addr) { + /* Secondary element */ + transition_type = LEVEL_TEMP_TT_MOVE; + level_temp_handler(state); + } +} + +static void gen_move_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t tid, tt, delay; + s16_t delta; + s32_t tmp32; + s64_t now; + struct generic_level_state *state = model->user_data; + + delta = (s16_t) net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + gen_level_move_get(model, ctx, buf); + return; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->last_delta = delta; + + tmp32 = state->level + delta; + if (tmp32 < INT16_MIN) { + tmp32 = INT16_MIN; + } else if (tmp32 > INT16_MAX) { + tmp32 = INT16_MAX; + } + + state->target_level = tmp32; + + if (state->target_level != state->level) { + level_tt_values(state, tt, delay); + } else { + gen_level_move_get(model, ctx, buf); + gen_level_move_publish(model); + return; + } + + if (state->transition->counter == 0) { + return; + } + + state->transition->just_started = true; + gen_level_move_get(model, ctx, buf); + gen_level_move_publish(model); + + if (bt_mesh_model_elem(model)->addr == elements[0].addr) { + /* Root element */ + transition_type = LEVEL_TT_MOVE; + level_lightness_handler(state); + } else if (bt_mesh_model_elem(model)->addr == elements[1].addr) { + /* Secondary element */ + transition_type = LEVEL_TEMP_TT_MOVE; + level_temp_handler(state); + } +} + +/* Generic Level Client message handlers */ +static void gen_level_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from GEN_LEVEL_SRV\n"); + printk("Present Level = %04x\n", net_buf_simple_pull_le16(buf)); + + if (buf->om_len == 3) { + printk("Target Level = %04x\n", net_buf_simple_pull_le16(buf)); + printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf)); + } +} + +/* Generic Default Transition Time Server message handlers */ +static void gen_def_trans_time_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); + struct gen_def_trans_time_state *state = model->user_data; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_GEN_DEF_TRANS_TIME_STATUS); + net_buf_simple_add_u8(msg, state->tt); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send GEN_DEF_TT_SRV Status response\n"); + } + + os_mbuf_free_chain(msg); +} + +static void gen_def_trans_time_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct gen_def_trans_time_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_GEN_DEF_TRANS_TIME_STATUS); + net_buf_simple_add_u8(msg, state->tt); + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } +} + +static bool gen_def_trans_time_setunack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t tt; + struct gen_def_trans_time_state *state = model->user_data; + + tt = net_buf_simple_pull_u8(buf); + + /* Here, Model specification is silent about tid implementation */ + + if ((tt & 0x3F) == 0x3F) { + return false; + } + + if (state->tt != tt) { + state->tt = tt; + default_tt = tt; + + save_on_flash(GEN_DEF_TRANS_TIME_STATE); + } + + return true; +} + +static void gen_def_trans_time_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + if (gen_def_trans_time_setunack(model, ctx, buf) == true) { + gen_def_trans_time_publish(model); + } +} + +static void gen_def_trans_time_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + if (gen_def_trans_time_setunack(model, ctx, buf) == true) { + gen_def_trans_time_get(model, ctx, buf); + gen_def_trans_time_publish(model); + } +} + +/* Generic Default Transition Time Client message handlers */ +static void gen_def_trans_time_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from GEN_DEF_TT_SRV\n"); + printk("Transition Time = %02x\n", net_buf_simple_pull_u8(buf)); +} + +/* Generic Power OnOff Server message handlers */ +static void gen_onpowerup_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4); + struct generic_onpowerup_state *state = model->user_data; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_GEN_ONPOWERUP_STATUS); + net_buf_simple_add_u8(msg, state->onpowerup); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send GEN_POWER_ONOFF_SRV Status response\n"); + } + + os_mbuf_free_chain(msg); +} + +/* Generic Power OnOff Client message handlers */ +static void gen_onpowerup_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from GEN_POWER_ONOFF_SRV\n"); + printk("OnPowerUp = %02x\n", net_buf_simple_pull_u8(buf)); +} + +/* Generic Power OnOff Setup Server message handlers */ + +static void gen_onpowerup_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct generic_onpowerup_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_GEN_ONPOWERUP_STATUS); + net_buf_simple_add_u8(msg, state->onpowerup); + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } +} + +static bool gen_onpowerup_setunack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t onpowerup; + struct generic_onpowerup_state *state = model->user_data; + + onpowerup = net_buf_simple_pull_u8(buf); + + /* Here, Model specification is silent about tid implementation */ + + if (onpowerup > STATE_RESTORE) { + return false; + } + + if (state->onpowerup != onpowerup) { + state->onpowerup = onpowerup; + + save_on_flash(GEN_ONPOWERUP_STATE); + } + + return true; +} + +static void gen_onpowerup_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + if (gen_onpowerup_setunack(model, ctx, buf) == true) { + gen_onpowerup_publish(model); + } +} + +static void gen_onpowerup_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + if (gen_onpowerup_setunack(model, ctx, buf) == true) { + gen_onpowerup_get(model, ctx, buf); + gen_onpowerup_publish(model); + } +} + +/* Vendor Model message handlers*/ +static void vnd_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(3 + 6 + 4); + struct vendor_state *state = model->user_data; + + /* This is dummy response for demo purpose */ + state->response = 0xA578FEB3; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_3(0x04, CID_RUNTIME)); + net_buf_simple_add_le16(msg, state->current); + net_buf_simple_add_le32(msg, state->response); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send VENDOR Status response\n"); + } + + os_mbuf_free_chain(msg); +} + +static void vnd_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t tid; + int current; + s64_t now; + struct vendor_state *state = model->user_data; + + current = net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + return; + } + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->current = current; + + printk("Vendor model message = %04x\n", state->current); + + if (state->current == STATE_ON) { + /* LED2 On */ + hal_gpio_write(led_device[1], 0); + } else { + /* LED2 Off */ + hal_gpio_write(led_device[1], 1); + } +} + +static void vnd_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + vnd_set_unack(model, ctx, buf); + vnd_get(model, ctx, buf); +} + +static void vnd_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from Vendor\n"); + printk("cmd = %04x\n", net_buf_simple_pull_le16(buf)); + printk("response = %08lx\n", net_buf_simple_pull_le32(buf)); +} + +/* Light Lightness Server message handlers */ +static void light_lightness_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4); + struct light_lightness_state *state = model->user_data; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_STATUS); + net_buf_simple_add_le16(msg, state->actual); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_le16(msg, state->target_actual); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send LightLightnessAct Status response\n"); + } + + os_mbuf_free_chain(msg); +} + +void light_lightness_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct light_lightness_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_STATUS); + net_buf_simple_add_le16(msg, state->actual); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_le16(msg, state->target_actual); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } +} + +static void light_lightness_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t tid, tt, delay; + u16_t actual; + s64_t now; + struct light_lightness_state *state = model->user_data; + + actual = net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + return; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + + if (actual > 0 && actual < state->light_range_min) { + actual = state->light_range_min; + } else if (actual > state->light_range_max) { + actual = state->light_range_max; + } + + state->target_actual = actual; + + if (state->target_actual != state->actual) { + light_lightness_actual_tt_values(state, tt, delay); + } else { + light_lightness_publish(model); + return; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->actual = state->target_actual; + } + + state->transition->just_started = true; + light_lightness_publish(model); + light_lightness_actual_handler(state); +} + +static void light_lightness_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t tid, tt, delay; + u16_t actual; + s64_t now; + struct light_lightness_state *state = model->user_data; + + actual = net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + light_lightness_get(model, ctx, buf); + return; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + + if (actual > 0 && actual < state->light_range_min) { + actual = state->light_range_min; + } else if (actual > state->light_range_max) { + actual = state->light_range_max; + } + + state->target_actual = actual; + + if (state->target_actual != state->actual) { + light_lightness_actual_tt_values(state, tt, delay); + } else { + light_lightness_get(model, ctx, buf); + light_lightness_publish(model); + return; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->actual = state->target_actual; + } + + state->transition->just_started = true; + light_lightness_get(model, ctx, buf); + light_lightness_publish(model); + light_lightness_actual_handler(state); +} + +static void light_lightness_linear_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4); + struct light_lightness_state *state = model->user_data; + + bt_mesh_model_msg_init(msg, + BT_MESH_MODEL_LIGHT_LIGHTNESS_LINEAR_STATUS); + net_buf_simple_add_le16(msg, state->linear); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_le16(msg, state->target_linear); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send LightLightnessLin Status response\n"); + } + + os_mbuf_free_chain(msg); +} + +void light_lightness_linear_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct light_lightness_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return; + } + + bt_mesh_model_msg_init(msg, + BT_MESH_MODEL_LIGHT_LIGHTNESS_LINEAR_STATUS); + net_buf_simple_add_le16(msg, state->linear); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_le16(msg, state->target_linear); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } +} + +static void light_lightness_linear_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t tid, tt, delay; + u16_t linear; + s64_t now; + struct light_lightness_state *state = model->user_data; + + linear = net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + return; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->target_linear = linear; + + if (state->target_linear != state->linear) { + light_lightness_linear_tt_values(state, tt, delay); + } else { + light_lightness_linear_publish(model); + return; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->linear = state->target_linear; + } + + state->transition->just_started = true; + light_lightness_linear_publish(model); + light_lightness_linear_handler(state); +} + +static void light_lightness_linear_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t tid, tt, delay; + u16_t linear; + s64_t now; + struct light_lightness_state *state = model->user_data; + + linear = net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + light_lightness_linear_get(model, ctx, buf); + return; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->target_linear = linear; + + if (state->target_linear != state->linear) { + light_lightness_linear_tt_values(state, tt, delay); + } else { + light_lightness_linear_get(model, ctx, buf); + light_lightness_linear_publish(model); + return; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->linear = state->target_linear; + } + + state->transition->just_started = true; + light_lightness_linear_get(model, ctx, buf); + light_lightness_linear_publish(model); + light_lightness_linear_handler(state); +} + +static void light_lightness_last_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 2 + 4); + struct light_lightness_state *state = model->user_data; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_LAST_STATUS); + net_buf_simple_add_le16(msg, state->last); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send LightLightnessLast Status response\n"); + } + + os_mbuf_free_chain(msg); +} + +static void light_lightness_default_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 2 + 4); + struct light_lightness_state *state = model->user_data; + + bt_mesh_model_msg_init(msg, + BT_MESH_MODEL_LIGHT_LIGHTNESS_DEFAULT_STATUS); + net_buf_simple_add_le16(msg, state->def); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send LightLightnessDef Status response\n"); + } + + os_mbuf_free_chain(msg); +} + +static void light_lightness_range_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4); + struct light_lightness_state *state = model->user_data; + + state->status_code = RANGE_SUCCESSFULLY_UPDATED; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_RANGE_STATUS); + net_buf_simple_add_u8(msg, state->status_code); + net_buf_simple_add_le16(msg, state->light_range_min); + net_buf_simple_add_le16(msg, state->light_range_max); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send LightLightnessRange Status response\n"); + } + + os_mbuf_free_chain(msg); +} + +/* Light Lightness Setup Server message handlers */ + +static void light_lightness_default_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct light_lightness_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return; + } + + bt_mesh_model_msg_init(msg, + BT_MESH_MODEL_LIGHT_LIGHTNESS_DEFAULT_STATUS); + net_buf_simple_add_le16(msg, state->def); + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } +} + +static void light_lightness_default_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u16_t lightness; + struct light_lightness_state *state = model->user_data; + + lightness = net_buf_simple_pull_le16(buf); + + /* Here, Model specification is silent about tid implementation */ + + if (state->def != lightness) { + state->def = lightness; + light_ctl_srv_user_data.lightness_def = state->def; + + save_on_flash(LIGHTNESS_TEMP_DEF_STATE); + } + + light_lightness_default_publish(model); +} + +static void light_lightness_default_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + light_lightness_default_set_unack(model, ctx, buf); + light_lightness_default_get(model, ctx, buf); + light_lightness_default_publish(model); +} + +static void light_lightness_range_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct light_lightness_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_LIGHTNESS_RANGE_STATUS); + net_buf_simple_add_u8(msg, state->status_code); + net_buf_simple_add_le16(msg, state->light_range_min); + net_buf_simple_add_le16(msg, state->light_range_max); + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } +} + +static bool light_lightness_range_setunack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u16_t min, max; + struct light_lightness_state *state = model->user_data; + + min = net_buf_simple_pull_le16(buf); + max = net_buf_simple_pull_le16(buf); + + /* Here, Model specification is silent about tid implementation */ + + if (min == 0 || max == 0) { + return false; + } else { + if (min <= max) { + state->status_code = RANGE_SUCCESSFULLY_UPDATED; + + if (state->light_range_min != min || + state->light_range_max != max) { + + state->light_range_min = min; + state->light_range_max = max; + + save_on_flash(LIGHTNESS_RANGE); + } + } else { + /* The provided value for Range Max cannot be set */ + state->status_code = CANNOT_SET_RANGE_MAX; + return false; + } + } + + return true; +} + +static void light_lightness_range_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + if (light_lightness_range_setunack(model, ctx, buf) == true) { + light_lightness_range_publish(model); + } +} + +static void light_lightness_range_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + if (light_lightness_range_setunack(model, ctx, buf) == true) { + light_lightness_range_get(model, ctx, buf); + light_lightness_range_publish(model); + } +} + +/* Light Lightness Client message handlers */ +static void light_lightness_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Actual)\n"); + printk("Present Lightness = %04x\n", net_buf_simple_pull_le16(buf)); + + if (buf->om_len == 3) { + printk("Target Lightness = %04x\n", + net_buf_simple_pull_le16(buf)); + printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf)); + } +} + +static void light_lightness_linear_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Linear)\n"); + printk("Present Lightness = %04x\n", net_buf_simple_pull_le16(buf)); + + if (buf->om_len == 3) { + printk("Target Lightness = %04x\n", + net_buf_simple_pull_le16(buf)); + printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf)); + } +} + +static void light_lightness_last_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Last)\n"); + printk("Lightness = %04x\n", net_buf_simple_pull_le16(buf)); +} + +static void light_lightness_default_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Default)\n"); + printk("Lightness = %04x\n", net_buf_simple_pull_le16(buf)); +} + +static void light_lightness_range_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from LIGHT_LIGHTNESS_SRV (Lightness Range)\n"); + printk("Status Code = %02x\n", net_buf_simple_pull_u8(buf)); + printk("Range Min = %04x\n", net_buf_simple_pull_le16(buf)); + printk("Range Max = %04x\n", net_buf_simple_pull_le16(buf)); +} + +/* Light CTL Server message handlers */ +static void light_ctl_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 9 + 4); + struct light_ctl_state *state = model->user_data; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_STATUS); + net_buf_simple_add_le16(msg, state->lightness); + net_buf_simple_add_le16(msg, state->temp); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_le16(msg, state->target_lightness); + net_buf_simple_add_le16(msg, state->target_temp); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send LightCTL Status response\n"); + } + + os_mbuf_free_chain(msg); +} + +void light_ctl_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct light_ctl_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_STATUS); + + /* Here, as per Model specification, status should be + * made up of lightness & temperature values only + */ + net_buf_simple_add_le16(msg, state->lightness); + net_buf_simple_add_le16(msg, state->temp); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_le16(msg, state->target_lightness); + net_buf_simple_add_le16(msg, state->target_temp); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } +} + +static void light_ctl_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t tid, tt, delay; + s16_t delta_uv; + u16_t lightness, temp; + s64_t now; + struct light_ctl_state *state = model->user_data; + + lightness = net_buf_simple_pull_le16(buf); + temp = net_buf_simple_pull_le16(buf); + delta_uv = (s16_t) net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + if (temp < TEMP_MIN || temp > TEMP_MAX) { + return; + } + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + return; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->target_lightness = lightness; + + if (temp < state->temp_range_min) { + temp = state->temp_range_min; + } else if (temp > state->temp_range_max) { + temp = state->temp_range_max; + } + + state->target_temp = temp; + state->target_delta_uv = delta_uv; + + if (state->target_lightness != state->lightness || + state->target_temp != state->temp || + state->target_delta_uv != state->delta_uv) { + light_ctl_tt_values(state, tt, delay); + } else { + light_ctl_publish(model); + return; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->lightness = state->target_lightness; + state->temp = state->target_temp; + state->delta_uv = state->target_delta_uv; + } + + state->transition->just_started = true; + light_ctl_publish(model); + light_ctl_handler(state); +} + +static void light_ctl_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t tid, tt, delay; + s16_t delta_uv; + u16_t lightness, temp; + s64_t now; + struct light_ctl_state *state = model->user_data; + + lightness = net_buf_simple_pull_le16(buf); + temp = net_buf_simple_pull_le16(buf); + delta_uv = (s16_t) net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + if (temp < TEMP_MIN || temp > TEMP_MAX) { + return; + } + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + light_ctl_get(model, ctx, buf); + return; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + state->target_lightness = lightness; + + if (temp < state->temp_range_min) { + temp = state->temp_range_min; + } else if (temp > state->temp_range_max) { + temp = state->temp_range_max; + } + + state->target_temp = temp; + state->target_delta_uv = delta_uv; + + if (state->target_lightness != state->lightness || + state->target_temp != state->temp || + state->target_delta_uv != state->delta_uv) { + light_ctl_tt_values(state, tt, delay); + } else { + light_ctl_get(model, ctx, buf); + light_ctl_publish(model); + return; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->lightness = state->target_lightness; + state->temp = state->target_temp; + state->delta_uv = state->target_delta_uv; + } + + state->transition->just_started = true; + light_ctl_get(model, ctx, buf); + light_ctl_publish(model); + light_ctl_handler(state); +} + +static void light_ctl_temp_range_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 5 + 4); + struct light_ctl_state *state = model->user_data; + + state->status_code = RANGE_SUCCESSFULLY_UPDATED; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_TEMP_RANGE_STATUS); + net_buf_simple_add_u8(msg, state->status_code); + net_buf_simple_add_le16(msg, state->temp_range_min); + net_buf_simple_add_le16(msg, state->temp_range_max); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send LightCTL Temp Range Status response\n"); + } + + os_mbuf_free_chain(msg); +} + +static void light_ctl_default_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 6 + 4); + struct light_ctl_state *state = model->user_data; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_DEFAULT_STATUS); + net_buf_simple_add_le16(msg, state->lightness_def); + net_buf_simple_add_le16(msg, state->temp_def); + net_buf_simple_add_le16(msg, state->delta_uv_def); + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send LightCTL Default Status response\n"); + } + + os_mbuf_free_chain(msg); +} + +/* Light CTL Setup Server message handlers */ + +static void light_ctl_default_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct light_ctl_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_DEFAULT_STATUS); + net_buf_simple_add_le16(msg, state->lightness_def); + net_buf_simple_add_le16(msg, state->temp_def); + net_buf_simple_add_le16(msg, state->delta_uv_def); + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } +} + +static bool light_ctl_default_setunack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u16_t lightness, temp; + s16_t delta_uv; + struct light_ctl_state *state = model->user_data; + + lightness = net_buf_simple_pull_le16(buf); + temp = net_buf_simple_pull_le16(buf); + delta_uv = (s16_t) net_buf_simple_pull_le16(buf); + + /* Here, Model specification is silent about tid implementation */ + + if (temp < TEMP_MIN || temp > TEMP_MAX) { + return false; + } + + if (temp < state->temp_range_min) { + temp = state->temp_range_min; + } else if (temp > state->temp_range_max) { + temp = state->temp_range_max; + } + + if (state->lightness_def != lightness || state->temp_def != temp || + state->delta_uv_def != delta_uv) { + state->lightness_def = lightness; + state->temp_def = temp; + state->delta_uv_def = delta_uv; + + save_on_flash(LIGHTNESS_TEMP_DEF_STATE); + } + + return true; +} + +static void light_ctl_default_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + if (light_ctl_default_setunack(model, ctx, buf) == true) { + light_ctl_default_publish(model); + } +} + +static void light_ctl_default_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + if (light_ctl_default_setunack(model, ctx, buf) == true) { + light_ctl_default_get(model, ctx, buf); + light_ctl_default_publish(model); + } +} + +static void light_ctl_temp_range_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct light_ctl_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_TEMP_RANGE_STATUS); + net_buf_simple_add_u8(msg, state->status_code); + net_buf_simple_add_le16(msg, state->temp_range_min); + net_buf_simple_add_le16(msg, state->temp_range_max); + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } +} + +static bool light_ctl_temp_range_setunack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u16_t min, max; + struct light_ctl_state *state = model->user_data; + + min = net_buf_simple_pull_le16(buf); + max = net_buf_simple_pull_le16(buf); + + /* Here, Model specification is silent about tid implementation */ + + /* This is as per 6.1.3.1 in Mesh Model Specification */ + if (min < TEMP_MIN || min > TEMP_MAX || + max < TEMP_MIN || max > TEMP_MAX) { + return false; + } + + if (min <= max) { + state->status_code = RANGE_SUCCESSFULLY_UPDATED; + + if (state->temp_range_min != min || + state->temp_range_max != max) { + + state->temp_range_min = min; + state->temp_range_max = max; + + save_on_flash(TEMPERATURE_RANGE); + } + } else { + /* The provided value for Range Max cannot be set */ + state->status_code = CANNOT_SET_RANGE_MAX; + return false; + } + + return true; +} + +static void light_ctl_temp_range_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + if (light_ctl_temp_range_setunack(model, ctx, buf) == true) { + light_ctl_temp_range_publish(model); + } +} + +static void light_ctl_temp_range_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + if (light_ctl_temp_range_setunack(model, ctx, buf) == true) { + light_ctl_temp_range_get(model, ctx, buf); + light_ctl_temp_range_publish(model); + } +} + +/* Light CTL Client message handlers */ +static void light_ctl_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from LIGHT_CTL_SRV\n"); + printk("Present CTL Lightness = %04x\n", net_buf_simple_pull_le16(buf)); + printk("Present CTL Temperature = %04x\n", + net_buf_simple_pull_le16(buf)); + + if (buf->om_len == 5) { + printk("Target CTL Lightness = %04x\n", + net_buf_simple_pull_le16(buf)); + printk("Target CTL Temperature = %04x\n", + net_buf_simple_pull_le16(buf)); + printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf)); + } +} + +static void light_ctl_temp_range_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from LIGHT_CTL_SRV (Temperature Range)\n"); + printk("Status Code = %02x\n", net_buf_simple_pull_u8(buf)); + printk("Range Min = %04x\n", net_buf_simple_pull_le16(buf)); + printk("Range Max = %04x\n", net_buf_simple_pull_le16(buf)); +} + +static void light_ctl_temp_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from LIGHT_CTL_TEMP_SRV\n"); + printk("Present CTL Temperature = %04x\n", + net_buf_simple_pull_le16(buf)); + printk("Present CTL Delta UV = %04x\n", + net_buf_simple_pull_le16(buf)); + + if (buf->om_len == 5) { + printk("Target CTL Temperature = %04x\n", + net_buf_simple_pull_le16(buf)); + printk("Target CTL Delta UV = %04x\n", + net_buf_simple_pull_le16(buf)); + printk("Remaining Time = %02x\n", net_buf_simple_pull_u8(buf)); + } +} + +static void light_ctl_default_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + printk("Acknownledgement from LIGHT_CTL_SRV (Default)\n"); + printk("Lightness = %04x\n", net_buf_simple_pull_le16(buf)); + printk("Temperature = %04x\n", net_buf_simple_pull_le16(buf)); + printk("Delta UV = %04x\n", net_buf_simple_pull_le16(buf)); +} + +/* Light CTL Temp. Server message handlers */ +static void light_ctl_temp_get(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 9 + 4); + struct light_ctl_state *state = model->user_data; + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_TEMP_STATUS); + net_buf_simple_add_le16(msg, state->temp); + net_buf_simple_add_le16(msg, state->delta_uv); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_le16(msg, state->target_temp); + net_buf_simple_add_le16(msg, state->target_delta_uv); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) { + printk("Unable to send LightCTL Temp. Status response\n"); + } + + os_mbuf_free_chain(msg); +} + +void light_ctl_temp_publish(struct bt_mesh_model *model) +{ + int err; + struct os_mbuf *msg = model->pub->msg; + struct light_ctl_state *state = model->user_data; + + if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) { + return; + } + + bt_mesh_model_msg_init(msg, BT_MESH_MODEL_LIGHT_CTL_TEMP_STATUS); + net_buf_simple_add_le16(msg, state->temp); + net_buf_simple_add_le16(msg, state->delta_uv); + + if (state->transition->counter) { + calculate_rt(state->transition); + net_buf_simple_add_le16(msg, state->target_temp); + net_buf_simple_add_le16(msg, state->target_delta_uv); + net_buf_simple_add_u8(msg, state->transition->rt); + } + + err = bt_mesh_model_publish(model); + if (err) { + printk("bt_mesh_model_publish err %d\n", err); + } +} + +static void light_ctl_temp_set_unack(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t tid, tt, delay; + s16_t delta_uv; + u16_t temp; + s64_t now; + struct light_ctl_state *state = model->user_data; + + temp = net_buf_simple_pull_le16(buf); + delta_uv = (s16_t) net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + if (temp < TEMP_MIN || temp > TEMP_MAX) { + return; + } + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + return; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + + if (temp < state->temp_range_min) { + temp = state->temp_range_min; + } else if (temp > state->temp_range_max) { + temp = state->temp_range_max; + } + + state->target_temp = temp; + state->target_delta_uv = delta_uv; + + if (state->target_temp != state->temp || + state->target_delta_uv != state->delta_uv) { + light_ctl_temp_tt_values(state, tt, delay); + } else { + light_ctl_temp_publish(model); + return; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->temp = state->target_temp; + state->delta_uv = state->target_delta_uv; + } + + state->transition->just_started = true; + light_ctl_temp_publish(model); + light_ctl_temp_handler(state); +} + +static void light_ctl_temp_set(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct os_mbuf *buf) +{ + u8_t tid, tt, delay; + s16_t delta_uv; + u16_t temp; + s64_t now; + struct light_ctl_state *state = model->user_data; + + temp = net_buf_simple_pull_le16(buf); + delta_uv = (s16_t) net_buf_simple_pull_le16(buf); + tid = net_buf_simple_pull_u8(buf); + + if (temp < TEMP_MIN || temp > TEMP_MAX) { + return; + } + + now = k_uptime_get(); + if (state->last_tid == tid && + state->last_src_addr == ctx->addr && + state->last_dst_addr == ctx->recv_dst && + (now - state->last_msg_timestamp <= K_SECONDS(6))) { + light_ctl_temp_get(model, ctx, buf); + return; + } + + switch (buf->om_len) { + case 0x00: /* No optional fields are available */ + tt = default_tt; + delay = 0; + break; + case 0x02: /* Optional fields are available */ + tt = net_buf_simple_pull_u8(buf); + if ((tt & 0x3F) == 0x3F) { + return; + } + + delay = net_buf_simple_pull_u8(buf); + break; + default: + return; + } + + *ptr_counter = 0; + os_callout_stop(ptr_timer); + + state->last_tid = tid; + state->last_src_addr = ctx->addr; + state->last_dst_addr = ctx->recv_dst; + state->last_msg_timestamp = now; + + if (temp < state->temp_range_min) { + temp = state->temp_range_min; + } else if (temp > state->temp_range_max) { + temp = state->temp_range_max; + } + + state->target_temp = temp; + state->target_delta_uv = delta_uv; + + if (state->target_temp != state->temp || + state->target_delta_uv != state->delta_uv) { + light_ctl_temp_tt_values(state, tt, delay); + } else { + light_ctl_temp_get(model, ctx, buf); + light_ctl_temp_publish(model); + return; + } + + /* For Instantaneous Transition */ + if (state->transition->counter == 0) { + state->temp = state->target_temp; + state->delta_uv = state->target_delta_uv; + } + + state->transition->just_started = true; + light_ctl_temp_get(model, ctx, buf); + light_ctl_temp_publish(model); + light_ctl_temp_handler(state); +} + +/* message handlers (End) */ + +/* Mapping of message handlers for Generic OnOff Server (0x1000) */ +static const struct bt_mesh_model_op gen_onoff_srv_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x01), 0, gen_onoff_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x02), 2, gen_onoff_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x03), 2, gen_onoff_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Generic OnOff Client (0x1001) */ +static const struct bt_mesh_model_op gen_onoff_cli_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x04), 1, gen_onoff_status }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Generic Levl Server (0x1002) */ +static const struct bt_mesh_model_op gen_level_srv_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x05), 0, gen_level_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x06), 3, gen_level_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x07), 3, gen_level_set_unack }, + { BT_MESH_MODEL_OP_2(0x82, 0x09), 5, gen_delta_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x0A), 5, gen_delta_set_unack }, + { BT_MESH_MODEL_OP_2(0x82, 0x0B), 3, gen_move_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x0C), 3, gen_move_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Generic Level Client (0x1003) */ +static const struct bt_mesh_model_op gen_level_cli_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x08), 2, gen_level_status }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Generic Default TT Server (0x1004) */ +static const struct bt_mesh_model_op gen_def_trans_time_srv_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x0D), 0, gen_def_trans_time_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x0E), 1, gen_def_trans_time_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x0F), 1, gen_def_trans_time_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Generic Default TT Client (0x1005) */ +static const struct bt_mesh_model_op gen_def_trans_time_cli_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x10), 1, gen_def_trans_time_status }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Generic Power OnOff Server (0x1006) */ +static const struct bt_mesh_model_op gen_power_onoff_srv_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x11), 0, gen_onpowerup_get }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Generic Power OnOff Setup Server (0x1007) */ +static const struct bt_mesh_model_op gen_power_onoff_setup_srv_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x13), 1, gen_onpowerup_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x14), 1, gen_onpowerup_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Generic Power OnOff Client (0x1008) */ +static const struct bt_mesh_model_op gen_power_onoff_cli_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x12), 1, gen_onpowerup_status }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Light Lightness Server (0x1300) */ +static const struct bt_mesh_model_op light_lightness_srv_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x4B), 0, light_lightness_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x4C), 3, light_lightness_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x4D), 3, light_lightness_set_unack }, + { BT_MESH_MODEL_OP_2(0x82, 0x4F), 0, light_lightness_linear_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x50), 3, light_lightness_linear_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x51), 3, + light_lightness_linear_set_unack }, + { BT_MESH_MODEL_OP_2(0x82, 0x53), 0, light_lightness_last_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x55), 0, light_lightness_default_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x57), 0, light_lightness_range_get }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Light Lightness Setup Server (0x1301) */ +static const struct bt_mesh_model_op light_lightness_setup_srv_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x59), 2, light_lightness_default_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x5A), 2, + light_lightness_default_set_unack }, + { BT_MESH_MODEL_OP_2(0x82, 0x5B), 4, light_lightness_range_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x5C), 4, light_lightness_range_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Light Lightness Client (0x1302) */ +static const struct bt_mesh_model_op light_lightness_cli_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x4E), 2, light_lightness_status }, + { BT_MESH_MODEL_OP_2(0x82, 0x52), 2, light_lightness_linear_status }, + { BT_MESH_MODEL_OP_2(0x82, 0x54), 2, light_lightness_last_status }, + { BT_MESH_MODEL_OP_2(0x82, 0x56), 2, light_lightness_default_status }, + { BT_MESH_MODEL_OP_2(0x82, 0x58), 5, light_lightness_range_status }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Light CTL Server (0x1303) */ +static const struct bt_mesh_model_op light_ctl_srv_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x5D), 0, light_ctl_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x5E), 7, light_ctl_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x5F), 7, light_ctl_set_unack }, + { BT_MESH_MODEL_OP_2(0x82, 0x62), 0, light_ctl_temp_range_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x67), 0, light_ctl_default_get }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Light CTL Setup Server (0x1304) */ +static const struct bt_mesh_model_op light_ctl_setup_srv_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x69), 6, light_ctl_default_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x6A), 6, light_ctl_default_set_unack }, + { BT_MESH_MODEL_OP_2(0x82, 0x6B), 4, light_ctl_temp_range_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x6C), 4, light_ctl_temp_range_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Light CTL Client (0x1305) */ +static const struct bt_mesh_model_op light_ctl_cli_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x60), 4, light_ctl_status }, + { BT_MESH_MODEL_OP_2(0x82, 0x63), 5, light_ctl_temp_range_status }, + { BT_MESH_MODEL_OP_2(0x82, 0x66), 4, light_ctl_temp_status }, + { BT_MESH_MODEL_OP_2(0x82, 0x68), 6, light_ctl_default_status }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Light CTL Temp. Server (0x1306) */ +static const struct bt_mesh_model_op light_ctl_temp_srv_op[] = { + { BT_MESH_MODEL_OP_2(0x82, 0x61), 0, light_ctl_temp_get }, + { BT_MESH_MODEL_OP_2(0x82, 0x64), 5, light_ctl_temp_set }, + { BT_MESH_MODEL_OP_2(0x82, 0x65), 5, light_ctl_temp_set_unack }, + BT_MESH_MODEL_OP_END, +}; + +/* Mapping of message handlers for Vendor (0x4321) */ +static const struct bt_mesh_model_op vnd_ops[] = { + { BT_MESH_MODEL_OP_3(0x01, CID_RUNTIME), 0, vnd_get }, + { BT_MESH_MODEL_OP_3(0x02, CID_RUNTIME), 3, vnd_set }, + { BT_MESH_MODEL_OP_3(0x03, CID_RUNTIME), 3, vnd_set_unack }, + { BT_MESH_MODEL_OP_3(0x04, CID_RUNTIME), 6, vnd_status }, + BT_MESH_MODEL_OP_END, +}; + +struct bt_mesh_model root_models[] = { + BT_MESH_MODEL_CFG_SRV(&cfg_srv), + BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub), + + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, + gen_onoff_srv_op, &gen_onoff_srv_pub_root, + &gen_onoff_srv_root_user_data), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, + gen_onoff_cli_op, &gen_onoff_cli_pub_root, + NULL), + + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_SRV, + gen_level_srv_op, &gen_level_srv_pub_root, + &gen_level_srv_root_user_data), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_CLI, + gen_level_cli_op, &gen_level_cli_pub_root, + NULL), + + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_SRV, + gen_def_trans_time_srv_op, + &gen_def_trans_time_srv_pub, + &gen_def_trans_time_srv_user_data), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_CLI, + gen_def_trans_time_cli_op, + &gen_def_trans_time_cli_pub, + NULL), + + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_POWER_ONOFF_SRV, + gen_power_onoff_srv_op, &gen_power_onoff_srv_pub, + &gen_power_onoff_srv_user_data), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_POWER_ONOFF_SETUP_SRV, + gen_power_onoff_setup_srv_op, + &gen_power_onoff_srv_pub, + &gen_power_onoff_srv_user_data), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_POWER_ONOFF_CLI, + gen_power_onoff_cli_op, &gen_power_onoff_cli_pub, + NULL), + + BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV, + light_lightness_srv_op, &light_lightness_srv_pub, + &light_lightness_srv_user_data), + BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SETUP_SRV, + light_lightness_setup_srv_op, + &light_lightness_srv_pub, + &light_lightness_srv_user_data), + BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_CLI, + light_lightness_cli_op, &light_lightness_cli_pub, + NULL), + + BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_CTL_SRV, + light_ctl_srv_op, &light_ctl_srv_pub, + &light_ctl_srv_user_data), + BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_CTL_SETUP_SRV, + light_ctl_setup_srv_op, &light_ctl_srv_pub, + &light_ctl_srv_user_data), + BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_CTL_CLI, + light_ctl_cli_op, &light_ctl_cli_pub, + NULL), +}; + +struct bt_mesh_model vnd_models[] = { + BT_MESH_MODEL_VND(CID_RUNTIME, 0x4321, vnd_ops, + &vnd_pub, &vnd_user_data), +}; + +struct bt_mesh_model s0_models[] = { + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_SRV, + gen_level_srv_op, &gen_level_srv_pub_s0, + &gen_level_srv_s0_user_data), + BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_CLI, + gen_level_cli_op, &gen_level_cli_pub_s0, + NULL), + + BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_CTL_TEMP_SRV, + light_ctl_temp_srv_op, &light_ctl_srv_pub, + &light_ctl_srv_user_data), +}; + +static struct bt_mesh_elem elements[] = { + BT_MESH_ELEM(0, root_models, vnd_models), + BT_MESH_ELEM(0, s0_models, BT_MESH_MODEL_NONE), +}; + +const struct bt_mesh_comp comp = { + .cid = CID_RUNTIME, + .elem = elements, + .elem_count = ARRAY_SIZE(elements), +}; + diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/device_composition.h b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/device_composition.h new file mode 100644 index 00000000..38507195 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/device_composition.h @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _DEVICE_COMPOSITION_H +#define _DEVICE_COMPOSITION_H + +#define CID_RUNTIME 0x05C3 + +#define STATE_OFF 0x00 +#define STATE_ON 0x01 +#define STATE_DEFAULT 0x01 +#define STATE_RESTORE 0x02 + +/* Following 4 values are as per Mesh Model specification */ +#define LIGHTNESS_MIN 0x0001 +#define LIGHTNESS_MAX 0xFFFF +#define TEMP_MIN 0x0320 +#define TEMP_MAX 0x4E20 + +/* Refer 7.2 of Mesh Model Specification */ +#define RANGE_SUCCESSFULLY_UPDATED 0x00 +#define CANNOT_SET_RANGE_MIN 0x01 +#define CANNOT_SET_RANGE_MAX 0x02 + +struct generic_onoff_state { + u8_t onoff; + u8_t target_onoff; + + u8_t last_tid; + u16_t last_src_addr; + u16_t last_dst_addr; + s64_t last_msg_timestamp; + + s32_t tt_delta; + + struct transition *transition; +}; + +struct generic_level_state { + s16_t level; + s16_t target_level; + + s16_t last_level; + s32_t last_delta; + + u8_t last_tid; + u16_t last_src_addr; + u16_t last_dst_addr; + s64_t last_msg_timestamp; + + s32_t tt_delta; + + struct transition *transition; +}; + +struct generic_onpowerup_state { + u8_t onpowerup; +}; + +struct gen_def_trans_time_state { + u8_t tt; +}; + +struct vendor_state { + int current; + u32_t response; + u8_t last_tid; + u16_t last_src_addr; + u16_t last_dst_addr; + s64_t last_msg_timestamp; +}; + +struct light_lightness_state { + u16_t linear; + u16_t target_linear; + + u16_t actual; + u16_t target_actual; + + u16_t last; + u16_t def; + + u8_t status_code; + u16_t light_range_min; + u16_t light_range_max; + u32_t lightness_range; + + u8_t last_tid; + u16_t last_src_addr; + u16_t last_dst_addr; + s64_t last_msg_timestamp; + + s32_t tt_delta_actual; + s32_t tt_delta_linear; + + struct transition *transition; +}; + +struct light_ctl_state { + u16_t lightness; + u16_t target_lightness; + + u16_t temp; + u16_t target_temp; + + s16_t delta_uv; + s16_t target_delta_uv; + + u8_t status_code; + u16_t temp_range_min; + u16_t temp_range_max; + u32_t temperature_range; + + u16_t lightness_def; + u16_t temp_def; + u32_t lightness_temp_def; + s16_t delta_uv_def; + + u32_t lightness_temp_last; + + u8_t last_tid; + u16_t last_src_addr; + u16_t last_dst_addr; + s64_t last_msg_timestamp; + + s32_t tt_delta_lightness; + s32_t tt_delta_temp; + s32_t tt_delta_duv; + + struct transition *transition; +}; + +extern struct generic_onoff_state gen_onoff_srv_root_user_data; +extern struct generic_level_state gen_level_srv_root_user_data; +extern struct gen_def_trans_time_state gen_def_trans_time_srv_user_data; +extern struct generic_onpowerup_state gen_power_onoff_srv_user_data; +extern struct light_lightness_state light_lightness_srv_user_data; +extern struct light_ctl_state light_ctl_srv_user_data; +extern struct generic_level_state gen_level_srv_s0_user_data; + +extern struct bt_mesh_model root_models[]; +extern struct bt_mesh_model vnd_models[]; +extern struct bt_mesh_model s0_models[]; + +extern const struct bt_mesh_comp comp; + +void gen_onoff_publish(struct bt_mesh_model *model); +void gen_level_publish(struct bt_mesh_model *model); +void light_lightness_publish(struct bt_mesh_model *model); +void light_lightness_linear_publish(struct bt_mesh_model *model); +void light_ctl_publish(struct bt_mesh_model *model); +void light_ctl_temp_publish(struct bt_mesh_model *model); + +#endif diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/main.c b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/main.c new file mode 100644 index 00000000..7c8d65e6 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/main.c @@ -0,0 +1,250 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#include "console/console.h" +#include "hal/hal_gpio.h" +#include "mesh/mesh.h" + +#include "app_gpio.h" +#include "storage.h" + +#include "ble_mesh.h" +#include "device_composition.h" +#include "no_transition_work_handler.h" +#include "publisher.h" +#include "state_binding.h" +#include "transition.h" + +static bool reset; + +static void light_default_var_init(void) +{ + gen_def_trans_time_srv_user_data.tt = 0x00; + + gen_power_onoff_srv_user_data.onpowerup = STATE_DEFAULT; + + light_lightness_srv_user_data.light_range_min = LIGHTNESS_MIN; + light_lightness_srv_user_data.light_range_max = LIGHTNESS_MAX; + light_lightness_srv_user_data.last = LIGHTNESS_MAX; + light_lightness_srv_user_data.def = LIGHTNESS_MAX; + + /* Following 2 values are as per specification */ + light_ctl_srv_user_data.temp_range_min = TEMP_MIN; + light_ctl_srv_user_data.temp_range_max = TEMP_MAX; + + light_ctl_srv_user_data.temp_def = TEMP_MIN; + + light_ctl_srv_user_data.lightness_temp_last = + (u32_t) ((LIGHTNESS_MAX << 16) | TEMP_MIN); +} + +static void light_default_status_init(void) +{ + u16_t lightness; + + lightness = (u16_t) (light_ctl_srv_user_data.lightness_temp_last >> 16); + + if (lightness) { + gen_onoff_srv_root_user_data.onoff = STATE_ON; + } else { + gen_onoff_srv_root_user_data.onoff = STATE_OFF; + } + + /* Retrieve Default Lightness & Temperature Values */ + + if (light_ctl_srv_user_data.lightness_temp_def) { + light_ctl_srv_user_data.lightness_def = (u16_t) + (light_ctl_srv_user_data.lightness_temp_def >> 16); + + light_ctl_srv_user_data.temp_def = (u16_t) + (light_ctl_srv_user_data.lightness_temp_def); + } + + light_lightness_srv_user_data.def = + light_ctl_srv_user_data.lightness_def; + + light_ctl_srv_user_data.temp = light_ctl_srv_user_data.temp_def; + + /* Retrieve Range of Lightness & Temperature */ + + if (light_lightness_srv_user_data.lightness_range) { + light_lightness_srv_user_data.light_range_max = (u16_t) + (light_lightness_srv_user_data.lightness_range >> 16); + + light_lightness_srv_user_data.light_range_min = (u16_t) + (light_lightness_srv_user_data.lightness_range); + } + + if (light_ctl_srv_user_data.temperature_range) { + light_ctl_srv_user_data.temp_range_max = (u16_t) + (light_ctl_srv_user_data.temperature_range >> 16); + + light_ctl_srv_user_data.temp_range_min = (u16_t) + (light_ctl_srv_user_data.temperature_range); + } + + switch (gen_power_onoff_srv_user_data.onpowerup) { + case STATE_OFF: + gen_onoff_srv_root_user_data.onoff = STATE_OFF; + state_binding(ONOFF, ONOFF_TEMP); + break; + case STATE_DEFAULT: + gen_onoff_srv_root_user_data.onoff = STATE_ON; + state_binding(ONOFF, ONOFF_TEMP); + break; + case STATE_RESTORE: + light_lightness_srv_user_data.last = (u16_t) + (light_ctl_srv_user_data.lightness_temp_last >> 16); + + light_ctl_srv_user_data.temp = + (u16_t) (light_ctl_srv_user_data.lightness_temp_last); + + state_binding(ONPOWERUP, ONOFF_TEMP); + break; + } + + default_tt = gen_def_trans_time_srv_user_data.tt; +} + +void update_light_state(void) +{ + u8_t power, color; + + power = 100 * ((float) lightness / 65535); + color = 100 * ((float) (temperature + 32768) / 65535); + + printk("power-> %d, color-> %d\n", power, color); + + if (lightness) { + /* LED1 On */ + hal_gpio_write(led_device[0], 0); + } else { + /* LED1 Off */ + hal_gpio_write(led_device[0], 1); + } + + if (power < 50) { + /* LED3 On */ + hal_gpio_write(led_device[2], 0); + } else { + /* LED3 Off */ + hal_gpio_write(led_device[2], 1); + } + + if (color < 50) { + /* LED4 On */ + hal_gpio_write(led_device[3], 0); + } else { + /* LED4 Off */ + hal_gpio_write(led_device[3], 1); + } + + if (*ptr_counter == 0 || reset == false) { + reset = true; + os_callout_reset(&no_transition_work, 0); + } +} + +static void short_time_multireset_bt_mesh_unprovisioning(void) +{ + if (reset_counter >= 4) { + reset_counter = 0; + printk("BT Mesh reset\n"); + bt_mesh_reset(); + } else { + printk("Reset Counter -> %d\n", reset_counter); + reset_counter++; + } + + save_on_flash(RESET_COUNTER); +} + +static void reset_counter_timer_handler(struct os_event *dummy) +{ + reset_counter = 0; + save_on_flash(RESET_COUNTER); + printk("Reset Counter set to Zero\n"); +} + +struct os_callout reset_counter_timer; + +static void init_timers(void) +{ + + os_callout_init(&reset_counter_timer, os_eventq_dflt_get(), + reset_counter_timer_handler, NULL); + os_callout_reset(&reset_counter_timer, + os_time_ms_to_ticks32(K_MSEC(7000))); + + no_transition_work_init(); +} + +void bt_initialized(void) +{ + light_default_status_init(); + + update_light_state(); + + randomize_publishers_TID(); + + short_time_multireset_bt_mesh_unprovisioning(); +} + +int main(void) +{ +#ifdef ARCH_sim + mcu_sim_parse_args(argc, argv); +#endif + + /* Initialize OS */ + sysinit(); + + light_default_var_init(); + + app_gpio_init(); + + init_timers(); + + transition_timers_init(); + + init_pub(); + + ps_settings_init(); + + printk("Initializing...\n"); + + /* Initialize the NimBLE host configuration. */ + ble_hs_cfg.reset_cb = blemesh_on_reset; + ble_hs_cfg.sync_cb = blemesh_on_sync; + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + + return 0; +} diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.c b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.c new file mode 100644 index 00000000..58630bdc --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.c @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ble_mesh.h" +#include "device_composition.h" + +#include "storage.h" + +static void unsolicitedly_publish_states_work_handler(struct os_event *work) +{ + gen_onoff_publish(&root_models[2]); + gen_level_publish(&root_models[4]); + light_lightness_publish(&root_models[11]); + light_lightness_linear_publish(&root_models[11]); + light_ctl_publish(&root_models[14]); + + gen_level_publish(&s0_models[0]); + light_ctl_temp_publish(&s0_models[2]); +} + +struct os_callout unsolicitedly_publish_states_work; + +static void unsolicitedly_publish_states_timer_handler(struct os_event *dummy) +{ + os_callout_reset(&unsolicitedly_publish_states_work, 0); +} + +struct os_callout unsolicitedly_publish_states_timer; + +static void save_lightness_temp_last_values_timer_handler(struct os_event *dummy) +{ + save_on_flash(LIGHTNESS_TEMP_LAST_STATE); +} + +struct os_callout save_lightness_temp_last_values_timer; + +static void no_transition_work_handler(struct os_event *work) +{ + os_callout_reset(&unsolicitedly_publish_states_timer, + os_time_ms_to_ticks32(K_MSEC(5000))); + + /* If Lightness & Temperature values remains stable for + * 10 Seconds then & then only get stored on SoC flash. + */ + if (gen_power_onoff_srv_user_data.onpowerup == STATE_RESTORE) { + os_callout_reset(&save_lightness_temp_last_values_timer, + os_time_ms_to_ticks32( + K_MSEC(10000))); + } +} + +struct os_callout no_transition_work; + +void no_transition_work_init(void) +{ + os_callout_init(&no_transition_work, os_eventq_dflt_get(), + no_transition_work_handler, NULL); + os_callout_init(&save_lightness_temp_last_values_timer, + os_eventq_dflt_get(), + save_lightness_temp_last_values_timer_handler, + NULL); + os_callout_init(&unsolicitedly_publish_states_work, os_eventq_dflt_get(), + unsolicitedly_publish_states_work_handler, NULL); + os_callout_init(&unsolicitedly_publish_states_timer, os_eventq_dflt_get(), + unsolicitedly_publish_states_timer_handler, NULL); +} diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.h b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.h new file mode 100644 index 00000000..a747dfda --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/no_transition_work_handler.h @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _NO_TRANSITION_WORK_HANDLER_H +#define _NO_TRANSITION_WORK_HANDLER_H + +extern struct os_callout no_transition_work; + +void no_transition_work_init(void); + +#endif diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/publisher.c b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/publisher.c new file mode 100644 index 00000000..21364b81 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/publisher.c @@ -0,0 +1,266 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "console/console.h" +#include "hal/hal_gpio.h" + +#include "app_gpio.h" + +#include "ble_mesh.h" +#include "device_composition.h" +#include "publisher.h" + +#define ONOFF +#define GENERIC_LEVEL +/* #define LIGHT_CTL */ +/* #define LIGHT_CTL_TEMP */ + +static bool is_randomization_of_TIDs_done; + +#if (defined(ONOFF) || defined(ONOFF_TT)) +static u8_t tid_onoff; +#elif defined(VND_MODEL_TEST) +static u8_t tid_vnd; +#endif + +static u8_t tid_level; + +void randomize_publishers_TID(void) +{ +#if (defined(ONOFF) || defined(ONOFF_TT)) + bt_rand(&tid_onoff, sizeof(tid_onoff)); +#elif defined(VND_MODEL_TEST) + bt_rand(&tid_vnd, sizeof(tid_vnd)); +#endif + + bt_rand(&tid_level, sizeof(tid_level)); + + is_randomization_of_TIDs_done = true; +} + +static u32_t button_read(int button) +{ + return (uint32_t) hal_gpio_read(button); +} + +void publish(struct os_event *work) +{ + int err = 0; + + if (is_randomization_of_TIDs_done == false) { + return; + } + + if (button_read(button_device[0]) == 0) { +#if defined(ONOFF) + bt_mesh_model_msg_init(root_models[3].pub->msg, + BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK); + net_buf_simple_add_u8(root_models[3].pub->msg, 0x01); + net_buf_simple_add_u8(root_models[3].pub->msg, tid_onoff++); + err = bt_mesh_model_publish(&root_models[3]); +#elif defined(ONOFF_TT) + bt_mesh_model_msg_init(root_models[3].pub->msg, + BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK); + net_buf_simple_add_u8(root_models[3].pub->msg, 0x01); + net_buf_simple_add_u8(root_models[3].pub->msg, tid_onoff++); + net_buf_simple_add_u8(root_models[3].pub->msg, 0x45); + net_buf_simple_add_u8(root_models[3].pub->msg, 0x28); + err = bt_mesh_model_publish(&root_models[3]); +#elif defined(VND_MODEL_TEST) + bt_mesh_model_msg_init(vnd_models[0].pub->msg, + BT_MESH_MODEL_OP_3(0x03, CID_RUNTIME)); + net_buf_simple_add_le16(vnd_models[0].pub->msg, 0x0001); + net_buf_simple_add_u8(vnd_models[0].pub->msg, tid_vnd++); + err = bt_mesh_model_publish(&vnd_models[0]); +#endif + + } else if (button_read(button_device[1]) == 0) { +#if defined(ONOFF) + bt_mesh_model_msg_init(root_models[3].pub->msg, + BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK); + net_buf_simple_add_u8(root_models[3].pub->msg, 0x00); + net_buf_simple_add_u8(root_models[3].pub->msg, tid_onoff++); + err = bt_mesh_model_publish(&root_models[3]); +#elif defined(ONOFF_TT) + bt_mesh_model_msg_init(root_models[3].pub->msg, + BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK); + net_buf_simple_add_u8(root_models[3].pub->msg, 0x00); + net_buf_simple_add_u8(root_models[3].pub->msg, tid_onoff++); + net_buf_simple_add_u8(root_models[3].pub->msg, 0x45); + net_buf_simple_add_u8(root_models[3].pub->msg, 0x28); + err = bt_mesh_model_publish(&root_models[3]); +#elif defined(VND_MODEL_TEST) + bt_mesh_model_msg_init(vnd_models[0].pub->msg, + BT_MESH_MODEL_OP_3(0x03, CID_RUNTIME)); + net_buf_simple_add_le16(vnd_models[0].pub->msg, 0x0000); + net_buf_simple_add_u8(vnd_models[0].pub->msg, tid_vnd++); + err = bt_mesh_model_publish(&vnd_models[0]); +#endif + + } else if (button_read(button_device[2]) == 0) { +#if defined(GENERIC_LEVEL) + bt_mesh_model_msg_init(root_models[5].pub->msg, + BT_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK); + net_buf_simple_add_le16(root_models[5].pub->msg, LEVEL_S25); + net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++); + err = bt_mesh_model_publish(&root_models[5]); +#elif defined(ONOFF_GET) + bt_mesh_model_msg_init(root_models[3].pub->msg, + BT_MESH_MODEL_OP_GEN_ONOFF_GET); + err = bt_mesh_model_publish(&root_models[3]); +#elif defined(GENERIC_DELTA_LEVEL) + bt_mesh_model_msg_init(root_models[5].pub->msg, + BT_MESH_MODEL_OP_GEN_DELTA_SET_UNACK); + net_buf_simple_add_le32(root_models[5].pub->msg, 100); + net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++); + err = bt_mesh_model_publish(&root_models[5]); +#elif defined(GENERIC_MOVE_LEVEL_TT) + bt_mesh_model_msg_init(root_models[5].pub->msg, + BT_MESH_MODEL_OP_GEN_MOVE_SET_UNACK); + net_buf_simple_add_le16(root_models[5].pub->msg, 13100); + net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++); + net_buf_simple_add_u8(root_models[5].pub->msg, 0x45); + net_buf_simple_add_u8(root_models[5].pub->msg, 0x00); + err = bt_mesh_model_publish(&root_models[5]); +#elif defined(LIGHT_LIGHTNESS_TT) + bt_mesh_model_msg_init(root_models[13].pub->msg, + BT_MESH_MODEL_OP_2(0x82, 0x4D)); + net_buf_simple_add_le16(root_models[13].pub->msg, LEVEL_U25); + net_buf_simple_add_u8(root_models[13].pub->msg, tid_level++); + net_buf_simple_add_u8(root_models[13].pub->msg, 0x45); + net_buf_simple_add_u8(root_models[13].pub->msg, 0x28); + err = bt_mesh_model_publish(&root_models[13]); +#elif defined(LIGHT_CTL) + bt_mesh_model_msg_init(root_models[16].pub->msg, + BT_MESH_MODEL_OP_2(0x82, 0x5F)); + /* Lightness */ + net_buf_simple_add_le16(root_models[16].pub->msg, LEVEL_U25); + /* Temperature (value should be from 0x0320 to 0x4E20 */ + /* This is as per 6.1.3.1 in Mesh Model Specification */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x0320); + /* Delta UV */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000); + net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++); + err = bt_mesh_model_publish(&root_models[16]); +#elif defined(LIGHT_CTL_TT) + bt_mesh_model_msg_init(root_models[16].pub->msg, + BT_MESH_MODEL_OP_2(0x82, 0x5F)); + /* Lightness */ + net_buf_simple_add_le16(root_models[16].pub->msg, LEVEL_U25); + /* Temperature (value should be from 0x0320 to 0x4E20 */ + /* This is as per 6.1.3.1 in Mesh Model Specification */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x0320); + /* Delta UV */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000); + net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++); + net_buf_simple_add_u8(root_models[16].pub->msg, 0x45); + net_buf_simple_add_u8(root_models[16].pub->msg, 0x00); + err = bt_mesh_model_publish(&root_models[16]); +#elif defined(LIGHT_CTL_TEMP) + bt_mesh_model_msg_init(root_models[16].pub->msg, + BT_MESH_MODEL_OP_2(0x82, 0x65)); + /* Temperature (value should be from 0x0320 to 0x4E20 */ + /* This is as per 6.1.3.1 in Mesh Model Specification */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x0320); + /* Delta UV */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000); + net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++); + err = bt_mesh_model_publish(&root_models[16]); +#endif + + } else if (button_read(button_device[3]) == 0) { +#if defined(GENERIC_LEVEL) + bt_mesh_model_msg_init(root_models[5].pub->msg, + BT_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK); + net_buf_simple_add_le16(root_models[5].pub->msg, LEVEL_S100); + net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++); + err = bt_mesh_model_publish(&root_models[5]); +#elif defined(GENERIC_DELTA_LEVEL) + bt_mesh_model_msg_init(root_models[5].pub->msg, + BT_MESH_MODEL_OP_GEN_DELTA_SET_UNACK); + net_buf_simple_add_le32(root_models[5].pub->msg, -100); + net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++); + err = bt_mesh_model_publish(&root_models[5]); +#elif defined(GENERIC_MOVE_LEVEL_TT) + bt_mesh_model_msg_init(root_models[5].pub->msg, + BT_MESH_MODEL_OP_GEN_MOVE_SET_UNACK); + net_buf_simple_add_le16(root_models[5].pub->msg, -13100); + net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++); + net_buf_simple_add_u8(root_models[5].pub->msg, 0x45); + net_buf_simple_add_u8(root_models[5].pub->msg, 0x00); + err = bt_mesh_model_publish(&root_models[5]); +#elif defined(LIGHT_LIGHTNESS_TT) + bt_mesh_model_msg_init(root_models[13].pub->msg, + BT_MESH_MODEL_OP_2(0x82, 0x4D)); + net_buf_simple_add_le16(root_models[13].pub->msg, LEVEL_U100); + net_buf_simple_add_u8(root_models[13].pub->msg, tid_level++); + net_buf_simple_add_u8(root_models[13].pub->msg, 0x45); + net_buf_simple_add_u8(root_models[13].pub->msg, 0x28); + err = bt_mesh_model_publish(&root_models[13]); +#elif defined(LIGHT_CTL) + bt_mesh_model_msg_init(root_models[16].pub->msg, + BT_MESH_MODEL_OP_2(0x82, 0x5F)); + /* Lightness */ + net_buf_simple_add_le16(root_models[16].pub->msg, LEVEL_U100); + /* Temperature (value should be from 0x0320 to 0x4E20 */ + /* This is as per 6.1.3.1 in Mesh Model Specification */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x4E20); + /* Delta UV */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000); + net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++); + err = bt_mesh_model_publish(&root_models[16]); +#elif defined(LIGHT_CTL_TT) + bt_mesh_model_msg_init(root_models[16].pub->msg, + BT_MESH_MODEL_OP_2(0x82, 0x5F)); + /* Lightness */ + net_buf_simple_add_le16(root_models[16].pub->msg, LEVEL_U100); + /* Temperature (value should be from 0x0320 to 0x4E20 */ + /* This is as per 6.1.3.1 in Mesh Model Specification */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x4E20); + /* Delta UV */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000); + net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++); + net_buf_simple_add_u8(root_models[16].pub->msg, 0x45); + net_buf_simple_add_u8(root_models[16].pub->msg, 0x00); + err = bt_mesh_model_publish(&root_models[16]); +#elif defined(LIGHT_CTL_TEMP) + bt_mesh_model_msg_init(root_models[16].pub->msg, + BT_MESH_MODEL_OP_2(0x82, 0x65)); + /* Temperature (value should be from 0x0320 to 0x4E20 */ + /* This is as per 6.1.3.1 in Mesh Model Specification */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x4E20); + /* Delta UV */ + net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000); + net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++); + err = bt_mesh_model_publish(&root_models[16]); +#endif + } + + if (err) { + printk("bt_mesh_model_publish: err: %d\n", err); + } +} + diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/publisher.h b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/publisher.h new file mode 100644 index 00000000..09b740b4 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/publisher.h @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _PUBLISHER_H +#define _PUBLISHER_H + +/* Others */ +#define LEVEL_S0 -32768 +#define LEVEL_S25 -16384 +#define LEVEL_S50 0 +#define LEVEL_S75 16384 +#define LEVEL_S100 32767 + +#define LEVEL_U0 0 +#define LEVEL_U25 16384 +#define LEVEL_U50 32768 +#define LEVEL_U75 49152 +#define LEVEL_U100 65535 + +void randomize_publishers_TID(void); +void publish(struct os_event *work); + +#endif diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/state_binding.c b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/state_binding.c new file mode 100644 index 00000000..ae539433 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/state_binding.c @@ -0,0 +1,308 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "ble_mesh.h" +#include "device_composition.h" +#include "state_binding.h" +#include "transition.h" + + +u16_t lightness, target_lightness; +s16_t temperature, target_temperature; + +static s32_t ceiling(float num) +{ + s32_t inum; + + inum = (s32_t) num; + if (num == (float) inum) { + return inum; + } + + return inum + 1; +} + +u16_t actual_to_linear(u16_t val) +{ + float tmp; + + tmp = ((float) val / 65535); + + return (u16_t) ceiling(65535 * tmp * tmp); +} + +u16_t linear_to_actual(u16_t val) +{ + return (u16_t) (65535 * sqrt(((float) val / 65535))); +} + +static void constrain_lightness(u16_t var) +{ + if (var > 0 && var < light_lightness_srv_user_data.light_range_min) { + var = light_lightness_srv_user_data.light_range_min; + } else if (var > light_lightness_srv_user_data.light_range_max) { + var = light_lightness_srv_user_data.light_range_max; + } + + lightness = var; +} + +static void constrain_lightness2(u16_t var) +{ + /* This is as per Mesh Model Specification 3.3.2.2.3 */ + if (var > 0 && var < light_lightness_srv_user_data.light_range_min) { + if (gen_level_srv_root_user_data.last_delta < 0) { + var = 0U; + } else { + var = light_lightness_srv_user_data.light_range_min; + } + } else if (var > light_lightness_srv_user_data.light_range_max) { + var = light_lightness_srv_user_data.light_range_max; + } + + lightness = var; +} + +static void constrain_target_lightness(u16_t var) +{ + if (var > 0 && + var < light_lightness_srv_user_data.light_range_min) { + var = light_lightness_srv_user_data.light_range_min; + } else if (var > light_lightness_srv_user_data.light_range_max) { + var = light_lightness_srv_user_data.light_range_max; + } + + target_lightness = var; +} + +static s16_t light_ctl_temp_to_level(u16_t temp) +{ + float tmp; + + /* Mesh Model Specification 6.1.3.1.1 2nd formula start */ + + tmp = (temp - light_ctl_srv_user_data.temp_range_min) * 65535; + + tmp = tmp / (light_ctl_srv_user_data.temp_range_max - + light_ctl_srv_user_data.temp_range_min); + + return (s16_t) (tmp - 32768); + + /* 6.1.3.1.1 2nd formula end */ +} + +static u16_t level_to_light_ctl_temp(s16_t level) +{ + u16_t tmp; + float diff; + + /* Mesh Model Specification 6.1.3.1.1 1st formula start */ + diff = (float) (light_ctl_srv_user_data.temp_range_max - + light_ctl_srv_user_data.temp_range_min) / 65535; + + + tmp = (u16_t) ((level + 32768) * diff); + + return (light_ctl_srv_user_data.temp_range_min + tmp); + + /* 6.1.3.1.1 1st formula end */ +} + +void state_binding(u8_t light, u8_t temp) +{ + switch (temp) { + case ONOFF_TEMP: + case CTL_TEMP: + temperature = + light_ctl_temp_to_level(light_ctl_srv_user_data.temp); + + gen_level_srv_s0_user_data.level = temperature; + break; + case LEVEL_TEMP: + temperature = gen_level_srv_s0_user_data.level; + light_ctl_srv_user_data.temp = + level_to_light_ctl_temp(temperature); + break; + default: + break; + } + + switch (light) { + case ONPOWERUP: + if (gen_onoff_srv_root_user_data.onoff == STATE_OFF) { + lightness = 0U; + } else if (gen_onoff_srv_root_user_data.onoff == STATE_ON) { + lightness = light_lightness_srv_user_data.last; + } + break; + case ONOFF: + if (gen_onoff_srv_root_user_data.onoff == STATE_OFF) { + lightness = 0U; + } else if (gen_onoff_srv_root_user_data.onoff == STATE_ON) { + if (light_lightness_srv_user_data.def == 0) { + lightness = light_lightness_srv_user_data.last; + } else { + lightness = light_lightness_srv_user_data.def; + } + } + break; + case LEVEL: + lightness = gen_level_srv_root_user_data.level + 32768; + break; + case DELTA_LEVEL: + lightness = gen_level_srv_root_user_data.level + 32768; + constrain_lightness2(lightness); + goto jump; + case ACTUAL: + lightness = light_lightness_srv_user_data.actual; + break; + case LINEAR: + lightness = + linear_to_actual(light_lightness_srv_user_data.linear); + break; + case CTL: + lightness = light_ctl_srv_user_data.lightness; + break; + default: + break; + } + + constrain_lightness(lightness); + +jump: + if (lightness != 0) { + light_lightness_srv_user_data.last = lightness; + } + + if (lightness) { + gen_onoff_srv_root_user_data.onoff = STATE_ON; + } else { + gen_onoff_srv_root_user_data.onoff = STATE_OFF; + } + + gen_level_srv_root_user_data.level = lightness - 32768; + light_lightness_srv_user_data.actual = lightness; + light_lightness_srv_user_data.linear = actual_to_linear(lightness); + light_ctl_srv_user_data.lightness = lightness; +} + +void calculate_lightness_target_values(u8_t type) +{ + bool set_light_ctl_temp_target_value; + u16_t tmp; + + set_light_ctl_temp_target_value = true; + + switch (type) { + case ONOFF: + if (gen_onoff_srv_root_user_data.target_onoff == 0) { + tmp = 0U; + } else { + if (light_lightness_srv_user_data.def == 0) { + tmp = light_lightness_srv_user_data.last; + } else { + tmp = light_lightness_srv_user_data.def; + } + } + break; + case LEVEL: + tmp = gen_level_srv_root_user_data.target_level + 32768; + break; + case ACTUAL: + tmp = light_lightness_srv_user_data.target_actual; + break; + case LINEAR: + tmp = linear_to_actual(light_lightness_srv_user_data.target_linear); + break; + case CTL: + set_light_ctl_temp_target_value = false; + + tmp = light_ctl_srv_user_data.target_lightness; + + target_temperature = light_ctl_temp_to_level(light_ctl_srv_user_data.target_temp); + gen_level_srv_s0_user_data.target_level = target_temperature; + break; + default: + return; + } + + constrain_target_lightness(tmp); + + if (target_lightness) { + gen_onoff_srv_root_user_data.target_onoff = STATE_ON; + } else { + gen_onoff_srv_root_user_data.target_onoff = STATE_OFF; + } + + gen_level_srv_root_user_data.target_level = target_lightness - 32768; + + light_lightness_srv_user_data.target_actual = target_lightness; + + light_lightness_srv_user_data.target_linear = + actual_to_linear(target_lightness); + + light_ctl_srv_user_data.target_lightness = target_lightness; + + if (set_light_ctl_temp_target_value) { + target_temperature = light_ctl_srv_user_data.temp; + light_ctl_srv_user_data.target_temp = target_temperature; + } +} + +void calculate_temp_target_values(u8_t type) +{ + bool set_light_ctl_delta_uv_target_value; + + set_light_ctl_delta_uv_target_value = true; + + switch (type) { + case LEVEL_TEMP: + target_temperature = gen_level_srv_s0_user_data.target_level; + light_ctl_srv_user_data.target_temp = + level_to_light_ctl_temp(target_temperature); + break; + case CTL_TEMP: + set_light_ctl_delta_uv_target_value = false; + + target_temperature = light_ctl_temp_to_level(light_ctl_srv_user_data.target_temp); + gen_level_srv_s0_user_data.target_level = target_temperature; + break; + default: + return; + } + + target_lightness = light_ctl_srv_user_data.lightness; + light_ctl_srv_user_data.target_lightness = target_lightness; + + if (set_light_ctl_delta_uv_target_value) { + + light_ctl_srv_user_data.target_delta_uv = + light_ctl_srv_user_data.delta_uv; + } +} + diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/state_binding.h b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/state_binding.h new file mode 100644 index 00000000..db1f2a2e --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/state_binding.h @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _STATE_BINDING_H +#define _STATE_BINDING_H + +enum state_binding { + ONPOWERUP = 0x01, + ONOFF, + LEVEL, + DELTA_LEVEL, + ACTUAL, + LINEAR, + CTL, + IGNORE, + + ONOFF_TEMP, + LEVEL_TEMP, + CTL_TEMP, + IGNORE_TEMP +}; + +extern u16_t lightness, target_lightness; +extern s16_t temperature, target_temperature; + +void state_binding(u8_t lightness, u8_t temperature); +void calculate_lightness_target_values(u8_t type); +void calculate_temp_target_values(u8_t type); + +#endif diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/storage.c b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/storage.c new file mode 100644 index 00000000..86fec7cc --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/storage.c @@ -0,0 +1,255 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "base64/base64.h" +#include "console/console.h" +#include "mesh/mesh.h" + +#include "ble_mesh.h" +#include "device_composition.h" +#include "storage.h" + +static u8_t storage_id; +u8_t reset_counter; + +static void save_reset_counter(void) +{ + char buf[5]; + + settings_str_from_bytes(&reset_counter, sizeof(reset_counter), buf, + sizeof(buf)); + + settings_save_one("ps/rc", buf); +} + +static void save_gen_def_trans_time_state(void) +{ + char buf[5]; + + settings_str_from_bytes(&gen_def_trans_time_srv_user_data.tt, + sizeof(gen_def_trans_time_srv_user_data.tt), + buf, sizeof(buf)); + + settings_save_one("ps/gdtt", buf); +} + +static void save_gen_onpowerup_state(void) +{ + char buf[5]; + + settings_str_from_bytes(&gen_power_onoff_srv_user_data.onpowerup, + sizeof(gen_power_onoff_srv_user_data.onpowerup), + buf, sizeof(buf)); + + settings_save_one("ps/gpo", buf); + + if (gen_power_onoff_srv_user_data.onpowerup == 0x02) { + save_on_flash(LIGHTNESS_TEMP_LAST_STATE); + } +} + +static void save_lightness_temp_def_state(void) +{ + char buf[12]; + + light_ctl_srv_user_data.lightness_temp_def = + (u32_t) ((light_ctl_srv_user_data.lightness_def << 16) | + light_ctl_srv_user_data.temp_def); + + settings_str_from_bytes(&light_ctl_srv_user_data.lightness_temp_def, + sizeof(light_ctl_srv_user_data.lightness_temp_def), + buf, sizeof(buf)); + + settings_save_one("ps/ltd", buf); +} + +static void save_lightness_temp_last_state(void) +{ + char buf[12]; + + light_ctl_srv_user_data.lightness_temp_last = + (u32_t) ((light_ctl_srv_user_data.lightness << 16) | + light_ctl_srv_user_data.temp); + + settings_str_from_bytes(&light_ctl_srv_user_data.lightness_temp_last, + sizeof(light_ctl_srv_user_data.lightness_temp_last), + buf, sizeof(buf)); + + settings_save_one("ps/ltl", buf); + + printk("Light CTL Last values have beed saved !!\n"); +} + +static void save_lightness_range(void) +{ + char buf[12]; + + light_lightness_srv_user_data.lightness_range = + (u32_t) ((light_lightness_srv_user_data.light_range_max << 16) | + light_lightness_srv_user_data.light_range_min); + + settings_str_from_bytes(&light_lightness_srv_user_data.lightness_range, + sizeof(light_lightness_srv_user_data.lightness_range), + buf, sizeof(buf)); + + settings_save_one("ps/lr", buf); +} + +static void save_temperature_range(void) +{ + char buf[12]; + + light_ctl_srv_user_data.temperature_range = + (u32_t) ((light_ctl_srv_user_data.temp_range_max << 16) | + light_ctl_srv_user_data.temp_range_min); + + settings_str_from_bytes(&light_ctl_srv_user_data.temperature_range, + sizeof(light_ctl_srv_user_data.temperature_range), + buf, sizeof(buf)); + + settings_save_one("ps/tr", buf); +} + +static void storage_work_handler(struct os_event *work) +{ + switch (storage_id) { + case RESET_COUNTER: + save_reset_counter(); + break; + case GEN_DEF_TRANS_TIME_STATE: + save_gen_def_trans_time_state(); + break; + case GEN_ONPOWERUP_STATE: + save_gen_onpowerup_state(); + break; + case LIGHTNESS_TEMP_DEF_STATE: + save_lightness_temp_def_state(); + break; + case LIGHTNESS_TEMP_LAST_STATE: + save_lightness_temp_last_state(); + break; + case LIGHTNESS_RANGE: + save_lightness_range(); + break; + case TEMPERATURE_RANGE: + save_temperature_range(); + break; + } +} + +struct os_callout storage_work; + +void save_on_flash(u8_t id) +{ + storage_id = id; + os_callout_reset(&storage_work, 0); +} + +static int ps_set(int argc, char **argv, char *val) +{ + int len; + + if (argc == 1) { + if (!strcmp(argv[0], "rc")) { + len = sizeof(reset_counter); + + return settings_bytes_from_str(val, &reset_counter, + &len); + } + + if (!strcmp(argv[0], "gdtt")) { + len = sizeof(gen_def_trans_time_srv_user_data.tt); + + return settings_bytes_from_str(val, + &gen_def_trans_time_srv_user_data.tt, &len); + } + + if (!strcmp(argv[0], "gpo")) { + len = sizeof(gen_power_onoff_srv_user_data.onpowerup); + + return settings_bytes_from_str(val, + &gen_power_onoff_srv_user_data.onpowerup, &len); + } + + if (!strcmp(argv[0], "ltd")) { + len = sizeof(light_ctl_srv_user_data.lightness_temp_def); + + return settings_bytes_from_str(val, + &light_ctl_srv_user_data.lightness_temp_def, + &len); + } + + if (!strcmp(argv[0], "ltl")) { + len = sizeof(light_ctl_srv_user_data. + lightness_temp_last); + + return settings_bytes_from_str(val, + &light_ctl_srv_user_data.lightness_temp_last, + &len); + } + + if (!strcmp(argv[0], "lr")) { + len = sizeof(light_lightness_srv_user_data. + lightness_range); + + return settings_bytes_from_str(val, + &light_lightness_srv_user_data.lightness_range, + &len); + } + + if (!strcmp(argv[0], "tr")) { + len = sizeof(light_ctl_srv_user_data. + temperature_range); + + return settings_bytes_from_str(val, + &light_ctl_srv_user_data. temperature_range, + &len); + } + } + + return -ENOENT; +} + +static struct conf_handler ps_settings = { + .ch_name = "ps", + .ch_set = ps_set, +}; + +int ps_settings_init(void) +{ + int err; + + os_callout_init(&storage_work, os_eventq_dflt_get(), + storage_work_handler, NULL); + + err = conf_register(&ps_settings); + if (err) { + printk("ps_settings_register failed (err %d)", err); + return err; + } + + return 0; +} diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/storage.h b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/storage.h new file mode 100644 index 00000000..e2905048 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/storage.h @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _STORAGE_H +#define _STORAGE_H + +enum ps_variables_id { + RESET_COUNTER = 0x01, + GEN_DEF_TRANS_TIME_STATE, + GEN_ONPOWERUP_STATE, + LIGHTNESS_TEMP_DEF_STATE, + LIGHTNESS_TEMP_LAST_STATE, + LIGHTNESS_RANGE, + TEMPERATURE_RANGE +}; + +extern u8_t reset_counter; + +extern struct os_callout storage_work; + +int ps_settings_init(void); +void save_on_flash(u8_t id); + +#endif diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/transition.c b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/transition.c new file mode 100644 index 00000000..c9463e10 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/transition.c @@ -0,0 +1,792 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ble_mesh.h" +#include "common.h" +#include "device_composition.h" +#include "state_binding.h" +#include "transition.h" + +struct os_callout onoff_work; +struct os_callout level_lightness_work; +struct os_callout level_temp_work; +struct os_callout light_lightness_actual_work; +struct os_callout light_lightness_linear_work; +struct os_callout light_ctl_work; +struct os_callout light_ctl_temp_work; + +struct os_callout dummy_timer; + +u8_t transition_type, default_tt; +u32_t *ptr_counter; +struct os_callout *ptr_timer = &dummy_timer; + +struct transition lightness_transition, temp_transition; + +/* Function to calculate Remaining Time (Start) */ + +void calculate_rt(struct transition *transition) +{ + u8_t steps, resolution; + s32_t duration_remainder; + s64_t now; + + if (transition->just_started) { + transition->rt = transition->tt; + } else { + now = k_uptime_get(); + duration_remainder = transition->total_duration - + (now - transition->start_timestamp); + + if (duration_remainder > 620000) { + /* > 620 seconds -> resolution = 0b11 [10 minutes] */ + resolution = 0x03; + steps = duration_remainder / 600000; + } else if (duration_remainder > 62000) { + /* > 62 seconds -> resolution = 0b10 [10 seconds] */ + resolution = 0x02; + steps = duration_remainder / 10000; + } else if (duration_remainder > 6200) { + /* > 6.2 seconds -> resolution = 0b01 [1 seconds] */ + resolution = 0x01; + steps = duration_remainder / 1000; + } else if (duration_remainder > 0) { + /* <= 6.2 seconds -> resolution = 0b00 [100 ms] */ + resolution = 0x00; + steps = duration_remainder / 100; + } else { + resolution = 0x00; + steps = 0x00; + } + + transition->rt = (resolution << 6) | steps; + } +} + +/* Function to calculate Remaining Time (End) */ + +static void bound_states_transition_type_reassignment(u8_t type) +{ + switch (type) { + case ONOFF: + case LEVEL: + case ACTUAL: + case LINEAR: + light_ctl_srv_user_data.transition = &lightness_transition; + break; + case CTL: + light_ctl_srv_user_data.transition = &lightness_transition; + gen_level_srv_s0_user_data.transition = &lightness_transition; + break; + case LEVEL_TEMP: + case CTL_TEMP: + gen_level_srv_s0_user_data.transition = &temp_transition; + light_ctl_srv_user_data.transition = &temp_transition; + break; + default: + break; + } +} + +static void tt_values_calculator(struct transition *transition) +{ + u8_t steps_multiplier, resolution; + + resolution = (transition->tt >> 6); + steps_multiplier = (transition->tt & 0x3F); + + switch (resolution) { + case 0: /* 100ms */ + transition->total_duration = steps_multiplier * 100; + break; + case 1: /* 1 second */ + transition->total_duration = steps_multiplier * 1000; + break; + case 2: /* 10 seconds */ + transition->total_duration = steps_multiplier * 10000; + break; + case 3: /* 10 minutes */ + transition->total_duration = steps_multiplier * 600000; + break; + } + + transition->counter = ((float) transition->total_duration / 100); + + if (transition->counter > DEVICE_SPECIFIC_RESOLUTION) { + transition->counter = DEVICE_SPECIFIC_RESOLUTION; + } + + ptr_counter = &transition->counter; +} + +void onoff_tt_values(struct generic_onoff_state *state, u8_t tt, u8_t delay) +{ + bound_states_transition_type_reassignment(ONOFF); + calculate_lightness_target_values(ONOFF); + state->transition->tt = tt; + state->transition->delay = delay; + + if (tt != 0) { + tt_values_calculator(state->transition); + } else { + return; + } + + state->transition->quo_tt = state->transition->total_duration / + state->transition->counter; + + state->tt_delta = ((float) (lightness - target_lightness) / + state->transition->counter); +} + +void level_tt_values(struct generic_level_state *state, u8_t tt, u8_t delay) +{ + if (state == &gen_level_srv_root_user_data) { + bound_states_transition_type_reassignment(LEVEL); + calculate_lightness_target_values(LEVEL); + } else if (state == &gen_level_srv_s0_user_data) { + bound_states_transition_type_reassignment(LEVEL_TEMP); + calculate_temp_target_values(LEVEL_TEMP); + } + state->transition->tt = tt; + state->transition->delay = delay; + + if (tt != 0) { + tt_values_calculator(state->transition); + } else { + return; + } + + state->transition->quo_tt = state->transition->total_duration / + state->transition->counter; + + state->tt_delta = ((float) (state->level - state->target_level) / + state->transition->counter); +} + +void light_lightness_actual_tt_values(struct light_lightness_state *state, + u8_t tt, u8_t delay) +{ + bound_states_transition_type_reassignment(ACTUAL); + calculate_lightness_target_values(ACTUAL); + state->transition->tt = tt; + state->transition->delay = delay; + + if (tt != 0) { + tt_values_calculator(state->transition); + } else { + return; + } + + state->transition->quo_tt = state->transition->total_duration / + state->transition->counter; + + state->tt_delta_actual = + ((float) (state->actual - state->target_actual) / + state->transition->counter); +} + +void light_lightness_linear_tt_values(struct light_lightness_state *state, + u8_t tt, u8_t delay) +{ + bound_states_transition_type_reassignment(LINEAR); + calculate_lightness_target_values(LINEAR); + state->transition->tt = tt; + state->transition->delay = delay; + + if (tt != 0) { + tt_values_calculator(state->transition); + } else { + return; + } + + state->transition->quo_tt = state->transition->total_duration / + state->transition->counter; + + state->tt_delta_linear = + ((float) (state->linear - state->target_linear) / + state->transition->counter); +} + +void light_ctl_tt_values(struct light_ctl_state *state, u8_t tt, u8_t delay) +{ + bound_states_transition_type_reassignment(CTL); + calculate_lightness_target_values(CTL); + state->transition->tt = tt; + state->transition->delay = delay; + + if (tt != 0) { + tt_values_calculator(state->transition); + } else { + return; + } + + state->transition->quo_tt = state->transition->total_duration / + state->transition->counter; + + state->tt_delta_lightness = + ((float) (state->lightness - state->target_lightness) / + state->transition->counter); + + state->tt_delta_temp = + ((float) (state->temp - state->target_temp) / + state->transition->counter); + + state->tt_delta_duv = + ((float) (state->delta_uv - state->target_delta_uv) / + state->transition->counter); +} + +void light_ctl_temp_tt_values(struct light_ctl_state *state, + u8_t tt, u8_t delay) +{ + bound_states_transition_type_reassignment(CTL_TEMP); + calculate_temp_target_values(CTL_TEMP); + state->transition->tt = tt; + state->transition->delay = delay; + + if (tt != 0) { + tt_values_calculator(state->transition); + } else { + return; + } + + state->transition->quo_tt = state->transition->total_duration / + state->transition->counter; + + state->tt_delta_temp = ((float) (state->temp - state->target_temp) / + state->transition->counter); + + state->tt_delta_duv = + ((float) (state->delta_uv - state->target_delta_uv) / + state->transition->counter); +} + +/* Timers related handlers & threads (Start) */ +static void onoff_work_handler(struct os_event *work) +{ + struct generic_onoff_state *state = &gen_onoff_srv_root_user_data; + + if (state->transition->just_started) { + state->transition->just_started = false; + + if (state->transition->counter == 0) { + state_binding(ONOFF, IGNORE_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } else { + state->transition->start_timestamp = k_uptime_get(); + + if (state->target_onoff == STATE_ON) { + state->onoff = STATE_ON; + } + } + + return; + } + + if (state->transition->counter != 0) { + state->transition->counter--; + + lightness -= state->tt_delta; + + state_binding(IGNORE, IGNORE_TEMP); + update_light_state(); + } + + if (state->transition->counter == 0) { + state->onoff = state->target_onoff; + lightness = target_lightness; + + state_binding(IGNORE, IGNORE_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } +} + +static void level_lightness_work_handler(struct os_event *work) +{ + u8_t level; + struct generic_level_state *state = &gen_level_srv_root_user_data; + + switch (transition_type) { + case LEVEL_TT: + level = LEVEL; + break; + case LEVEL_TT_DELTA: + level = DELTA_LEVEL; + break; + case LEVEL_TT_MOVE: + level = LEVEL; + break; + default: + return; + } + + if (state->transition->just_started) { + state->transition->just_started = false; + + if (state->transition->counter == 0) { + state_binding(level, IGNORE_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } else { + state->transition->start_timestamp = k_uptime_get(); + } + + return; + } + + if (state->transition->counter != 0) { + state->transition->counter--; + + state->level -= state->tt_delta; + + state_binding(level, IGNORE_TEMP); + update_light_state(); + } + + if (state->transition->counter == 0) { + state->level = state->target_level; + + state_binding(level, IGNORE_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } +} + +static void level_temp_work_handler(struct os_event *work) +{ + struct generic_level_state *state = &gen_level_srv_s0_user_data; + + switch (transition_type) { + case LEVEL_TEMP_TT: + break; + case LEVEL_TEMP_TT_DELTA: + break; + case LEVEL_TEMP_TT_MOVE: + break; + default: + return; + } + + if (state->transition->just_started) { + state->transition->just_started = false; + + if (state->transition->counter == 0) { + state_binding(IGNORE, LEVEL_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } else { + state->transition->start_timestamp = k_uptime_get(); + } + + return; + } + + if (state->transition->counter != 0) { + state->transition->counter--; + + state->level -= state->tt_delta; + + state_binding(IGNORE, LEVEL_TEMP); + update_light_state(); + } + + if (state->transition->counter == 0) { + state->level = state->target_level; + + state_binding(IGNORE, LEVEL_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } +} + +static void light_lightness_actual_work_handler(struct os_event *work) +{ + struct light_lightness_state *state = &light_lightness_srv_user_data; + + if (state->transition->just_started) { + state->transition->just_started = false; + + if (state->transition->counter == 0) { + state_binding(ACTUAL, IGNORE_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } else { + state->transition->start_timestamp = k_uptime_get(); + } + + return; + } + + if (state->transition->counter != 0) { + state->transition->counter--; + + state->actual -= state->tt_delta_actual; + + state_binding(ACTUAL, IGNORE_TEMP); + update_light_state(); + } + + if (state->transition->counter == 0) { + state->actual = state->target_actual; + + state_binding(ACTUAL, IGNORE_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } +} + +static void light_lightness_linear_work_handler(struct os_event *work) +{ + struct light_lightness_state *state = &light_lightness_srv_user_data; + + if (state->transition->just_started) { + state->transition->just_started = false; + + if (state->transition->counter == 0) { + state_binding(LINEAR, IGNORE_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } else { + state->transition->start_timestamp = k_uptime_get(); + } + + return; + } + + if (state->transition->counter != 0) { + state->transition->counter--; + + state->linear -= state->tt_delta_linear; + + state_binding(LINEAR, IGNORE_TEMP); + update_light_state(); + } + + if (state->transition->counter == 0) { + state->linear = state->target_linear; + + state_binding(LINEAR, IGNORE_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } +} + +static void light_ctl_work_handler(struct os_event *work) +{ + struct light_ctl_state *state = &light_ctl_srv_user_data; + + if (state->transition->just_started) { + state->transition->just_started = false; + + if (state->transition->counter == 0) { + state_binding(CTL, CTL_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } else { + state->transition->start_timestamp = k_uptime_get(); + } + + return; + } + + if (state->transition->counter != 0) { + state->transition->counter--; + + /* Lightness */ + state->lightness -= state->tt_delta_lightness; + + /* Temperature */ + state->temp -= state->tt_delta_temp; + + /* Delta_UV */ + state->delta_uv -= state->tt_delta_duv; + + state_binding(CTL, CTL_TEMP); + update_light_state(); + } + + if (state->transition->counter == 0) { + state->lightness = state->target_lightness; + state->temp = state->target_temp; + state->delta_uv = state->target_delta_uv; + + state_binding(CTL, CTL_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } +} + +static void light_ctl_temp_work_handler(struct os_event *work) +{ + struct light_ctl_state *state = &light_ctl_srv_user_data; + + if (state->transition->just_started) { + state->transition->just_started = false; + + if (state->transition->counter == 0) { + state_binding(IGNORE, CTL_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } else { + state->transition->start_timestamp = k_uptime_get(); + } + + return; + } + + if (state->transition->counter != 0) { + state->transition->counter--; + + /* Temperature */ + state->temp -= state->tt_delta_temp; + + /* Delta UV */ + state->delta_uv -= state->tt_delta_duv; + + state_binding(IGNORE, CTL_TEMP); + update_light_state(); + } + + if (state->transition->counter == 0) { + state->temp = state->target_temp; + state->delta_uv = state->target_delta_uv; + + state_binding(IGNORE, CTL_TEMP); + update_light_state(); + + os_callout_stop(ptr_timer); + } +} + +static void dummy_timer_handler(struct os_event *ev) +{ } + +static void onoff_tt_handler(struct os_event *ev) +{ + struct generic_onoff_state *state = ev->ev_arg; + + assert(state != NULL); + os_callout_reset(&onoff_work, 0); + os_callout_reset(&state->transition->timer, + os_time_ms_to_ticks32( + K_MSEC(state->transition->quo_tt))); +} + +static void level_lightness_tt_handler(struct os_event *ev) +{ + struct generic_level_state *state = ev->ev_arg; + + assert(state != NULL); + os_callout_reset(&level_lightness_work, 0); + os_callout_reset(&state->transition->timer, + os_time_ms_to_ticks32( + K_MSEC(state->transition->quo_tt))); +} + +static void level_temp_tt_handler(struct os_event *ev) +{ + struct generic_level_state *state = ev->ev_arg; + + assert(state != NULL); + os_callout_reset(&level_temp_work, 0); + os_callout_reset(&state->transition->timer, + os_time_ms_to_ticks32( + K_MSEC(state->transition->quo_tt))); +} + +static void light_lightness_actual_tt_handler(struct os_event *ev) +{ + struct light_lightness_state *state = ev->ev_arg; + + assert(state != NULL); + os_callout_reset(&light_lightness_actual_work, 0); + os_callout_reset(&state->transition->timer, + os_time_ms_to_ticks32( + K_MSEC(state->transition->quo_tt))); +} + +static void light_lightness_linear_tt_handler(struct os_event *ev) +{ + struct light_lightness_state *state = ev->ev_arg; + + assert(state != NULL); + os_callout_reset(&light_lightness_linear_work, 0); + os_callout_reset(&state->transition->timer, + os_time_ms_to_ticks32( + K_MSEC(state->transition->quo_tt))); +} + +static void light_ctl_tt_handler(struct os_event *ev) +{ + struct light_ctl_state *state = ev->ev_arg; + + assert(state != NULL); + os_callout_reset(&light_ctl_work, 0); + os_callout_reset(&state->transition->timer, + os_time_ms_to_ticks32( + K_MSEC(state->transition->quo_tt))); +} + +static void light_ctl_temp_tt_handler(struct os_event *ev) +{ + struct light_ctl_state *state = ev->ev_arg; + + assert(state != NULL); + os_callout_reset(&light_ctl_temp_work, 0); + os_callout_reset(&state->transition->timer, + os_time_ms_to_ticks32( + K_MSEC(state->transition->quo_tt))); +} +/* Timers related handlers & threads (End) */ + +/* Messages handlers (Start) */ +void onoff_handler(struct generic_onoff_state *state) +{ + ptr_timer = &state->transition->timer; + + os_callout_init(ptr_timer, os_eventq_dflt_get(), + onoff_tt_handler, NULL); + ptr_timer->c_ev.ev_arg = state; + os_callout_reset(ptr_timer, + os_time_ms_to_ticks32( + K_MSEC(5 * state->transition->delay))); +} + +void level_lightness_handler(struct generic_level_state *state) +{ + ptr_timer = &state->transition->timer; + + os_callout_init(ptr_timer, os_eventq_dflt_get(), + level_lightness_tt_handler, NULL); + ptr_timer->c_ev.ev_arg = state; + os_callout_reset(ptr_timer, + os_time_ms_to_ticks32( + K_MSEC(5 * state->transition->delay))); +} + +void level_temp_handler(struct generic_level_state *state) +{ + ptr_timer = &state->transition->timer; + + os_callout_init(ptr_timer, os_eventq_dflt_get(), + level_temp_tt_handler, NULL); + ptr_timer->c_ev.ev_arg = state; + os_callout_reset(ptr_timer, + os_time_ms_to_ticks32( + K_MSEC(5 * state->transition->delay))); +} + +void light_lightness_actual_handler(struct light_lightness_state *state) +{ + ptr_timer = &state->transition->timer; + + os_callout_init(ptr_timer, os_eventq_dflt_get(), + light_lightness_actual_tt_handler, NULL); + ptr_timer->c_ev.ev_arg = state; + os_callout_reset(ptr_timer, + os_time_ms_to_ticks32( + K_MSEC(5 * state->transition->delay))); +} + +void light_lightness_linear_handler(struct light_lightness_state *state) +{ + ptr_timer = &state->transition->timer; + + os_callout_init(ptr_timer, os_eventq_dflt_get(), + light_lightness_linear_tt_handler, NULL); + ptr_timer->c_ev.ev_arg = state; + os_callout_reset(ptr_timer, + os_time_ms_to_ticks32( + K_MSEC(5 * state->transition->delay))); +} + +void light_ctl_handler(struct light_ctl_state *state) +{ + ptr_timer = &state->transition->timer; + + os_callout_init(ptr_timer, os_eventq_dflt_get(), + light_ctl_tt_handler, NULL); + ptr_timer->c_ev.ev_arg = state; + os_callout_reset(ptr_timer, + os_time_ms_to_ticks32( + K_MSEC(5 * state->transition->delay))); +} + +void light_ctl_temp_handler(struct light_ctl_state *state) +{ + ptr_timer = &state->transition->timer; + + os_callout_init(ptr_timer, os_eventq_dflt_get(), + light_ctl_temp_tt_handler, NULL); + ptr_timer->c_ev.ev_arg = state; + os_callout_reset(ptr_timer, + os_time_ms_to_ticks32( + K_MSEC(5 * state->transition->delay))); +} +/* Messages handlers (End) */ + +void transition_timers_init(void) +{ + os_callout_init(&onoff_work, os_eventq_dflt_get(), + onoff_work_handler, NULL); + + os_callout_init(&level_lightness_work, os_eventq_dflt_get(), + level_lightness_work_handler, NULL); + os_callout_init(&level_temp_work, os_eventq_dflt_get(), + level_temp_work_handler, NULL); + + os_callout_init(&light_lightness_actual_work, + os_eventq_dflt_get(), + light_lightness_actual_work_handler, NULL); + os_callout_init(&light_lightness_linear_work, + os_eventq_dflt_get(), + light_lightness_linear_work_handler, NULL); + + os_callout_init(&light_ctl_work, os_eventq_dflt_get(), + light_ctl_work_handler, NULL); + os_callout_init(&light_ctl_temp_work, os_eventq_dflt_get(), + light_ctl_temp_work_handler, NULL); + + os_callout_init(&dummy_timer, os_eventq_dflt_get(), + dummy_timer_handler, NULL); +} + diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/transition.h b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/transition.h new file mode 100644 index 00000000..84101395 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/src/transition.h @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models + * + * Copyright (c) 2018 Vikrant More + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _TRANSITION_H +#define _TRANSITION_H + +#define UNKNOWN_VALUE 0x3F +#define DEVICE_SPECIFIC_RESOLUTION 10 + +enum level_transition_types { + LEVEL_TT, + LEVEL_TT_DELTA, + LEVEL_TT_MOVE, + + LEVEL_TEMP_TT, + LEVEL_TEMP_TT_DELTA, + LEVEL_TEMP_TT_MOVE, +}; + +struct transition { + bool just_started; + u8_t tt; + u8_t rt; + u8_t delay; + u32_t quo_tt; + u32_t counter; + u32_t total_duration; + s64_t start_timestamp; + + struct os_callout timer; +}; + +extern u8_t transition_type, default_tt; +extern u32_t *ptr_counter; +extern struct os_callout *ptr_timer; + +extern struct transition lightness_transition, temp_transition; + +extern struct os_callout dummy_timer; + +void calculate_rt(struct transition *transition); + + +void onoff_tt_values(struct generic_onoff_state *state, u8_t tt, u8_t delay); +void level_tt_values(struct generic_level_state *state, u8_t tt, u8_t delay); +void light_lightness_actual_tt_values(struct light_lightness_state *state, + u8_t tt, u8_t delay); +void light_lightness_linear_tt_values(struct light_lightness_state *state, + u8_t tt, u8_t delay); +void light_ctl_tt_values(struct light_ctl_state *state, u8_t tt, u8_t delay); +void light_ctl_temp_tt_values(struct light_ctl_state *state, + u8_t tt, u8_t delay); + +void onoff_handler(struct generic_onoff_state *state); +void level_lightness_handler(struct generic_level_state *state); +void level_temp_handler(struct generic_level_state *state); +void light_lightness_actual_handler(struct light_lightness_state *state); +void light_lightness_linear_handler(struct light_lightness_state *state); +void light_ctl_handler(struct light_ctl_state *state); +void light_ctl_temp_handler(struct light_ctl_state *state); + +void transition_timers_init(void); + +#endif diff --git a/src/libs/mynewt-nimble/apps/blemesh_models_example_2/syscfg.yml b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/syscfg.yml new file mode 100644 index 00000000..b56e9d2d --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_models_example_2/syscfg.yml @@ -0,0 +1,60 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.vals: + # Set log level to info (disable debug logging). + LOG_LEVEL: 1 + + # Default task settings + OS_MAIN_STACK_SIZE: 4096 + + # SMP is not supported in this app, so disable smp-over-shell. + SHELL_MGMT: 0 + + MSYS_1_BLOCK_COUNT: 48 + + FLOAT_USER: 1 + HARD_FLOAT: 1 + + BLE_MESH_DEV_UUID: "((uint8_t[16]){0xdd, 0xdd, 0})" + BLE_MESH_ADV_BUF_COUNT: 60 + BLE_MESH_TX_SEG_MAX: 6 + BLE_MESH_TX_SEG_MSG_COUNT: 3 + BLE_MESH_RX_SEG_MSG_COUNT: 3 + BLE_MESH_CRPL: 128 + BLE_MESH_RPL_STORE_TIMEOUT: 120 + BLE_MESH_MSG_CACHE_SIZE: 100 + + BLE_MESH_SETTINGS: 1 + CONFIG_FCB: 1 + + BLE_MESH: 1 + BLE_MESH_RELAY: 1 + BLE_MESH_LOW_POWER: 0 + BLE_MESH_LPN_AUTO: 0 + BLE_MESH_FRIEND: 0 + + BLE_MESH_PROV: 1 + BLE_MESH_PB_ADV: 1 + BLE_MESH_PB_GATT: 1 + BLE_MESH_GATT_PROXY: 1 + + BLE_MESH_SUBNET_COUNT: 2 + BLE_MESH_APP_KEY_COUNT: 2 + BLE_MESH_MODEL_GROUP_COUNT: 2 + BLE_MESH_LABEL_COUNT: 3 diff --git a/src/libs/mynewt-nimble/apps/blemesh_shell/pkg.yml b/src/libs/mynewt-nimble/apps/blemesh_shell/pkg.yml new file mode 100644 index 00000000..ccf43be1 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_shell/pkg.yml @@ -0,0 +1,37 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: apps/blemesh_shell +pkg.type: app +pkg.description: Sample application for BLE Mesh node with shell support +pkg.author: "Michał Narajowski " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/shell" + - nimble/controller + - nimble/host + - nimble/host/services/gap + - nimble/host/services/gatt + - nimble/host/store/ram + - nimble/transport/ram diff --git a/src/libs/mynewt-nimble/apps/blemesh_shell/src/main.c b/src/libs/mynewt-nimble/apps/blemesh_shell/src/main.c new file mode 100644 index 00000000..4ad23e1d --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_shell/src/main.c @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#define MESH_LOG_MODULE BLE_MESH_LOG + +#include +#include "os/mynewt.h" +#include "mesh/mesh.h" +#include "console/console.h" +#include "hal/hal_system.h" +#include "hal/hal_gpio.h" +#include "bsp/bsp.h" +#include "shell/shell.h" + +/* BLE */ +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "services/gap/ble_svc_gap.h" +#include "mesh/glue.h" +#include "mesh/testing.h" + + +void net_recv_ev(uint8_t ttl, uint8_t ctl, uint16_t src, uint16_t dst, + const void *payload, size_t payload_len) +{ + console_printf("Received net packet: ttl 0x%02x ctl 0x%02x src 0x%04x " + "dst 0x%04x " "payload_len %d\n", ttl, ctl, src, dst, + payload_len); +} + +static void model_bound_cb(u16_t addr, struct bt_mesh_model *model, + u16_t key_idx) +{ + console_printf("Model bound: remote addr 0x%04x key_idx 0x%04x model %p\n", + addr, key_idx, model); +} + +static void model_unbound_cb(u16_t addr, struct bt_mesh_model *model, + u16_t key_idx) +{ + console_printf("Model unbound: remote addr 0x%04x key_idx 0x%04x " + "model %p\n", addr, key_idx, model); +} + +static void invalid_bearer_cb(u8_t opcode) +{ + console_printf("Invalid bearer: opcode 0x%02x\n", opcode); +} + +static void incomp_timer_exp_cb(void) +{ + console_printf("Incomplete timer expired\n"); +} + +static struct bt_test_cb bt_test_cb = { + .mesh_net_recv = net_recv_ev, + .mesh_model_bound = model_bound_cb, + .mesh_model_unbound = model_unbound_cb, + .mesh_prov_invalid_bearer = invalid_bearer_cb, + .mesh_trans_incomp_timer_exp = incomp_timer_exp_cb, +}; + +static void +blemesh_on_reset(int reason) +{ + BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason); +} + +static void +blemesh_on_sync(void) +{ + console_printf("Bluetooth initialized\n"); + + shell_register_default_module("mesh"); + + if (IS_ENABLED(CONFIG_BT_TESTING)) { + bt_test_cb_register(&bt_test_cb); + } +} + +int +main(void) +{ + /* Initialize OS */ + sysinit(); + + /* Initialize the NimBLE host configuration. */ + ble_hs_cfg.reset_cb = blemesh_on_reset; + ble_hs_cfg.sync_cb = blemesh_on_sync; + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + + hal_gpio_init_out(LED_2, 0); + + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} diff --git a/src/libs/mynewt-nimble/apps/blemesh_shell/syscfg.yml b/src/libs/mynewt-nimble/apps/blemesh_shell/syscfg.yml new file mode 100644 index 00000000..cbfd0c59 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blemesh_shell/syscfg.yml @@ -0,0 +1,57 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.vals: + # Enable the shell task. + SHELL_TASK: 1 + + # Set log level to info (disable debug logging). + LOG_LEVEL: 0 + + # Default task settings + OS_MAIN_STACK_SIZE: 768 + + # SMP is not supported in this app, so disable smp-over-shell. + SHELL_MGMT: 0 + + MSYS_1_BLOCK_COUNT: 80 + + BLE_MESH_ADV_BUF_COUNT: 20 + BLE_MESH_TX_SEG_MAX: 6 + + BLE_MESH: 1 + BLE_MESH_SHELL: 1 + BLE_MESH_PROV: 1 + BLE_MESH_PROVISIONER: 1 + BLE_MESH_RELAY: 1 + BLE_MESH_PB_ADV: 1 + BLE_MESH_PB_GATT: 1 + BLE_MESH_LOW_POWER: 1 + BLE_MESH_LPN_AUTO: 0 + BLE_MESH_GATT_PROXY: 1 + BLE_MESH_LABEL_COUNT: 2 + BLE_MESH_SUBNET_COUNT: 2 + BLE_MESH_MODEL_GROUP_COUNT: 2 + BLE_MESH_MODEL_EXTENSIONS: 1 + BLE_MESH_APP_KEY_COUNT: 4 + BLE_MESH_IV_UPDATE_TEST: 1 + BLE_MESH_TESTING: 1 + BLE_MESH_FRIEND: 1 + BLE_MESH_CFG_CLI: 1 + BLE_MESH_SETTINGS: 0 + CONFIG_NFFS: 0 diff --git a/src/libs/mynewt-nimble/apps/bleprph/pkg.yml b/src/libs/mynewt-nimble/apps/bleprph/pkg.yml new file mode 100644 index 00000000..38b6e445 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bleprph/pkg.yml @@ -0,0 +1,45 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: apps/bleprph +pkg.type: app +pkg.description: Simple BLE peripheral application. +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/boot/split" + - "@mcuboot/boot/bootutil" + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/mgmt/imgmgr" + - "@apache-mynewt-core/mgmt/smp" + - "@apache-mynewt-core/mgmt/smp/transport/ble" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/sysinit" + - "@apache-mynewt-core/sys/id" + - nimble/host + - nimble/host/services/ans + - nimble/host/services/dis + - nimble/host/services/gap + - nimble/host/services/gatt + - nimble/host/store/config + - nimble/host/util + - nimble/transport diff --git a/src/libs/mynewt-nimble/apps/bleprph/src/bleprph.h b/src/libs/mynewt-nimble/apps/bleprph/src/bleprph.h new file mode 100644 index 00000000..5ec34610 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bleprph/src/bleprph.h @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BLEPRPH_ +#define H_BLEPRPH_ + +#include +#include "nimble/ble.h" +#include "modlog/modlog.h" +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_hs_cfg; +struct ble_gatt_register_ctxt; + +/** GATT server. */ +#define GATT_SVR_SVC_ALERT_UUID 0x1811 +#define GATT_SVR_CHR_SUP_NEW_ALERT_CAT_UUID 0x2A47 +#define GATT_SVR_CHR_NEW_ALERT 0x2A46 +#define GATT_SVR_CHR_SUP_UNR_ALERT_CAT_UUID 0x2A48 +#define GATT_SVR_CHR_UNR_ALERT_STAT_UUID 0x2A45 +#define GATT_SVR_CHR_ALERT_NOT_CTRL_PT 0x2A44 + +void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg); +int gatt_svr_init(void); + +/* PHY support */ +#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT) +#define CONN_HANDLE_INVALID 0xffff + +void phy_init(void); +void phy_conn_changed(uint16_t handle); +void phy_update(uint8_t phy); +#endif + +/** Misc. */ +void print_bytes(const uint8_t *bytes, int len); +void print_addr(const void *addr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libs/mynewt-nimble/apps/bleprph/src/gatt_svr.c b/src/libs/mynewt-nimble/apps/bleprph/src/gatt_svr.c new file mode 100644 index 00000000..632ef4fb --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bleprph/src/gatt_svr.c @@ -0,0 +1,204 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include "bsp/bsp.h" +#include "host/ble_hs.h" +#include "host/ble_uuid.h" +#include "bleprph.h" + +/** + * The vendor specific security test service consists of two characteristics: + * o random-number-generator: generates a random 32-bit number each time + * it is read. This characteristic can only be read over an encrypted + * connection. + * o static-value: a single-byte characteristic that can always be read, + * but can only be written over an encrypted connection. + */ + +/* 59462f12-9543-9999-12c8-58b459a2712d */ +static const ble_uuid128_t gatt_svr_svc_sec_test_uuid = + BLE_UUID128_INIT(0x2d, 0x71, 0xa2, 0x59, 0xb4, 0x58, 0xc8, 0x12, + 0x99, 0x99, 0x43, 0x95, 0x12, 0x2f, 0x46, 0x59); + +/* 5c3a659e-897e-45e1-b016-007107c96df6 */ +static const ble_uuid128_t gatt_svr_chr_sec_test_rand_uuid = + BLE_UUID128_INIT(0xf6, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0, + 0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c); + +/* 5c3a659e-897e-45e1-b016-007107c96df7 */ +static const ble_uuid128_t gatt_svr_chr_sec_test_static_uuid = + BLE_UUID128_INIT(0xf7, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0, + 0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c); + +static uint8_t gatt_svr_sec_test_static_val; + +static int +gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static const struct ble_gatt_svc_def gatt_svr_svcs[] = { + { + /*** Service: Security test. */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = &gatt_svr_svc_sec_test_uuid.u, + .characteristics = (struct ble_gatt_chr_def[]) { { + /*** Characteristic: Random number generator. */ + .uuid = &gatt_svr_chr_sec_test_rand_uuid.u, + .access_cb = gatt_svr_chr_access_sec_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC, + }, { + /*** Characteristic: Static value. */ + .uuid = &gatt_svr_chr_sec_test_static_uuid.u, + .access_cb = gatt_svr_chr_access_sec_test, + .flags = BLE_GATT_CHR_F_READ | + BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC, + }, { + 0, /* No more characteristics in this service. */ + } }, + }, + + { + 0, /* No more services. */ + }, +}; + +static int +gatt_svr_chr_write(struct os_mbuf *om, uint16_t min_len, uint16_t max_len, + void *dst, uint16_t *len) +{ + uint16_t om_len; + int rc; + + om_len = OS_MBUF_PKTLEN(om); + if (om_len < min_len || om_len > max_len) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + rc = ble_hs_mbuf_to_flat(om, dst, max_len, len); + if (rc != 0) { + return BLE_ATT_ERR_UNLIKELY; + } + + return 0; +} + +static int +gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + const ble_uuid_t *uuid; + int rand_num; + int rc; + + uuid = ctxt->chr->uuid; + + /* Determine which characteristic is being accessed by examining its + * 128-bit UUID. + */ + + if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_rand_uuid.u) == 0) { + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + + /* Respond with a 32-bit random number. */ + rand_num = rand(); + rc = os_mbuf_append(ctxt->om, &rand_num, sizeof rand_num); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_static_uuid.u) == 0) { + switch (ctxt->op) { + case BLE_GATT_ACCESS_OP_READ_CHR: + rc = os_mbuf_append(ctxt->om, &gatt_svr_sec_test_static_val, + sizeof gatt_svr_sec_test_static_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + + case BLE_GATT_ACCESS_OP_WRITE_CHR: + rc = gatt_svr_chr_write(ctxt->om, + sizeof gatt_svr_sec_test_static_val, + sizeof gatt_svr_sec_test_static_val, + &gatt_svr_sec_test_static_val, NULL); + return rc; + + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } + } + + /* Unknown characteristic; the nimble stack should not have called this + * function. + */ + assert(0); + return BLE_ATT_ERR_UNLIKELY; +} + +void +gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) +{ + char buf[BLE_UUID_STR_LEN]; + + switch (ctxt->op) { + case BLE_GATT_REGISTER_OP_SVC: + MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n", + ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf), + ctxt->svc.handle); + break; + + case BLE_GATT_REGISTER_OP_CHR: + MODLOG_DFLT(DEBUG, "registering characteristic %s with " + "def_handle=%d val_handle=%d\n", + ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf), + ctxt->chr.def_handle, + ctxt->chr.val_handle); + break; + + case BLE_GATT_REGISTER_OP_DSC: + MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n", + ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf), + ctxt->dsc.handle); + break; + + default: + assert(0); + break; + } +} + +int +gatt_svr_init(void) +{ + int rc; + + rc = ble_gatts_count_cfg(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_add_svcs(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + return 0; +} diff --git a/src/libs/mynewt-nimble/apps/bleprph/src/main.c b/src/libs/mynewt-nimble/apps/bleprph/src/main.c new file mode 100644 index 00000000..66f9bacc --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bleprph/src/main.c @@ -0,0 +1,359 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include "os/mynewt.h" +#include "bsp/bsp.h" +#include "hal/hal_gpio.h" +#include "console/console.h" +#include "hal/hal_system.h" +#include "config/config.h" +#include "split/split.h" +#if MYNEWT_VAL(BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM) >= 0 +#include "bootutil/image.h" +#include "imgmgr/imgmgr.h" +#include "services/dis/ble_svc_dis.h" +#endif + +/* BLE */ +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "host/util/util.h" +#include "services/gap/ble_svc_gap.h" + +/* Application-specified header. */ +#include "bleprph.h" + +static int bleprph_gap_event(struct ble_gap_event *event, void *arg); + +/** + * Logs information about a connection to the console. + */ +static void +bleprph_print_conn_desc(struct ble_gap_conn_desc *desc) +{ + MODLOG_DFLT(INFO, "handle=%d our_ota_addr_type=%d our_ota_addr=", + desc->conn_handle, desc->our_ota_addr.type); + print_addr(desc->our_ota_addr.val); + MODLOG_DFLT(INFO, " our_id_addr_type=%d our_id_addr=", + desc->our_id_addr.type); + print_addr(desc->our_id_addr.val); + MODLOG_DFLT(INFO, " peer_ota_addr_type=%d peer_ota_addr=", + desc->peer_ota_addr.type); + print_addr(desc->peer_ota_addr.val); + MODLOG_DFLT(INFO, " peer_id_addr_type=%d peer_id_addr=", + desc->peer_id_addr.type); + print_addr(desc->peer_id_addr.val); + MODLOG_DFLT(INFO, " conn_itvl=%d conn_latency=%d supervision_timeout=%d " + "encrypted=%d authenticated=%d bonded=%d\n", + desc->conn_itvl, desc->conn_latency, + desc->supervision_timeout, + desc->sec_state.encrypted, + desc->sec_state.authenticated, + desc->sec_state.bonded); +} + +/** + * Enables advertising with the following parameters: + * o General discoverable mode. + * o Undirected connectable mode. + */ +static void +bleprph_advertise(void) +{ + uint8_t own_addr_type; + struct ble_gap_adv_params adv_params; + struct ble_hs_adv_fields fields; + const char *name; + int rc; + + /* Figure out address to use while advertising (no privacy for now) */ + rc = ble_hs_id_infer_auto(0, &own_addr_type); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); + return; + } + + /** + * Set the advertisement data included in our advertisements: + * o Flags (indicates advertisement type and other general info). + * o Advertising tx power. + * o Device name. + * o 16-bit service UUIDs (alert notifications). + */ + + memset(&fields, 0, sizeof fields); + + /* Advertise two flags: + * o Discoverability in forthcoming advertisement (general) + * o BLE-only (BR/EDR unsupported). + */ + fields.flags = BLE_HS_ADV_F_DISC_GEN | + BLE_HS_ADV_F_BREDR_UNSUP; + + /* Indicate that the TX power level field should be included; have the + * stack fill this value automatically. This is done by assiging the + * special value BLE_HS_ADV_TX_PWR_LVL_AUTO. + */ + fields.tx_pwr_lvl_is_present = 1; + fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + + name = ble_svc_gap_device_name(); + fields.name = (uint8_t *)name; + fields.name_len = strlen(name); + fields.name_is_complete = 1; + + fields.uuids16 = (ble_uuid16_t[]){ + BLE_UUID16_INIT(GATT_SVR_SVC_ALERT_UUID) + }; + fields.num_uuids16 = 1; + fields.uuids16_is_complete = 1; + + rc = ble_gap_adv_set_fields(&fields); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc); + return; + } + + /* Begin advertising. */ + memset(&adv_params, 0, sizeof adv_params); + adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; + adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER, + &adv_params, bleprph_gap_event, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc); + return; + } +} + +/** + * The nimble host executes this callback when a GAP event occurs. The + * application associates a GAP event callback with each connection that forms. + * bleprph uses the same callback for all connections. + * + * @param event The type of event being signalled. + * @param ctxt Various information pertaining to the event. + * @param arg Application-specified argument; unuesd by + * bleprph. + * + * @return 0 if the application successfully handled the + * event; nonzero on failure. The semantics + * of the return code is specific to the + * particular GAP event being signalled. + */ +static int +bleprph_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + MODLOG_DFLT(INFO, "connection %s; status=%d ", + event->connect.status == 0 ? "established" : "failed", + event->connect.status); + if (event->connect.status == 0) { + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + bleprph_print_conn_desc(&desc); + +#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT) + phy_conn_changed(event->connect.conn_handle); +#endif + } + MODLOG_DFLT(INFO, "\n"); + + if (event->connect.status != 0) { + /* Connection failed; resume advertising. */ + bleprph_advertise(); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason); + bleprph_print_conn_desc(&event->disconnect.conn); + MODLOG_DFLT(INFO, "\n"); + +#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT) + phy_conn_changed(CONN_HANDLE_INVALID); +#endif + + /* Connection terminated; resume advertising. */ + bleprph_advertise(); + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE: + /* The central has updated the connection parameters. */ + MODLOG_DFLT(INFO, "connection updated; status=%d ", + event->conn_update.status); + rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc); + assert(rc == 0); + bleprph_print_conn_desc(&desc); + MODLOG_DFLT(INFO, "\n"); + return 0; + + case BLE_GAP_EVENT_ADV_COMPLETE: + MODLOG_DFLT(INFO, "advertise complete; reason=%d", + event->adv_complete.reason); + bleprph_advertise(); + return 0; + + case BLE_GAP_EVENT_ENC_CHANGE: + /* Encryption has been enabled or disabled for this connection. */ + MODLOG_DFLT(INFO, "encryption change event; status=%d ", + event->enc_change.status); + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + bleprph_print_conn_desc(&desc); + MODLOG_DFLT(INFO, "\n"); + return 0; + + case BLE_GAP_EVENT_SUBSCRIBE: + MODLOG_DFLT(INFO, "subscribe event; conn_handle=%d attr_handle=%d " + "reason=%d prevn=%d curn=%d previ=%d curi=%d\n", + event->subscribe.conn_handle, + event->subscribe.attr_handle, + event->subscribe.reason, + event->subscribe.prev_notify, + event->subscribe.cur_notify, + event->subscribe.prev_indicate, + event->subscribe.cur_indicate); + return 0; + + case BLE_GAP_EVENT_MTU: + MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d cid=%d mtu=%d\n", + event->mtu.conn_handle, + event->mtu.channel_id, + event->mtu.value); + return 0; + + case BLE_GAP_EVENT_REPEAT_PAIRING: + /* We already have a bond with the peer, but it is attempting to + * establish a new secure link. This app sacrifices security for + * convenience: just throw away the old bond and accept the new link. + */ + + /* Delete the old bond. */ + rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc); + assert(rc == 0); + ble_store_util_delete_peer(&desc.peer_id_addr); + + /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should + * continue with the pairing operation. + */ + return BLE_GAP_REPEAT_PAIRING_RETRY; + +#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT) + case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: + /* XXX: assume symmetric phy for now */ + phy_update(event->phy_updated.tx_phy); + return 0; +#endif + } + + return 0; +} + +static void +bleprph_on_reset(int reason) +{ + MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason); +} + +static void +bleprph_on_sync(void) +{ + int rc; + + /* Make sure we have proper identity address set (public preferred) */ + rc = ble_hs_util_ensure_addr(0); + assert(rc == 0); + + /* Begin advertising. */ + bleprph_advertise(); +} + +/** + * main + * + * The main task for the project. This function initializes the packages, + * then starts serving events from default event queue. + * + * @return int NOTE: this function should never return! + */ +int +main(void) +{ +#if MYNEWT_VAL(BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM) >= 0 + struct image_version ver; + static char ver_str[IMGMGR_NMGR_MAX_VER]; +#endif + int rc; + + /* Initialize OS */ + sysinit(); + + /* Initialize the NimBLE host configuration. */ + ble_hs_cfg.reset_cb = bleprph_on_reset; + ble_hs_cfg.sync_cb = bleprph_on_sync; + ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb; + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + + rc = gatt_svr_init(); + assert(rc == 0); + +#if MYNEWT_VAL(BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM) >= 0 + /* Set firmware version in DIS */ + imgr_my_version(&ver); + imgr_ver_str(&ver, ver_str); + ble_svc_dis_firmware_revision_set(ver_str); +#endif + +#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT) + phy_init(); +#endif + + conf_load(); + + /* If this app is acting as the loader in a split image setup, jump into + * the second stage application instead of starting the OS. + */ +#if MYNEWT_VAL(SPLIT_LOADER) + { + void *entry; + rc = split_app_go(&entry, true); + if (rc == 0) { + hal_system_start(entry); + } + } +#endif + + /* + * As the last thing, process events from default event queue. + */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} diff --git a/src/libs/mynewt-nimble/apps/bleprph/src/misc.c b/src/libs/mynewt-nimble/apps/bleprph/src/misc.c new file mode 100644 index 00000000..640b7ff8 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bleprph/src/misc.c @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "bleprph.h" + +/** + * Utility function to log an array of bytes. + */ +void +print_bytes(const uint8_t *bytes, int len) +{ + int i; + + for (i = 0; i < len; i++) { + MODLOG_DFLT(INFO, "%s0x%02x", i != 0 ? ":" : "", bytes[i]); + } +} + +void +print_addr(const void *addr) +{ + const uint8_t *u8p; + + u8p = addr; + MODLOG_DFLT(INFO, "%02x:%02x:%02x:%02x:%02x:%02x", + u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); +} diff --git a/src/libs/mynewt-nimble/apps/bleprph/src/phy.c b/src/libs/mynewt-nimble/apps/bleprph/src/phy.c new file mode 100644 index 00000000..c6fb2b35 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bleprph/src/phy.c @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "os/mynewt.h" +#include "bsp/bsp.h" +#include "hal/hal_gpio.h" +#include "host/ble_gap.h" +#include "bleprph.h" + +#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT) + +static const int button_gpio[4] = MYNEWT_VAL(BLEPRPH_LE_PHY_BUTTON_GPIO); +static const int led_gpio[3] = MYNEWT_VAL(BLEPRPH_LE_PHY_LED_GPIO); + +#define PHY_TO_PTR(_mask, _opts) (void *)(((_opts) << 16) | ((_mask))) +#define PTR_TO_PHY_MASK(_ptr) (uint8_t)(((int)_ptr) & 0x0ff) +#define PTR_TO_PHY_OPTS(_ptr) (uint8_t)(((int)_ptr) >> 16) + +static struct os_event gpio_event; + +static uint16_t conn_handle = CONN_HANDLE_INVALID; + +static void +gpio_irq_handler(void *arg) +{ + gpio_event.ev_arg = arg; + os_eventq_put(os_eventq_dflt_get(), &gpio_event); +} + +static void +gpio_event_handler(struct os_event *ev) +{ + uint8_t phy_mask; + uint8_t phy_opts; + int sr; + + OS_ENTER_CRITICAL(sr); + phy_mask = PTR_TO_PHY_MASK(ev->ev_arg); + phy_opts = PTR_TO_PHY_OPTS(ev->ev_arg); + OS_EXIT_CRITICAL(sr); + + if (conn_handle != CONN_HANDLE_INVALID) { + ble_gap_set_prefered_le_phy(conn_handle, phy_mask, phy_mask, phy_opts); + } +} + +static void +setup_button_gpio(int button, uint8_t phy_mask, uint8_t phy_opts) +{ + if (button < 0) { + return; + } + + hal_gpio_irq_init(button, gpio_irq_handler, PHY_TO_PTR(phy_mask, phy_opts), + HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP); + hal_gpio_irq_enable(button); +} + +void +phy_init(void) +{ + gpio_event.ev_cb = gpio_event_handler; + + /* + * XXX: we could make this configurable, but for now assume all pins are + * valid, buttons gpio pins are pulled-up and LEDs are active-low - this + * is valid for nRF52840 PDK. + */ + setup_button_gpio(button_gpio[0], BLE_GAP_LE_PHY_1M_MASK, + BLE_GAP_LE_PHY_CODED_ANY); + setup_button_gpio(button_gpio[1], BLE_GAP_LE_PHY_2M_MASK, + BLE_GAP_LE_PHY_CODED_ANY); + setup_button_gpio(button_gpio[2], BLE_GAP_LE_PHY_CODED_MASK, + BLE_GAP_LE_PHY_CODED_S2); + setup_button_gpio(button_gpio[3], BLE_GAP_LE_PHY_CODED_MASK, + BLE_GAP_LE_PHY_CODED_S8); + + hal_gpio_init_out(led_gpio[0], 1); + hal_gpio_init_out(led_gpio[1], 1); + hal_gpio_init_out(led_gpio[2], 1); +} + +void +phy_conn_changed(uint16_t handle) +{ + uint8_t phy = 0; + + conn_handle = handle; + + if (handle != CONN_HANDLE_INVALID) { + /* XXX: assume symmetric phy for now */ + ble_gap_read_le_phy(handle, &phy, &phy); + } + + phy_update(phy); +} + +void +phy_update(uint8_t phy) +{ + if (conn_handle == CONN_HANDLE_INVALID) { + hal_gpio_write(led_gpio[0], 1); + hal_gpio_write(led_gpio[1], 1); + hal_gpio_write(led_gpio[2], 1); + } else { + hal_gpio_write(led_gpio[0], !(phy == BLE_GAP_LE_PHY_1M)); + hal_gpio_write(led_gpio[1], !(phy == BLE_GAP_LE_PHY_2M)); + hal_gpio_write(led_gpio[2], !(phy == BLE_GAP_LE_PHY_CODED)); + } +} + +#endif diff --git a/src/libs/mynewt-nimble/apps/bleprph/syscfg.yml b/src/libs/mynewt-nimble/apps/bleprph/syscfg.yml new file mode 100644 index 00000000..c39e6b01 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bleprph/syscfg.yml @@ -0,0 +1,68 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.defs: + BLEPRPH_LE_PHY_SUPPORT: + description: > + Enable support for changing PHY preference on active connection. + PHY preference change is triggered by configured GPIO pins. + Current PHY is indicated using LEDs connected to configured + GPIO pins. + value: 0 + BLEPRPH_LE_PHY_BUTTON_GPIO: + description: > + GPIO pins for changing PHY preference on active connection. This + is an array of 4 GPIO pin numbers for 1M, 2M, LE Coded S=2 and + LE Coded S=8 respectively. + value: "(int[]){ BUTTON_1, BUTTON_2, BUTTON_3, BUTTON_4 }" + BLEPRPH_LE_PHY_LED_GPIO: + description: > + GPIO pins for indicating current PHY on active connection. This + is an array of 3 GPIO pin numbers for 1M, 2M and LE Coded + respectively. + value: "(int[]){ LED_1, LED_2, LED_3 }" + +syscfg.vals: + # Disable central and observer roles. + BLE_ROLE_BROADCASTER: 1 + BLE_ROLE_CENTRAL: 0 + BLE_ROLE_OBSERVER: 0 + BLE_ROLE_PERIPHERAL: 1 + + # Configure DIS + BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM: 1 + + # Log reboot messages to a flash circular buffer. + REBOOT_LOG_FCB: 1 + LOG_FCB: 1 + CONFIG_FCB: 1 + + # Enable smp commands. + STATS_MGMT: 1 + LOG_MGMT: 1 + CONFIG_MGMT: 1 + + # OS main/default task + OS_MAIN_STACK_SIZE: 512 + + # Lots of smaller mbufs are required for smp using typical BLE ATT MTU + # values. + MSYS_1_BLOCK_COUNT: 22 + MSYS_1_BLOCK_SIZE: 110 + + BLE_SVC_GAP_DEVICE_NAME: '"nimble-bleprph"' diff --git a/src/libs/mynewt-nimble/apps/blestress/README.md b/src/libs/mynewt-nimble/apps/blestress/README.md new file mode 100644 index 00000000..8524397a --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blestress/README.md @@ -0,0 +1,201 @@ +BLE Stress Tests + +****************************************************************************** + QUICK TIPS: + You need 2 controllers supported by MyNewt. One will become TX device, + the other will become RX device. + + 1. Set role (TX=0, RX=1) for current device in syscfg.yml + BLE_STRESS_TEST_ROLE: 1 + + The RX has LED2 turned on. + + 2. Set (in syscfg.yml) number of times to repeat each tested action + in use case, e.g. do 1000 times connect/disconnect to complete use case. + RX_STRESS_REPEAT: 1000 + + 3. To perform only specific test, go to rx_stress.c, + find definition of rx_stress_main_task_fn(void *arg) and in place of + for-loop just paste function rx_stress_start(i), where i is id of test. + +****************************************************************************** + +No | Use case +----------------------------------------------------------------------------- + 1 | Stress Connect -> Connect Cancel - repeat 1000 + | RX: Nothing + | TX: Connect/Connect cancel + | + 2 | Stress Connect/Disconnect legacy - repeat 1000 + | RX: Advertise legacy + | TX: Connect/Disconnect + | + 3 | Stress Connect/Disconnect ext adv - repeat 1000 + | RX: Advertise Ext + | TX: Connect/Disconnect + | + 4 | Stress connection params update (TX) - (1000) + | RX: Advertise + | TX: Connect/Connect param update + | + 5 | Stress connection params update (RX) - (1000) + | RX: Advertise/Connect param update + | TX: Connect + | + 6 | Stress Scan + | RX: Advertise a known data pattern + | TX: Scan and check received data with pattern + | + 7 | Stress PHY Update (TX) - (1000) + | RX: Advertise + | TX: Connect/Phy update + | + 8 | Stress PHY update (RX) - (1000) + | RX: Advertise/Phy update + | TX: Connect + | + 9 | Stress multi connection + | RX: Advertise Ext + | TX: Establish and maintain as many instances as possible + | +10 | Stress L2CAP send + | RX: Send 64kB of data with L2CAP + | TX: Measure bit rate and max received data MTU + | +11 | Stress Advertise/Connect/Continue Adv/Disconnect + | RX: Advertise Ext/Continue same advertise after connect + | TX: Connect + | +12 | Stress GATT indicating + | RX: Indicate + | TX: Receive indication. Measure average time of indicating. + | +13 | Stress GATT notification + | RX: Notify. Measure average time of notifying. + | TX: Count the number of received notification. + | +14 | Stress GATT Subscribe/Notify/Unsubscribe + | RX: Notify on subscribe + | TX: Measure the average time from sending a subscription request + | to receiving a notification. + | +15 | Stress Connect/Send/Disconnect + | RX: Advertise/Send via ATT/Disconnect + | TX: Receive notification. Measure time of notifying. + +****************************************************************************** + Concept: + The RX device advertises data containing a UUID128 of test use case that + should be performed. The TX device scan for known UUIDs, when it finds, + adapts to the advertised use case and runs a test. + + Stress Task vs Semaphore: + The rx_stress_start_auto function starts main stress test task that runs + all stress tests one by one. The tests are based on event handling, so to + synchronize main task with events, a semaphore is used. On use case start + there is 0 tokens for semaphore. After main task starts one of use cases, + it comes across a semaphore and has to wait. When use case is completed, + 1 token is released, so main task can use it to pass through semaphore. + The tx_stress_start_auto function works analogically. + + + Newt target set example: + rymek@rymek:~/projects/bletiny_proj$ newt target show bletest_tx + targets/bletest_tx + app=@apache-mynewt-nimble/apps/blestress + bsp=@apache-mynewt-core/hw/bsp/nordic_pca10056 + build_profile=debug + syscfg=BLE_STRESS_TEST_ROLE=0 + rymek@rymek:~/projects/bletiny_proj$ newt target show bletest_rx + targets/bletest_rx + app=@apache-mynewt-nimble/apps/blestress + bsp=@apache-mynewt-core/hw/bsp/nordic_pca10056 + build_profile=debug + syscfg=BLE_STRESS_TEST_ROLE=1 + + + Example of expected logs on TX side(LOG_LEVEL > 1): + Start test num 1 + >>>>>>>>>>>>>>>>>>>> Stress test 1 completed + Start scan for test + Start test num 2 + >>>>>>>>>>>>>>>>>>>> Stress test 2 completed + Start scan for test + Start test num 3 + >>>>>>>>>>>>>>>>>>>> Stress test 3 completed + Start scan for test + Start test num 4 + >>>>>>>>>>>>>>>>>>>> Stress test 4 completed + Start scan for test + Start test num 5 + >>>>>>>>>>>>>>>>>>>> Stress test 5 completed + Start scan for test + Start test num 6 + >>>>>>>>>>>>>>>>>>>> Stress test 6 completed + Start scan for test + Start test num 7 + >>>>>>>>>>>>>>>>>>>> Stress test 7 completed + Start scan for test + Start test num 8 + >>>>>>>>>>>>>>>>>>>> Stress test 8 completed + All tests completed + Tests results: + Use case 1 - Stress Connect -> Connect Cancel: + Con attempts = 20 + Con success = 0 + Use case 2 - Stress Connect/Disconnect legacy: + Con attempts = 20 + Con success = 20 + Use case 3 - Stress Connect/Disconnect ext adv: + Con attempts = 20 + Con success = 20 + Use case 4 - Stress connection params update (TX): + Params updates = 20 + Use case 5 - Stress connection params update (RX): + Params updates = 20 + Use case 6 - Stress Scan: + Received first packets = 20 + Received all packets = 20 + Use case 7 - Stress PHY Update (TX): + PHY updates = 20 + Use case 8 - Stress Connect -> Connect Cancel: + PHY updates = 20 + + + Example of expected logs on RX side(LOG_LEVEL > 1): + Start test num 2 + >>>>>>>>>>>>>>>>>>>> Stress test 2 completed + Start test num 3 + >>>>>>>>>>>>>>>>>>>> Stress test 3 completed + Start test num 4 + >>>>>>>>>>>>>>>>>>>> Stress test 4 completed + Start test num 5 + >>>>>>>>>>>>>>>>>>>> Stress test 5 completed + Start test num 6 + Received signal to switch test + Start test num 7 + >>>>>>>>>>>>>>>>>>>> Stress test 7 completed + Start test num 8 + >>>>>>>>>>>>>>>>>>>> Stress test 8 completed + All tests completed + Tests results: + Use case 1 - Stress Connect -> Connect Cancel: + Con attempts = 0 + Con success = 0 + Use case 2 - Stress Connect/Disconnect legacy: + Con attempts = 20 + Con success = 20 + Use case 3 - Stress Connect/Disconnect ext adv: + Con attempts = 20 + Con success = 20 + Use case 4 - Stress connection params update (TX): + Params updates = 20 + Use case 5 - Stress connection params update (RX): + Params updates = 20 + Use case 6 - Stress Scan: + Received first packets = 0 + Received all packets = 0 + Use case 7 - Stress PHY Update (TX): + PHY updates = 20 + Use case 8 - Stress Connect -> Connect Cancel: + PHY updates = 20 diff --git a/src/libs/mynewt-nimble/apps/blestress/pkg.yml b/src/libs/mynewt-nimble/apps/blestress/pkg.yml new file mode 100644 index 00000000..da23ecb0 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blestress/pkg.yml @@ -0,0 +1,39 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: "apps/blestress" +pkg.type: app +pkg.description: "Stress tests sample application." +pkg.keywords: + +pkg.deps: + - "@mcuboot/boot/bootutil" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/id" + - "@apache-mynewt-nimble/nimble/controller" + - "@apache-mynewt-nimble/nimble/host" + - "@apache-mynewt-nimble/nimble/host/util" + - "@apache-mynewt-nimble/nimble/host/services/gap" + - "@apache-mynewt-nimble/nimble/host/services/gatt" + - "@apache-mynewt-nimble/nimble/host/store/ram" + - "@apache-mynewt-nimble/nimble/transport/ram" diff --git a/src/libs/mynewt-nimble/apps/blestress/src/main.c b/src/libs/mynewt-nimble/apps/blestress/src/main.c new file mode 100644 index 00000000..ec28ed8a --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blestress/src/main.c @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include "os/mynewt.h" +#include "config/config.h" +#include "bsp.h" +#include "hal/hal_gpio.h" + +/* BLE */ +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "host/util/util.h" +#include "services/gap/ble_svc_gap.h" + +/* Application-specified header. */ +#include "rx_stress.h" +#include "tx_stress.h" +#include "stress_gatt.h" + +static void +stress_test_on_reset(int reason) +{ + MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason); +} + +static void +stress_test_on_sync(void) +{ + int rc; + + /* Make sure we have proper identity address set (public preferred) */ + rc = ble_hs_util_ensure_addr(1); + assert(rc == 0); + +#if MYNEWT_VAL(BLE_STRESS_TEST_ROLE) + rx_stress_start_auto(); +#else + tx_stress_start_auto(); +#endif +} + +/** + * main + * + * The main task for the project. This function initializes the packages, + * then starts serving events from default event queue. + * + * @return int NOTE: this function should never return! + */ +int +main(void) +{ + int rc; + + sysinit(); + + ble_hs_cfg.reset_cb = stress_test_on_reset; + ble_hs_cfg.sync_cb = stress_test_on_sync; + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + + /* Please do not change name. Otherwise some tests could fail. */ + rc = ble_svc_gap_device_name_set("STRESS"); + assert(rc == 0); + + conf_load(); + + rc = gatt_svr_init(); + assert(rc == 0); + +#if MYNEWT_VAL(BLE_STRESS_TEST_ROLE) + /* RX device */ + ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb; + hal_gpio_init_out(LED_2, 1); + hal_gpio_toggle(LED_2); +#endif + + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} diff --git a/src/libs/mynewt-nimble/apps/blestress/src/misc.c b/src/libs/mynewt-nimble/apps/blestress/src/misc.c new file mode 100644 index 00000000..bd71a871 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blestress/src/misc.c @@ -0,0 +1,224 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "misc.h" + +void +rand_bytes(uint8_t *data, int len) { + int i; + + for (i = 0; i < len; ++i) { + data[i] = (uint8_t) rand() % UINT8_MAX; + } +} + +/** + * Utility function to log an array of bytes. + */ +void +print_bytes(const uint8_t *bytes, int len) +{ + int i; + + for (i = 0; i < len; i++) { + MODLOG_DFLT(DEBUG, "%s0x%02x", i != 0 ? ":" : "", bytes[i]); + } +} + +void +print_addr(const void *addr) +{ + const uint8_t *u8p; + + u8p = addr; + MODLOG_DFLT(DEBUG, "%02x:%02x:%02x:%02x:%02x:%02x", + u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); +} + +void +print_mbuf(const struct os_mbuf *om) +{ + while (om != NULL) { + print_bytes(om->om_data, om->om_len); + om = SLIST_NEXT(om, om_next); + + if(om == NULL) { + return; + } + + /* Separate buf fields with colon to maintain continuity */ + MODLOG_DFLT(DEBUG, ":"); + } +} + +char * +addr_str(const void *addr) +{ + /* 6 bytes of MAC address * 2 signs for each byte in string + 5 colons to + * separate bytes + 1 byte for null-character appended by sprintf + */ + static char buf[6 * 2 + 5 + 1]; + const uint8_t *u8p; + + u8p = addr; + sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", + u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); + + return buf; +} + +void +print_uuid(const ble_uuid_t *uuid) +{ + char buf[BLE_UUID_STR_LEN]; + + MODLOG_DFLT(DEBUG, "%s", ble_uuid_to_str(uuid, buf)); +} + +/** + * Logs information about a connection to the console. + */ +void +print_conn_desc(const struct ble_gap_conn_desc *desc) +{ + MODLOG_DFLT(DEBUG, "handle=%d our_ota_addr_type=%d our_ota_addr=%s ", + desc->conn_handle, desc->our_ota_addr.type, + addr_str(desc->our_ota_addr.val)); + MODLOG_DFLT(DEBUG, "our_id_addr_type=%d our_id_addr=%s ", + desc->our_id_addr.type, addr_str(desc->our_id_addr.val)); + MODLOG_DFLT(DEBUG, "peer_ota_addr_type=%d peer_ota_addr=%s ", + desc->peer_ota_addr.type, addr_str(desc->peer_ota_addr.val)); + MODLOG_DFLT(DEBUG, "peer_id_addr_type=%d peer_id_addr=%s ", + desc->peer_id_addr.type, addr_str(desc->peer_id_addr.val)); + MODLOG_DFLT(DEBUG, "conn_itvl=%d conn_latency=%d supervision_timeout=%d " + "encrypted=%d authenticated=%d bonded=%d", + desc->conn_itvl, desc->conn_latency, + desc->supervision_timeout, + desc->sec_state.encrypted, + desc->sec_state.authenticated, + desc->sec_state.bonded); +} + +void +print_adv_fields(const struct ble_hs_adv_fields *fields) +{ + char s[BLE_HS_ADV_MAX_SZ]; + const uint8_t *u8p; + int i; + + if (fields->flags != 0) { + MODLOG_DFLT(DEBUG, " flags=0x%02x\n", fields->flags); + } + + if (fields->uuids16 != NULL) { + MODLOG_DFLT(DEBUG, " uuids16(%scomplete)=", + fields->uuids16_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids16; i++) { + print_uuid(&fields->uuids16[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uuids32 != NULL) { + MODLOG_DFLT(DEBUG, " uuids32(%scomplete)=", + fields->uuids32_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids32; i++) { + print_uuid(&fields->uuids32[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uuids128 != NULL) { + MODLOG_DFLT(DEBUG, " uuids128(%scomplete)=", + fields->uuids128_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids128; i++) { + print_uuid(&fields->uuids128[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->name != NULL) { + assert(fields->name_len < sizeof s - 1); + memcpy(s, fields->name, fields->name_len); + s[fields->name_len] = '\0'; + MODLOG_DFLT(DEBUG, " name(%scomplete)=%s\n", + fields->name_is_complete ? "" : "in", s); + } + + if (fields->tx_pwr_lvl_is_present) { + MODLOG_DFLT(DEBUG, " tx_pwr_lvl=%d\n", fields->tx_pwr_lvl); + } + + if (fields->slave_itvl_range != NULL) { + MODLOG_DFLT(DEBUG, " slave_itvl_range="); + print_bytes(fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->svc_data_uuid16 != NULL) { + MODLOG_DFLT(DEBUG, " svc_data_uuid16="); + print_bytes(fields->svc_data_uuid16, fields->svc_data_uuid16_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->public_tgt_addr != NULL) { + MODLOG_DFLT(DEBUG, " public_tgt_addr="); + u8p = fields->public_tgt_addr; + for (i = 0; i < fields->num_public_tgt_addrs; i++) { + MODLOG_DFLT(DEBUG, "public_tgt_addr=%s ", addr_str(u8p)); + u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN; + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->appearance_is_present) { + MODLOG_DFLT(DEBUG, " appearance=0x%04x\n", fields->appearance); + } + + if (fields->adv_itvl_is_present) { + MODLOG_DFLT(DEBUG, " adv_itvl=0x%04x\n", fields->adv_itvl); + } + + if (fields->svc_data_uuid32 != NULL) { + MODLOG_DFLT(DEBUG, " svc_data_uuid32="); + print_bytes(fields->svc_data_uuid32, fields->svc_data_uuid32_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->svc_data_uuid128 != NULL) { + MODLOG_DFLT(DEBUG, " svc_data_uuid128="); + print_bytes(fields->svc_data_uuid128, fields->svc_data_uuid128_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uri != NULL) { + MODLOG_DFLT(DEBUG, " uri="); + print_bytes(fields->uri, fields->uri_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->mfg_data != NULL) { + MODLOG_DFLT(DEBUG, " mfg_data="); + print_bytes(fields->mfg_data, fields->mfg_data_len); + MODLOG_DFLT(DEBUG, "\n"); + } +} diff --git a/src/libs/mynewt-nimble/apps/blestress/src/misc.h b/src/libs/mynewt-nimble/apps/blestress/src/misc.h new file mode 100644 index 00000000..39e5e737 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blestress/src/misc.h @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef BLE_TGT_MISC_H +#define BLE_TGT_MISC_H + +#include +#include +#include +#include "host/ble_hs.h" +#include "host/ble_uuid.h" +#include "host/ble_hs_adv.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void rand_bytes(uint8_t *data, int len); + +void print_bytes(const uint8_t *bytes, int len); + +void print_addr(const void *addr); + +void print_mbuf(const struct os_mbuf *om); + +char *addr_str(const void *addr); + +void print_uuid(const ble_uuid_t *uuid); + +void print_conn_desc(const struct ble_gap_conn_desc *desc); + +void print_adv_fields(const struct ble_hs_adv_fields *fields); + +#ifdef __cplusplus +} +#endif + +#endif //BLE_TGT_MISC_H diff --git a/src/libs/mynewt-nimble/apps/blestress/src/rx_stress.c b/src/libs/mynewt-nimble/apps/blestress/src/rx_stress.c new file mode 100644 index 00000000..a4253ce6 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blestress/src/rx_stress.c @@ -0,0 +1,1471 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include "rx_stress.h" + +/* UUID128 of stress test use cases*/ +static uint8_t rx_stress_uuid128[STRESS_UUIDS_NUM][16]; + +static struct com_stress_test_ctx rx_stress_ctxD = { + .conn_handle = 0xffff, + .cur_test_id = 0, + .s6_rcv_adv_first = 0, + .s6_rcv_adv_suc = 0, +}; + +static struct com_stress_test_ctx *rx_stress_ctx = &rx_stress_ctxD; + +#define EXTENDED_ADVERT 0 +#define LEGACY_ADVERT 1 +/* Advertising instances ids */ +#define SWITCHER_INSTANCE 0 +#define TEST_INSTANCE 1 +/* Main test task priority. Set a high value so that the task does not + * interfere with event handling */ +#define RX_STRESS_MAIN_TASK_PRIO 0xf0 + +/* Advertising settings */ +struct rx_stress_adv_set { + uint8_t instance; + uint8_t *instance_uuid128; + uint8_t legacy_pdu; + ble_gap_event_fn *cb; + const uint8_t *pattern_data; + int pattern_len; +}; + +/* Define task stack and task object */ +#define RX_STRESS_MAIN_TASK_STACK_SIZE (2000) +static struct os_task rx_stress_main_task; +static os_stack_t rx_stress_main_task_stack[RX_STRESS_MAIN_TASK_STACK_SIZE]; +static struct os_sem rx_stress_main_sem; + +static void +rx_stress_on_test_finish(int test_num) +{ + console_printf("\033[0;32m\nStress test %d completed\033[0m\n", test_num); + os_sem_release(&rx_stress_main_sem); +} + +static int +rx_stress_adv_start(uint8_t instance) +{ + int rc; + + /* Resume advertising earlier configured instance */ + rc = ble_gap_ext_adv_start(instance, 0, 0); + assert (rc == 0 || rc == 2); + MODLOG_DFLT(INFO, "Instance %d started; rc: %d\n", instance, rc); + return rc; +} + +static int +rx_stress_adv_start_with_rand_addr(uint8_t instance) +{ + int rc; + ble_addr_t addr; + + ble_gap_ext_adv_stop(instance); + + rc = ble_hs_id_gen_rnd(1, &addr); + assert (rc == 0); + + /* Set random address for advertising instance */ + rc = ble_gap_ext_adv_set_addr(instance, &addr); + assert (rc == 0); + + return rx_stress_adv_start(instance); +} + +static void +rx_stress_simple_adv(struct rx_stress_adv_set *adv_set) +{ + uint8_t own_addr_type; + struct ble_gap_ext_adv_params params; + struct ble_hs_adv_fields fields; + struct os_mbuf *adv_data; + ble_addr_t addr; + const char *name; + int rc; + int pattern_len; + + /* Determine own address type */ + rc = ble_hs_id_infer_auto(0, &own_addr_type); + if (rc != 0) { + MODLOG_DFLT(ERROR, "\033[0;31mError determining own address type; " + "rc=%d\033[0m\n", rc); + return; + } + + /* Use defaults for non-set fields */ + memset(&fields, 0, sizeof fields); + /* General Discoverable and BrEdrNotSupported */ + fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP; + /* Set device name as instance name */ + name = ble_svc_gap_device_name(); + fields.name = (uint8_t *) name; + fields.name_len = strlen(name); + fields.name_is_complete = 1; + /* Set UUID 128 data service */ + fields.svc_data_uuid128 = adv_set->instance_uuid128; + fields.svc_data_uuid128_len = 16; + + /* Use defaults for non-set params */ + memset(¶ms, 0, sizeof(params)); + /* Set adv mode */ + if (adv_set->legacy_pdu == 1) { + params.connectable = 1; + params.scannable = 1; + } else if (adv_set->pattern_len < 255) { + params.connectable = 1; + } + + params.own_addr_type = own_addr_type; + params.primary_phy = BLE_HCI_LE_PHY_1M; + /* If legacy, this param will be lowered by API */ + params.secondary_phy = BLE_HCI_LE_PHY_2M; + params.sid = adv_set->instance; + params.legacy_pdu = adv_set->legacy_pdu; + + ble_gap_set_prefered_default_le_phy(TX_PHY_MASK, RX_PHY_MASK); + + rc = ble_gap_ext_adv_remove(adv_set->instance); + assert(rc == 0 || rc == BLE_HS_EALREADY); + + /* Configure instance with the params set */ + rc = ble_gap_ext_adv_configure(adv_set->instance, ¶ms, + NULL, adv_set->cb, NULL); + assert (rc == 0); + + if (own_addr_type == 0) { + memcpy(addr.val, MYNEWT_VAL(BLE_PUBLIC_DEV_ADDR), 6); + } else { + rc = ble_hs_id_gen_rnd(1, &addr); + assert (rc == 0); + + /* Set random address for advertising instance */ + rc = ble_gap_ext_adv_set_addr(adv_set->instance, &addr); + assert (rc == 0); + } + + /* Get mbuf for adv data */ + adv_data = os_msys_get_pkthdr(16, 0); + assert(adv_data != NULL); + + /* Fill mbuf with adv fields - structured data */ + rc = ble_hs_adv_set_fields_mbuf(&fields, adv_data); + + if (rc) { + os_mbuf_free_chain(adv_data); + assert(0); + } + + pattern_len = min(adv_set->pattern_len, + MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE) - adv_data->om_len); + + /* Append to mbuf pattern data - raw data */ + rc = os_mbuf_append(adv_data, adv_set->pattern_data, pattern_len); + + if (rc) { + os_mbuf_free_chain(adv_data); + assert(0); + } + + /* Include mbuf data in advertisement */ + rc = ble_gap_ext_adv_set_data(adv_set->instance, adv_data); + assert (rc == 0); + + /* Start advertising */ + rc = ble_gap_ext_adv_start(adv_set->instance, 0, 0); + assert (rc == 0); + + MODLOG_DFLT(INFO, "instance %u started\n", adv_set->instance); +} + +static int +rx_stress_0_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n", + event->connect.status, + ++rx_stress_ctx->con_stat[0].num); + + /* Stop test advert */ + ble_gap_ext_adv_stop(TEST_INSTANCE); + ble_gap_terminate(event->connect.conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + } else { + /* Connection failed; resume advertising */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + } + return 0; + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + console_printf("\033[0;32mReceived signal to switch test\033[0m\n"); + /* Add token to semaphore. Main task will start next test. */ + os_sem_release(&rx_stress_main_sem); + + return 0; + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +rx_stress_2_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++rx_stress_ctx->con_stat[2].attempts_num; + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n", + event->connect.status, + ++rx_stress_ctx->con_stat[2].num); + } else { + /* Connection failed; resume advertising. */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + console_printf("\033[0;32m>\033[0m"); + if (rx_stress_ctx->con_stat[2].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + rx_stress_on_test_finish(2); + } else { + /* Connection terminated; resume advertising. */ + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +rx_stress_3_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++rx_stress_ctx->con_stat[3].attempts_num; + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n", + event->connect.status, + ++rx_stress_ctx->con_stat[3].num); + } else { + /* Connection failed; resume advertising. */ + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + console_printf("\033[0;32m>\033[0m"); + if (rx_stress_ctx->con_stat[3].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + rx_stress_on_test_finish(3); + } else { + /* Connection terminated; resume advertising. */ + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +rx_stress_4_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++rx_stress_ctx->con_stat[4].attempts_num; + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d", + event->connect.status, + ++rx_stress_ctx->con_stat[4].num); + + /* Remember connection handler */ + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + rx_stress_ctx->conn_handle = event->connect.conn_handle; + } else { + /* Connection failed; resume advertising. */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason); + if (rx_stress_ctx->con_stat[4].prms_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + rx_stress_on_test_finish(4); + } else { + /* Connection terminated; resume advertising. */ + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE: + if (event->conn_update.status != 0) { + MODLOG_DFLT(INFO, "Connection update failed\n"); + } else { + MODLOG_DFLT(INFO, "Connection updated; num=%d\n", + ++rx_stress_ctx->con_stat[4].prms_upd_num); + console_printf("\033[0;32m>\033[0m"); + } + + if (rx_stress_ctx->con_stat[4].prms_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + /* Test completed. */ + ble_gap_terminate(rx_stress_ctx->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +rx_stress_5_con_update(void) +{ + int rc; + + /* With every next update at least one param must change. Otherwise no + * event occurs and test will not be continued */ + struct ble_gap_upd_params params = { + .itvl_min = BLE_GAP_INITIAL_CONN_ITVL_MIN, + .itvl_max = BLE_GAP_INITIAL_CONN_ITVL_MAX, + .latency = BLE_GAP_INITIAL_CONN_LATENCY, + /* So let's change e.g. timeout value. Put ...% 2 ? 1 : 2 to make sure + * that value won't grow significantly and will be different with every + * iteration. */ + .supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT + + (rx_stress_ctx->con_stat[5].prms_upd_num % 2 ? + 1 : 2), + .min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN, + .max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN, + }; + + rc = ble_gap_update_params(rx_stress_ctx->conn_handle, ¶ms); + + if (rc == BLE_HS_ENOTCONN) { + MODLOG_DFLT(INFO, "Device disconnected. Connection update failed\n"); + assert(0); + } + + if (rc != 0) { + MODLOG_DFLT(ERROR, "\033[0;31mError during connection update; " + "rc=%d\033[0m\n", rc); + assert(0); + } + + return rc; +} + +static int +rx_stress_5_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++rx_stress_ctx->con_stat[5].attempts_num; + + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d", + event->connect.status, + ++rx_stress_ctx->con_stat[5].num); + + /* Remember connection handler */ + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + rx_stress_ctx->conn_handle = event->connect.conn_handle; + + /* Update connection. */ + rc = rx_stress_5_con_update(); + assert(rc == 0); + } else { + /* Connection failed; resume advertising. */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason); + if (rx_stress_ctx->con_stat[5].prms_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + rx_stress_on_test_finish(5); + } else { + /* Connection terminated; resume advertising. */ + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE: + if (event->conn_update.status != 0) { + MODLOG_DFLT(INFO, "Connection update failed\n"); + } else { + MODLOG_DFLT(INFO, "Connection updated; num=%d\n", + ++rx_stress_ctx->con_stat[5].prms_upd_num); + console_printf("\033[0;32m>\033[0m"); + } + + if (rx_stress_ctx->con_stat[5].prms_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + /* Test completed. */ + ble_gap_terminate(rx_stress_ctx->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + } else { + /* Update connection. */ + rc = rx_stress_5_con_update(); + assert(rc == 0); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +rx_stress_6_gap_event(struct ble_gap_event *event, void *arg) +{ + MODLOG_DFLT(INFO, "Event occurs=%d\n", event->type); + return 0; +} + +static int +rx_stress_7_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++rx_stress_ctx->con_stat[7].attempts_num; + + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d", + event->connect.status, + ++rx_stress_ctx->con_stat[7].num); + + /* Remember connection handler */ + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + rx_stress_ctx->conn_handle = event->connect.conn_handle; + } else { + /* Connection failed; resume advertising. */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason); + if (rx_stress_ctx->con_stat[7].phy_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + rx_stress_on_test_finish(7); + } else { + /* Connection terminated; resume advertising. */ + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: + if (event->phy_updated.status != 0) { + MODLOG_DFLT(INFO, "PHY update failed\n"); + } else { + MODLOG_DFLT(INFO, "PHY updated; num=%d\n", + ++rx_stress_ctx->con_stat[7].phy_upd_num); + console_printf("\033[0;32m>\033[0m"); + } + + if (rx_stress_ctx->con_stat[7].phy_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + /* Test completed. */ + ble_gap_terminate(rx_stress_ctx->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +rx_stress_8_con_update(void) +{ + int rc; + uint8_t tx_phys_mask; + uint8_t rx_phys_mask; + + ble_gap_read_le_phy(rx_stress_ctx->conn_handle, &tx_phys_mask, + &rx_phys_mask); + + /* With every next update at least one param must change */ + switch (rx_phys_mask) { + case BLE_GAP_LE_PHY_1M_MASK: + rx_phys_mask = BLE_GAP_LE_PHY_2M_MASK; + break; + case BLE_GAP_LE_PHY_2M_MASK: +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + rx_phys_mask = BLE_GAP_LE_PHY_CODED_MASK; + break; + case BLE_GAP_LE_PHY_CODED_MASK: +#endif + rx_phys_mask = BLE_GAP_LE_PHY_1M_MASK; + break; + default: + rx_phys_mask = BLE_GAP_LE_PHY_1M_MASK; + break; + } + + switch (tx_phys_mask) { + case BLE_GAP_LE_PHY_1M_MASK: + tx_phys_mask = BLE_GAP_LE_PHY_2M_MASK; + break; + case BLE_GAP_LE_PHY_2M_MASK: +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + tx_phys_mask = BLE_GAP_LE_PHY_CODED_MASK; + break; + case BLE_GAP_LE_PHY_CODED_MASK: +#endif + tx_phys_mask = BLE_GAP_LE_PHY_1M_MASK; + break; + default: + tx_phys_mask = BLE_GAP_LE_PHY_1M_MASK; + break; + } + + rc = ble_gap_set_prefered_le_phy(rx_stress_ctx->conn_handle, + tx_phys_mask, rx_phys_mask, 0); + + if (rc == BLE_HS_ENOTCONN) { + MODLOG_DFLT(INFO, "Device disconnected. Connection update failed\n"); + return rc; + } + + if (rc != 0) { + MODLOG_DFLT(ERROR, "\033[0;31mError during PHY update; " + "rc=%d\033[0m\n", rc); + } + + return rc; +} + +static int +rx_stress_8_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++rx_stress_ctx->con_stat[8].attempts_num; + + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d", + event->connect.status, + ++rx_stress_ctx->con_stat[8].num); + + /* Remember connection handler */ + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + rx_stress_ctx->conn_handle = event->connect.conn_handle; + + /* Update connection. */ + rc = rx_stress_8_con_update(); + assert(rc == 0); + } else { + /* Connection failed; resume advertising. */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason); + if (rx_stress_ctx->con_stat[8].phy_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + rx_stress_on_test_finish(8); + } else { + /* Connection terminated; resume advertising. */ + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: + if (event->phy_updated.status != 0) { + MODLOG_DFLT(INFO, "PHY update failed\n"); + } else { + MODLOG_DFLT(INFO, "PHY updated; num=%d; rx:%d, tx:%d\n", + ++rx_stress_ctx->con_stat[8].phy_upd_num, + event->phy_updated.rx_phy, event->phy_updated.tx_phy); + console_printf("\033[0;32m>\033[0m"); + } + + if (rx_stress_ctx->con_stat[8].phy_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + /* Test completed. */ + ble_gap_terminate(rx_stress_ctx->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + } else { + /* Update connection. */ + rc = rx_stress_8_con_update(); + assert(rc == 0); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +rx_stress_9_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++rx_stress_ctx->con_stat[9].attempts_num; + + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d", + event->connect.status, + ++rx_stress_ctx->con_stat[9].num); + console_printf("\033[0;32m>\033[0m"); + + /* Remember max number of established connections */ + if (rx_stress_ctx->con_stat[9].num > + rx_stress_ctx->con_stat[9].max_num) { + rx_stress_ctx->con_stat[9].max_num = rx_stress_ctx->con_stat[9].num; + } + } else { + /* Connection failed; resume advertising. */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason); + console_printf("\033[0;31mX\033[0m"); + MODLOG_DFLT(INFO, "Connections num: %d\n", + --rx_stress_ctx->con_stat[9].num); + + if (rx_stress_ctx->con_stat[9].num != 0 && + rx_stress_ctx->con_stat[9].num < + MYNEWT_VAL(BLE_MAX_CONNECTIONS) + 1) { + rx_stress_adv_start_with_rand_addr(TEST_INSTANCE); + } else { + /* When TX device has terminated all connections, stop advertising. */ + ble_gap_ext_adv_stop(TEST_INSTANCE); + rx_stress_on_test_finish(9); + } + return 0; + + case BLE_GAP_EVENT_ADV_COMPLETE: + /* Stop test when TX device has terminated all connections or + * number of connections has reached the max possible value. */ + if (rx_stress_ctx->con_stat[9].num != 0 && + rx_stress_ctx->con_stat[9].num < + MYNEWT_VAL(BLE_MAX_CONNECTIONS) + 1) { + rx_stress_adv_start_with_rand_addr(TEST_INSTANCE); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + } + return 0; +} + +static void +tx_stress_10_l2cap_update_event(uint16_t conn_handle, int status, void *arg) +{ + if (status == 0) { + MODLOG_DFLT(INFO, "L2CAP params updated\n"); + } else { + MODLOG_DFLT(INFO, "L2CAP params update failed; rc=%d\n", status); + assert(0); + } +} + +static int +rx_stress_10_l2cap_event(struct ble_l2cap_event *event, void *arg) +{ + int rc; + struct os_mbuf *data_buf; + static int data_len = 1000; + static int send_cnt = 0; + static bool stalled = false; + struct ble_l2cap_chan_info chan_info; + + switch (event->type) { + case BLE_L2CAP_EVENT_COC_CONNECTED: + if (event->connect.status) { + MODLOG_DFLT(INFO, "LE COC error: %d\n", event->connect.status); + return 0; + } + + ble_l2cap_get_chan_info(event->connect.chan, &chan_info); + + MODLOG_DFLT(INFO, + "LE COC connected, conn: %d, chan: 0x%08lx, scid: 0x%04x, " + "dcid: 0x%04x, our_mtu: 0x%04x, peer_mtu: 0x%04x\n", + event->connect.conn_handle, + (uint32_t) event->connect.chan, + chan_info.scid, + chan_info.dcid, + chan_info.our_l2cap_mtu, + chan_info.peer_l2cap_mtu); + + struct ble_l2cap_sig_update_params params = { + .itvl_min = 0x0006,//BLE_GAP_INITIAL_CONN_ITVL_MIN + .itvl_max = 0x0006,//BLE_GAP_INITIAL_CONN_ITVL_MIN + .slave_latency = 0x0000, + .timeout_multiplier = 0x0100, + }; + + rc = ble_l2cap_sig_update(event->connect.conn_handle, ¶ms, + &tx_stress_10_l2cap_update_event, NULL); + assert(rc == 0); + return 0; + + case BLE_L2CAP_EVENT_COC_DISCONNECTED: + MODLOG_DFLT(INFO, "LE CoC disconnected, chan: 0x%08lx\n", + (uint32_t) event->disconnect.chan); + return 0; + + case BLE_L2CAP_EVENT_COC_ACCEPT: + stress_l2cap_coc_accept(event->accept.peer_sdu_size, + event->accept.chan); + return 0; + + case BLE_L2CAP_EVENT_COC_DATA_RECEIVED: + stress_l2cap_coc_recv(event->receive.chan, event->receive.sdu_rx); + + MODLOG_DFLT(INFO, "L2CAP server received data; num=%d\n", + ++rx_stress_ctx->rcv_num); + rx_stress_ctx->chan = event->receive.chan; + + /* In this use case, receiving any data by RX device L2CAP server means + * request from TX device to send data. */ + + /* Do not send if stalled on the last sending. */ + if (stalled) { + return 0; + } + break; + + case BLE_L2CAP_EVENT_COC_TX_UNSTALLED: + MODLOG_DFLT(INFO, "L2CAP unstalled event\n"); + + stalled = false; + + /* Send if was stalled on the last request to send. */ + if (rx_stress_ctx->rcv_num > send_cnt) { + break; + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other L2CAP event occurs: %d\n", event->type); + return 0; + } + + /* Send pattern data */ + + /* Get mbuf for adv data */ + data_buf = os_msys_get_pkthdr(data_len, 0); + + MODLOG_DFLT(INFO, "Data buf %s\n", data_buf ? "OK" : "NOK"); + assert(data_buf != NULL); + + /* The first 2 bytes of data is the size of appended pattern data. */ + rc = os_mbuf_append(data_buf, (uint8_t[]) {data_len >> 8, data_len}, + 2); + if (rc) { + os_mbuf_free_chain(data_buf); + assert(0); + } + + /* Fill mbuf with the pattern */ + stress_fill_mbuf_with_pattern(data_buf, data_len); + + /* Send data */ + rc = ble_l2cap_send(rx_stress_ctx->chan, data_buf); + MODLOG_DFLT(INFO, "Return code=%d\n", rc); + if (rc) { + MODLOG_DFLT(INFO, "L2CAP stalled - waiting\n"); + stalled = true; + } + + MODLOG_DFLT(INFO, " %d, %d\n", ++send_cnt, data_len); + data_len += 500; + + return 0; +} + +static int +rx_stress_10_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc out_desc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++rx_stress_ctx->con_stat[10].attempts_num; + + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d", + event->connect.status, + ++rx_stress_ctx->con_stat[10].num); + + ble_gap_conn_find(event->connect.conn_handle, &out_desc); + MODLOG_DFLT(INFO, "Address %s", + addr_str(out_desc.peer_id_addr.val)); + } else { + /* Connection failed; resume advertising. */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason); + rx_stress_ctx->completed[10] = true; + rx_stress_on_test_finish(10); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +rx_stress_11_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++rx_stress_ctx->con_stat[11].attempts_num; + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n", + event->connect.status, + ++rx_stress_ctx->con_stat[11].num); + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + } + + ble_gap_terminate(event->connect.conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + console_printf("\033[0;32m>\033[0m"); + if (rx_stress_ctx->con_stat[11].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + rx_stress_on_test_finish(11); + } else { + /* Connection terminated; resume advertising. */ + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +rx_stress_12_gap_event(struct ble_gap_event *event, void *arg) +{ + int om_len = 10000; + struct os_mbuf *om; + int64_t us = 0; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n", + event->connect.status, + ++rx_stress_ctx->con_stat[12].num); + rx_stress_ctx->conn_handle = event->connect.conn_handle; + + break; + } else { + /* Connection failed; resume advertising */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + + rx_stress_ctx->s12_notif_time = rx_stress_ctx->time_sum / + rx_stress_ctx->send_num; + + MODLOG_DFLT(INFO, "Average time: %d us\n", + rx_stress_ctx->s12_notif_time); + + rx_stress_on_test_finish(12); + return 0; + + case BLE_GAP_EVENT_NOTIFY_TX: + rx_stress_ctx->end_us = os_get_uptime_usec(); + MODLOG_DFLT(INFO, "Notify TX event\n"); + + if (!event->notify_tx.status) { + /* Send next only after previous indication is done */ + return 0; + } + assert(event->notify_tx.status == BLE_HS_EDONE); + + if (rx_stress_ctx->send_num++ >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + ble_gap_terminate(event->notify_tx.conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + return 0; + } + + /* Time of data sending */ + us = rx_stress_ctx->end_us - rx_stress_ctx->begin_us; + console_printf("Indication time: %lld\n", us); + rx_stress_ctx->time_sum += us; + console_printf("\033[0;32m>\033[0m"); + break; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } + + /* Indicate data pattern */ + rx_stress_ctx->begin_us = os_get_uptime_usec(); + om = os_msys_get_pkthdr(om_len, 0); + stress_fill_mbuf_with_pattern(om, om_len); + rc = ble_gattc_indicate_custom(rx_stress_ctx->conn_handle, hrs_hrm_handle, + om); + assert(rc == 0); + return 0; +} + +static int +rx_stress_13_gap_event(struct ble_gap_event *event, void *arg) +{ + int rc; + struct os_mbuf *om = NULL; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n", + event->connect.status, + ++rx_stress_ctx->con_stat[13].num); + rx_stress_ctx->conn_handle = event->connect.conn_handle; + + rx_stress_ctx->begin_us = os_get_uptime_usec(); + break; + } else { + /* Connection failed; resume advertising */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + assert(0); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + + rx_stress_ctx->time_sum = rx_stress_ctx->end_us - + rx_stress_ctx->begin_us; + + rx_stress_ctx->s13_notif_time = rx_stress_ctx->time_sum / + rx_stress_ctx->send_num; + + MODLOG_DFLT(INFO, "Average time: %lld us\n", + rx_stress_ctx->s13_notif_time); + rx_stress_on_test_finish(13); + return 0; + + case BLE_GAP_EVENT_NOTIFY_TX: + MODLOG_DFLT(INFO, "Notify TX event; num=%d\n", + ++rx_stress_ctx->send_num); + assert(event->notify_tx.status == 0); + + if (rx_stress_ctx->send_num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + rx_stress_ctx->end_us = os_get_uptime_usec(); + ble_gap_terminate(event->connect.conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + return 0; + } + break; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } + + om = ble_hs_mbuf_from_flat(test_6_pattern, 10); + rc = ble_gattc_notify_custom(rx_stress_ctx->conn_handle, + hrs_hrm_handle, om); + assert(rc == 0); + return 0; +} + +static int +rx_stress_14_gap_event(struct ble_gap_event *event, void *arg) +{ + int bytes_num = 10000; + static struct os_mbuf *om = NULL; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n", + event->connect.status, + ++rx_stress_ctx->con_stat[14].num); + rx_stress_ctx->conn_handle = event->connect.conn_handle; + } else { + /* Connection failed; resume advertising */ + MODLOG_DFLT(INFO, "Connection failed; status=%d ", + event->connect.status); + assert(0); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + + rx_stress_ctx->s14_notif_time = rx_stress_ctx->time_sum / + rx_stress_ctx->send_num; + + MODLOG_DFLT(INFO, "Average time: %d us\n", + rx_stress_ctx->s14_notif_time); + + rx_stress_on_test_finish(14); + return 0; + + case BLE_GAP_EVENT_NOTIFY_TX: + MODLOG_DFLT(INFO, "Notify TX event\n"); + assert(event->notify_tx.status == 0); + return 0; + + case BLE_GAP_EVENT_SUBSCRIBE: + MODLOG_DFLT(INFO, "Subscribe event\n"); + + if (event->subscribe.cur_notify) { + MODLOG_DFLT(INFO, "Notification subscribed\n"); + + ++rx_stress_ctx->send_num; + + /* Notify data pattern */ + om = ble_hs_mbuf_from_flat(test_6_pattern, bytes_num); + + rc = ble_gattc_notify_custom(rx_stress_ctx->conn_handle, + hrs_hrm_handle, om); + assert(rc == 0); + + console_printf("\033[0;32m>\033[0m"); + } else if (event->subscribe.prev_notify) { + MODLOG_DFLT(INFO, "Notification unsubscribed\n"); + } else { + MODLOG_DFLT(INFO, "Other subscription\n"); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +rx_stress_15_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++rx_stress_ctx->con_stat[15].attempts_num; + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n", + event->connect.status, + ++rx_stress_ctx->con_stat[15].num); + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + assert(0); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + console_printf("\033[0;32m>\033[0m"); + if (rx_stress_ctx->con_stat[15].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + rx_stress_on_test_finish(15); + } else { + /* Connection terminated; resume advertising. */ + rx_stress_adv_start(TEST_INSTANCE); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +/* Advert settings for each test. */ +static struct rx_stress_adv_set rx_stress_adv_sets[] = { + { + .instance = SWITCHER_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[0], + .legacy_pdu = LEGACY_ADVERT, + .cb = rx_stress_0_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = SWITCHER_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[0], + .legacy_pdu = LEGACY_ADVERT, + .cb = rx_stress_0_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[2], + .legacy_pdu = LEGACY_ADVERT, + .cb = rx_stress_2_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[3], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_3_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[4], + .legacy_pdu = LEGACY_ADVERT, + .cb = rx_stress_4_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[5], + .legacy_pdu = LEGACY_ADVERT, + .cb = rx_stress_5_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[6], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_6_gap_event, + .pattern_data = test_6_pattern, + .pattern_len = 1640, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[7], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_7_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[8], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_8_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[9], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_9_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[10], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_10_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[11], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_11_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[12], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_12_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[13], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_13_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[14], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_14_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, + { + .instance = TEST_INSTANCE, + .instance_uuid128 = rx_stress_uuid128[15], + .legacy_pdu = EXTENDED_ADVERT, + .cb = rx_stress_15_gap_event, + .pattern_data = NULL, + .pattern_len = 0, + }, +}; + +static void +rx_stress_start(int test_num) +{ + int rc; + + /* Init semaphore with 0 tokens. */ + os_sem_init(&rx_stress_main_sem, 0); + + console_printf("\033[1;36mStart test num %d - ", test_num); + + /* Start test. */ + switch (test_num) { + case 2: + console_printf("Stress Connect/Disconnect legacy\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[2]); + break; + case 3: + console_printf("Stress Connect/Disconnect ext adv\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[3]); + break; + case 4: + console_printf("Stress connection params update (TX)\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[4]); + break; + case 5: + console_printf("Stress connection params update (RX)\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[5]); + break; + case 6: + /* Start SWITCHER advert that gives possibility to remotely start + * next test advert */ + console_printf("Stress Scan\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[0]); + rx_stress_simple_adv(&rx_stress_adv_sets[6]); + break; + case 7: + console_printf("Stress PHY Update (TX)\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[7]); + break; + case 8: + console_printf("Stress PHY Update (RX)\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[8]); + break; + case 9: + console_printf("Stress multi connection\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[9]); + break; + case 10: + console_printf("Stress L2CAP send\033[0m\n"); + rc = ble_l2cap_create_server(1, STRESS_COC_MTU, + rx_stress_10_l2cap_event, NULL); + assert(rc == 0); + rx_stress_simple_adv(&rx_stress_adv_sets[10]); + break; + case 11: + console_printf("Stress Advertise/Connect/Disconnect\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[11]); + break; + case 12: + console_printf("Stress GATT indication\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[12]); + break; + case 13: + console_printf("Stress GATT notification\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[13]); + break; + case 14: + console_printf("Stress GATT Subscribe/Notify/Unsubscribe\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[14]); + break; + case 15: + console_printf("Stress Connect/Send/Disconnect\033[0m\n"); + rx_stress_simple_adv(&rx_stress_adv_sets[15]); + break; + default: + console_printf("\033[0;31mFound test, but do not know how to perform." + "\033[0m\n"); + assert(0); + } + + /* Wait for the test to finish. Then 1 token will be released + * allowing to pass through semaphore. */ + os_sem_pend(&rx_stress_main_sem, OS_TIMEOUT_NEVER); + + ble_gap_ext_adv_stop(SWITCHER_INSTANCE); + + stress_clear_ctx_reusable_var(rx_stress_ctx); +} + +static void +stress_uuid_init() +{ + uint8_t i; + + for (i = 0; i < STRESS_UUIDS_NUM; ++i) { + /* Fill all 16 bytes of UUID128 */ + rx_stress_uuid128[i][0] = 0xC0; + rx_stress_uuid128[i][1] = 0xDE; + rx_stress_uuid128[i][2] = i; + memcpy(&rx_stress_uuid128[i][3], MYNEWT_VAL(BLE_STRESS_UUID_BASE), 13); + } +} + +static void +rx_stress_read_command_cb(void) +{ + console_printf("Start testing\n"); + os_sem_release(&rx_stress_main_sem); +} + +static void +rx_stress_main_task_fn(void *arg) +{ + int i; + + stress_uuid_init(); + + console_printf("\033[1;36mRX device\033[0m\n"); + console_printf("Press ENTER to start: \n"); + console_init(&rx_stress_read_command_cb); + + /* Waite for pressing ENTER in console */ + os_sem_pend(&rx_stress_main_sem, OS_TIMEOUT_NEVER); + + /* Standard tests perform */ + for (i = 11; i < STRESS_UUIDS_NUM; ++i) { + if (i == 7 || i == 8 || i == 13) { + /* 7,8: PHY update tests cause that the device during the next test + * will stuck somewhere and will reset. Skip them for now. + * 13: Should work after fixing ble_gattc_notify_custom (nimble issue on GitHub)*/ + continue; + } + /* Start test. */ + rx_stress_start(i); + } + + /* Print tests results */ + com_stress_print_report(rx_stress_ctx); + + /* Task should never return */ + while (1) { + } +} + +void +rx_stress_start_auto() +{ + /* Start task that will run all stress tests one by one. */ + os_task_init(&rx_stress_main_task, "rx_stress_main_task", + rx_stress_main_task_fn, NULL, RX_STRESS_MAIN_TASK_PRIO, + OS_WAIT_FOREVER, rx_stress_main_task_stack, + RX_STRESS_MAIN_TASK_STACK_SIZE); +} diff --git a/src/libs/mynewt-nimble/apps/blestress/src/rx_stress.h b/src/libs/mynewt-nimble/apps/blestress/src/rx_stress.h new file mode 100644 index 00000000..62f84117 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blestress/src/rx_stress.h @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef _BLE_STRESS_RX_H +#define _BLE_STRESS_RX_H + +#include +#include +#include +#include +#include + +/* BLE */ +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "services/gap/ble_svc_gap.h" +#include "host/ble_gap.h" +#include + +#include "misc.h" +#include "stress.h" +#include "stress_gatt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Executes stress tests one by one. + */ +void rx_stress_start_auto(); + +#ifdef __cplusplus +} +#endif + +#endif //_BLE_STRESS_RX_H diff --git a/src/libs/mynewt-nimble/apps/blestress/src/stress.c b/src/libs/mynewt-nimble/apps/blestress/src/stress.c new file mode 100644 index 00000000..6f5badf0 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blestress/src/stress.c @@ -0,0 +1,389 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "stress.h" + +void +com_stress_print_report(const struct com_stress_test_ctx *test_ctxs) +{ + console_printf("\033[0;32mAll tests completed\033[0m\n"); + console_printf("Tests results:\n"); + + console_printf( + "\033[0;33mUse case 1 - Stress Connect -> Connect Cancel: \n\033[0m"); + console_printf("Con attempts = %d\n", test_ctxs->con_stat[1].attempts_num); + console_printf("Con success = %d\n", test_ctxs->con_stat[1].num); + + console_printf( + "\033[0;33mUse case 2 - Stress Connect/Disconnect legacy: \n\033[0m"); + console_printf("Con attempts = %d\n", test_ctxs->con_stat[2].attempts_num); + console_printf("Con success = %d\n", test_ctxs->con_stat[2].num); + + console_printf( + "\033[0;33mUse case 3 - Stress Connect/Disconnect ext adv: \n\033[0m"); + console_printf("Con attempts = %d\n", test_ctxs->con_stat[3].attempts_num); + console_printf("Con success = %d\n", test_ctxs->con_stat[3].num); + + console_printf( + "\033[0;33mUse case 4 - Stress connection params update (TX): \n\033[0m"); + console_printf("Params updates = %d\n", + test_ctxs->con_stat[4].prms_upd_num); + + console_printf( + "\033[0;33mUse case 5 - Stress connection params update (RX): \n\033[0m"); + console_printf("Params updates = %d\n", + test_ctxs->con_stat[5].prms_upd_num); + + console_printf("\033[0;33mUse case 6 - Stress Scan: \n\033[0m"); + console_printf("Received first packets = %d\n", + test_ctxs->s6_rcv_adv_first); + console_printf("Received all packets = %d\n", test_ctxs->s6_rcv_adv_suc); + + console_printf("\033[0;33mUse case 7 - Stress PHY Update (TX): \n\033[0m"); + console_printf("PHY updates = %d\n", test_ctxs->con_stat[7].phy_upd_num); + + console_printf("\033[0;33mUse case 8 - Stress PHY Update (RX): \n\033[0m"); + console_printf("PHY updates = %d\n", test_ctxs->con_stat[8].phy_upd_num); + + console_printf( + "\033[0;33mUse case 9 - Stress multi connection: \n\033[0m"); + console_printf("Max reached num of connections = %d\n", + test_ctxs->con_stat[9].max_num); + + console_printf("\033[0;33mUse case 10 - Stress L2CAP send: \n\033[0m"); + console_printf("Average bit rate = %d\n", test_ctxs->s10_bit_rate); + console_printf("Max received MTU = %lld\n", test_ctxs->s10_max_mtu); + + console_printf("\033[0;33mUse case 11 - " + "Stress Advertise/Connect/Continue adv \n\033[0m"); +// console_printf(" = %d\n",); + + console_printf("\033[0;33mUse case 12 - " + "Stress GATT indication: \n\033[0m"); + console_printf("Average bit rate = %d\n", test_ctxs->s12_notif_time); + + console_printf("\033[0;33mUse case 13 - " + "Stress GATT notification: \n\033[0m"); + console_printf("Average time = %d\n", test_ctxs->s13_notif_time); + + console_printf("\033[0;33mUse case 14 - " + "Stress GATT Subscribe/Notify/Unsubscribe: \n\033[0m"); + console_printf("Average time = %d\n", test_ctxs->s14_notif_time); + + console_printf("\033[0;33mUse case 15 - " + "Stress Connect/Send/Disconnect: \n\033[0m"); + console_printf("Con num = %d\n", test_ctxs->con_stat[15].num); +} + +void +stress_clear_ctx_reusable_var(struct com_stress_test_ctx *ctx) +{ + ctx->cur_test_id = 0; + ctx->dev_addr.type = 0; + ctx->dev_addr.type = 0; + ctx->conn_handle = 0; + ctx->chan = 0; + ctx->rcv_data_bytes = 0; + ctx->rcv_num = 0; + ctx->send_num = 0; + ctx->begin_us = 0; + ctx->end_us = 0; + ctx->time_sum = 0; + ctx->bytes_sum = 0; + ctx->timeout_flag = 0; + ctx->rcv_data_flag = 0; +} + +int +stress_fill_mbuf_with_pattern(struct os_mbuf *om, uint16_t len) +{ + int rc, i, mul, rest; + + mul = len / STRESS_PAT_LEN; + rest = len % STRESS_PAT_LEN; + + for (i = 0; i < mul; ++i) { + rc = os_mbuf_append(om, &test_6_pattern[29], STRESS_PAT_LEN); + + if (rc) { + os_mbuf_free_chain(om); + assert(0); + } + } + + rc = os_mbuf_append(om, &test_6_pattern[29], rest); + + if (rc) { + os_mbuf_free_chain(om); + assert(0); + } + + return rc; +} + +void +stress_l2cap_coc_recv(struct ble_l2cap_chan *chan, struct os_mbuf *sdu) +{ + int rc; + console_printf("LE CoC SDU received, chan: 0x%08lx, data len %d\n", + (uint32_t) chan, OS_MBUF_PKTLEN(sdu)); + + rc = os_mbuf_free_chain(sdu); + assert(rc == 0); + + /* Get buffer for data chain */ + sdu = os_msys_get_pkthdr(STRESS_COC_MTU, 0); + assert(sdu != NULL); + + /* Receive data chain */ + rc = ble_l2cap_recv_ready(chan, sdu); + assert(rc == 0); +} + +void +stress_l2cap_coc_accept(uint16_t peer_mtu, struct ble_l2cap_chan *chan) +{ + struct os_mbuf *sdu_rx; + int rc; + + console_printf("LE CoC accepting, chan: 0x%08lx, peer_mtu %d\n", + (uint32_t) chan, peer_mtu); + + sdu_rx = os_msys_get_pkthdr(STRESS_COC_MTU, 0); + assert(sdu_rx != NULL); + + rc = ble_l2cap_recv_ready(chan, sdu_rx); + assert(rc == 0); +} + +void +stress_start_timer(uint32_t timeout_ms, os_event_fn *ev_cb) +{ + int rc; + os_callout_stop(&stress_timer_callout); + + os_callout_init(&stress_timer_callout, os_eventq_dflt_get(), ev_cb, NULL); + + rc = os_callout_reset(&stress_timer_callout, + os_time_ms_to_ticks32(timeout_ms)); + + assert(rc == 0); +} + +int64_t +stress_calc_bit_rate(int64_t us, int64_t bytes_num) +{ + int bit_rate; + + /* Multiply by 1000000 so you don't lose accuracy */ + bit_rate = 1000000 * bytes_num / us; + + return bit_rate; +} + +static int +stress_disc_dsc_fn(uint16_t conn_handle, + const struct ble_gatt_error *error, + uint16_t chr_val_handle, + const struct ble_gatt_dsc *dsc, + void *arg) +{ + struct stress_gatt_search_ctx *search_ctx; + static bool found = false; + + search_ctx = (struct stress_gatt_search_ctx *) arg; + + if (error->status == 0) { + if (!ble_uuid_cmp(&dsc->uuid.u, &search_ctx->dsc_uuid.u) && !found) { + MODLOG_DFLT(INFO, "Found chr descriptor\n"); + search_ctx->dsc_handle = dsc->handle; + MODLOG_DFLT(INFO, "uuid=%#06x; handle=%#06x", dsc->uuid.u16.value, + dsc->handle); + found = true; + } + return 0; + } + + if (error->status == BLE_HS_EDONE) { + MODLOG_DFLT(INFO, "Done descriptor discovery\n"); + + if (found) { + found = false; + search_ctx->disc_end_fn(search_ctx); + return 0; + } + + MODLOG_DFLT(ERROR, "\033[0;31mDid not find particular descriptor" + "\033[0m\n"); + return 0; + } + + MODLOG_DFLT(ERROR, "\033[0;31mError during descriptor discovery" + "\033[0m\n"); + assert(0); + return 0; +} + +static int +stress_disc_chr_fn(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_chr *chr, void *arg) +{ + int rc; + struct stress_gatt_search_ctx *search_ctx; + static bool found = false; + + search_ctx = (struct stress_gatt_search_ctx *) arg; + + if (error->status == 0) { + MODLOG_DFLT(INFO, "Found characteristic\n"); + search_ctx->chr_start_handle = chr->val_handle; + found = true; + return 0; + } + + if (error->status == BLE_HS_EDONE) { + MODLOG_DFLT(INFO, "Done characteristic discovery\n"); + + if (found) { + found = false; + + if (search_ctx->search_goal == STRESS_FIND_CHR) { + search_ctx->disc_end_fn(search_ctx); + return 0; + } + + rc = ble_gattc_disc_all_dscs(conn_handle, + search_ctx->chr_start_handle, + search_ctx->srv_end_handle, + &stress_disc_dsc_fn, search_ctx); + assert(rc == 0); + return 0; + } + + MODLOG_DFLT(ERROR, "\033[0;31mDid not find particular " + "characteristic\033[0m\n"); + return 0; + } + + MODLOG_DFLT(ERROR, + "\033[0;31mError during characteristic discovery\033[0m\n"); + assert(0); + return 0; +} + +static int +stress_disc_svc_fn(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_svc *service, void *arg) +{ + int rc; + struct stress_gatt_search_ctx *search_ctx = NULL; + static bool found = false; + + search_ctx = (struct stress_gatt_search_ctx *) arg; + + if (error->status == 0) { + MODLOG_DFLT(INFO, "Found service\n"); + search_ctx->srv_start_handle = service->start_handle; + search_ctx->srv_end_handle = service->end_handle; + found = true; + return 0; + } + + if (error->status == BLE_HS_EDONE) { + MODLOG_DFLT(INFO, "Done service discovery\n"); + + if (found) { + found = false; + if (search_ctx->search_goal == STRESS_FIND_SRV) { + search_ctx->disc_end_fn(search_ctx); + return 0; + } + + rc = ble_gattc_disc_chrs_by_uuid(conn_handle, + search_ctx->srv_start_handle, + search_ctx->srv_end_handle, + &search_ctx->chr_uuid.u, + &stress_disc_chr_fn, + search_ctx); + MODLOG_DFLT(INFO, "rc=%d\n", rc); + assert(rc == 0); + return 0; + } + + MODLOG_DFLT(ERROR, + "\033[0;31mDid not find particular service\033[0m\n"); + return 0; + } + + MODLOG_DFLT(ERROR, "\033[0;31mError during service discovery\033[0m\n"); + assert(0); + return 0; +} + +static void +stress_gatt_find_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid, + const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid, + stress_gatt_disc_end_fn *disc_end_fn, + uint8_t search_goal) +{ + static struct stress_gatt_search_ctx search_ctx; + int rc; + + search_ctx.conn_handle = conn_handle; + ble_uuid_copy((ble_uuid_any_t *) &search_ctx.srv_uuid, srv_uuid); + ble_uuid_copy((ble_uuid_any_t *) &search_ctx.chr_uuid, chr_uuid); + ble_uuid_copy((ble_uuid_any_t *) &search_ctx.dsc_uuid, dsc_uuid); + search_ctx.disc_end_fn = disc_end_fn; + search_ctx.search_goal = search_goal; + search_ctx.srv_start_handle = 0; + search_ctx.srv_end_handle = 0; + search_ctx.chr_start_handle = 0; + search_ctx.dsc_handle = 0; + + rc = ble_gattc_disc_svc_by_uuid(conn_handle, srv_uuid, &stress_disc_svc_fn, + &search_ctx); + assert(rc == 0); +} + +void +stress_find_svc_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid, + stress_gatt_disc_end_fn *disc_end_fn) +{ + stress_gatt_find_handle(conn_handle, srv_uuid, NULL, NULL, disc_end_fn, + STRESS_FIND_SRV); +} + +void +stress_find_chr_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid, + const ble_uuid_t *chr_uuid, + stress_gatt_disc_end_fn *disc_end_fn) +{ + stress_gatt_find_handle(conn_handle, srv_uuid, chr_uuid, NULL, disc_end_fn, + STRESS_FIND_CHR); +} + +void +stress_find_dsc_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid, + const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid, + stress_gatt_disc_end_fn *disc_end_fn) +{ + stress_gatt_find_handle(conn_handle, srv_uuid, chr_uuid, dsc_uuid, + disc_end_fn, STRESS_FIND_DSC); +} diff --git a/src/libs/mynewt-nimble/apps/blestress/src/stress.h b/src/libs/mynewt-nimble/apps/blestress/src/stress.h new file mode 100644 index 00000000..91ab4f47 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blestress/src/stress.h @@ -0,0 +1,375 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef BLE_TGT_STRESS_H +#define BLE_TGT_STRESS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#define STRESS_UUIDS_NUM (16) + +/* No preferred PHY */ +#define TX_PHY_MASK 0 +#define RX_PHY_MASK 0 +/* L2CAP SDU */ +#define STRESS_COC_MTU (64000) + +#define STRESS_FIND_SRV 1 +#define STRESS_FIND_CHR 2 +#define STRESS_FIND_DSC 3 + +struct os_callout stress_timer_callout; +struct stress_gatt_search_ctx; +typedef void stress_gatt_disc_end_fn(struct stress_gatt_search_ctx *search_ctx); + +struct stress_gatt_search_ctx { + /* Connection handle */ + uint16_t conn_handle; + + /* Wanted service uuid */ + ble_uuid16_t srv_uuid; + + /* Wanted characteristic uuid */ + ble_uuid16_t chr_uuid; + + /* Wanted descriptor uuid */ + ble_uuid16_t dsc_uuid; + + /* Search goal: 1 - service, 2 - characteristic, 3 - descriptor */ + uint8_t search_goal; + + /* Callback after reaching the goal */ + stress_gatt_disc_end_fn *disc_end_fn; + + /* Service start handle */ + uint16_t srv_start_handle; + + /* Service end handle */ + uint16_t srv_end_handle; + + /* Characteristic start handle */ + uint16_t chr_start_handle; + + /* Descriptor handle */ + uint16_t dsc_handle; +}; + +struct stress_con_stat { + /* Number of successful connection attempts */ + int num; + + /* Max number of established connections */ + int max_num; + + /* Number connection attempts */ + int attempts_num; + + /* Number of params updates */ + int prms_upd_num; + + /* Number of PHY updates */ + int phy_upd_num; +}; + +/* Common stress test context. + * (Reusable) - auxiliary variable + * (Stress x) - variable contains result of x stress test */ +struct com_stress_test_ctx { + /* Which of tests are completed already. Each element for different + * stress test. */ + bool completed[STRESS_UUIDS_NUM]; + + /* Connection stats. Each element for different stress test. */ + struct stress_con_stat con_stat[STRESS_UUIDS_NUM]; + +/* Reusable variables */ + + /* Stress test number */ + int cur_test_id; + + /* Instance address */ + ble_addr_t dev_addr; + + /* Connection handler */ + uint16_t conn_handle; + + /* L2CAP channel (Reusable) */ + struct ble_l2cap_chan * chan; + + /* Size of received data */ + int64_t rcv_data_bytes; + + /* Number of received packages */ + int rcv_num; + + /* Number of send actions */ + int send_num; + + /* Variables for bit rate measurement */ + int64_t begin_us; + int64_t end_us; + int64_t time_sum; + int64_t bytes_sum; + + /* Timeout flag */ + bool timeout_flag; + + /* Data received flag */ + bool rcv_data_flag; + + uint16_t start_handle; + uint16_t end_handle; + uint16_t dsc_handle; + +/* Variables for test results */ + /* Reached timeout of scanning for test */ + bool scan_timeout; + + /* Number of received first packets of adverts */ + int s6_rcv_adv_first; + + /* Number of full received adverts */ + int s6_rcv_adv_suc; + + /* L2CAP Send Bit Rate */ + int s10_bit_rate; + + /* Average indication time */ + int s12_notif_time; + + /* Average notification time */ + int s13_notif_time; + + /* Average notification time */ + int s14_notif_time; + + /* Max size of received MTU */ + int64_t s10_max_mtu; +}; + +#define STRESS_PAT_LEN 1650 + +static const uint8_t test_6_pattern[STRESS_PAT_LEN] = { + /* Random data */ + 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x08, 0x00, 0x0a, + 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, + 0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1e, + 0x00, 0x20, 0x00, 0x22, 0x00, 0x24, 0x00, 0x26, 0x00, 0x28, + 0x00, 0x2a, 0x00, 0x2c, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x32, + 0x00, 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, + 0x00, 0x3e, 0x00, 0x40, 0x00, 0x42, 0x00, 0x44, 0x00, 0x46, + 0x00, 0x48, 0x00, 0x4a, 0x00, 0x4c, 0x00, 0x4e, 0x00, 0x50, + 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, 0x00, 0x58, 0x00, 0x5a, + 0x00, 0x5c, 0x00, 0x5e, 0x00, 0x60, 0x00, 0x62, 0x00, 0x64, + 0x00, 0x66, 0x00, 0x68, 0x00, 0x6a, 0x00, 0x6c, 0x00, 0x6e, + 0x00, 0x70, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x78, + 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7e, 0x00, 0x80, 0x00, 0x82, + 0x00, 0x84, 0x00, 0x86, 0x00, 0x88, 0x00, 0x8a, 0x00, 0x8c, + 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, + 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, 0x9e, 0x00, 0xa0, + 0x00, 0xa2, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, + 0x00, 0xac, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, + 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbc, 0x00, 0xbe, + 0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc8, + 0x00, 0xca, 0x00, 0xcc, 0x00, 0xce, 0x00, 0xd0, 0x00, 0xd2, + 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd8, 0x00, 0xda, 0x00, 0xdc, + 0x00, 0xde, 0x00, 0xe0, 0x00, 0xe2, 0x00, 0xe4, 0x00, 0xe6, + 0x00, 0xe8, 0x00, 0xea, 0x00, 0xec, 0x00, 0xee, 0x00, 0xf0, + 0x00, 0xf2, 0x00, 0xf4, 0x00, 0xf6, 0x00, 0xf8, 0x00, 0xfa, + 0x00, 0xfc, 0x00, 0xfe, 0x01, 0x01, 0x01, 0x03, 0x01, 0x05, + 0x01, 0x07, 0x01, 0x09, 0x01, 0x0b, 0x01, 0x0d, 0x01, 0x0f, + 0x01, 0x11, 0x01, 0x13, 0x01, 0x15, 0x01, 0x17, 0x01, 0x19, + 0x01, 0x1b, 0x01, 0x1d, 0x01, 0x1f, 0x01, 0x21, 0x01, 0x23, + 0x01, 0x25, 0x01, 0x27, 0x01, 0x29, 0x01, 0x2b, 0x01, 0x2d, + 0x01, 0x2f, 0x01, 0x31, 0x01, 0x33, 0x01, 0x35, 0x01, 0x37, + 0x01, 0x39, 0x01, 0x3b, 0x01, 0x3d, 0x01, 0x3f, 0x01, 0x41, + 0x01, 0x43, 0x01, 0x45, 0x01, 0x47, 0x01, 0x49, 0x01, 0x4b, + 0x01, 0x4d, 0x01, 0x4f, 0x01, 0x51, 0x01, 0x53, 0x01, 0x55, + 0x01, 0x57, 0x01, 0x59, 0x01, 0x5b, 0x01, 0x5d, 0x01, 0x5f, + 0x01, 0x61, 0x01, 0x63, 0x01, 0x65, 0x01, 0x67, 0x01, 0x69, + 0x01, 0x6b, 0x01, 0x6d, 0x01, 0x6f, 0x01, 0x71, 0x01, 0x73, + 0x01, 0x75, 0x01, 0x77, 0x01, 0x79, 0x01, 0x7b, 0x01, 0x7d, + 0x01, 0x7f, 0x01, 0x81, 0x01, 0x83, 0x01, 0x85, 0x01, 0x87, + 0x01, 0x89, 0x01, 0x8b, 0x01, 0x8d, 0x01, 0x8f, 0x01, 0x91, + 0x01, 0x93, 0x01, 0x95, 0x01, 0x97, 0x01, 0x99, 0x01, 0x9b, + 0x01, 0x9d, 0x01, 0x9f, 0x01, 0xa1, 0x01, 0xa3, 0x01, 0xa5, + 0x01, 0xa7, 0x01, 0xa9, 0x01, 0xab, 0x01, 0xad, 0x01, 0xaf, + 0x01, 0xb1, 0x01, 0xb3, 0x01, 0xb5, 0x01, 0xb7, 0x01, 0xb9, + 0x01, 0xbb, 0x01, 0xbd, 0x01, 0xbf, 0x01, 0xc1, 0x01, 0xc3, + 0x01, 0xc5, 0x01, 0xc7, 0x01, 0xc9, 0x01, 0xcb, 0x01, 0xcd, + 0x01, 0xcf, 0x01, 0xd1, 0x01, 0xd3, 0x01, 0xd5, 0x01, 0xd7, + 0x01, 0xd9, 0x01, 0xdb, 0x01, 0xdd, 0x01, 0xdf, 0x01, 0xe1, + 0x01, 0xe3, 0x01, 0xe5, 0x01, 0xe7, 0x01, 0xe9, 0x01, 0xeb, + 0x01, 0xed, 0x01, 0xef, 0x01, 0xf1, 0x01, 0xf3, 0x01, 0xf5, + 0x01, 0xf7, 0x01, 0xf9, 0x01, 0xfb, 0x01, 0xfd, 0x02, 0x00, + 0x02, 0x02, 0x02, 0x04, 0x02, 0x06, 0x02, 0x08, 0x02, 0x0a, + 0x02, 0x0c, 0x02, 0x0e, 0x02, 0x10, 0x02, 0x12, 0x02, 0x14, + 0x02, 0x16, 0x02, 0x18, 0x02, 0x1a, 0x02, 0x1c, 0x02, 0x1e, + 0x02, 0x20, 0x02, 0x22, 0x02, 0x24, 0x02, 0x26, 0x02, 0x28, + 0x02, 0x2a, 0x02, 0x2c, 0x02, 0x2e, 0x02, 0x30, 0x02, 0x32, + 0x02, 0x34, 0x02, 0x36, 0x02, 0x38, 0x02, 0x3a, 0x02, 0x3c, + 0x02, 0x3e, 0x02, 0x40, 0x02, 0x42, 0x02, 0x44, 0x02, 0x46, + 0x02, 0x48, 0x02, 0x4a, 0x02, 0x4c, 0x02, 0x4e, 0x02, 0x50, + 0x02, 0x52, 0x02, 0x54, 0x02, 0x56, 0x02, 0x58, 0x02, 0x5a, + 0x02, 0x5c, 0x02, 0x5e, 0x02, 0x60, 0x02, 0x62, 0x02, 0x64, + 0x02, 0x66, 0x02, 0x68, 0x02, 0x6a, 0x02, 0x6c, 0x02, 0x6e, + 0x02, 0x70, 0x02, 0x72, 0x02, 0x74, 0x02, 0x76, 0x02, 0x78, + 0x02, 0x7a, 0x02, 0x7c, 0x02, 0x7e, 0x02, 0x80, 0x02, 0x82, + 0x02, 0x84, 0x02, 0x86, 0x02, 0x88, 0x02, 0x8a, 0x02, 0x8c, + 0x02, 0x8e, 0x02, 0x90, 0x02, 0x92, 0x02, 0x94, 0x02, 0x96, + 0x02, 0x98, 0x02, 0x9a, 0x02, 0x9c, 0x02, 0x9e, 0x02, 0xa0, + 0x02, 0xa2, 0x02, 0xa4, 0x02, 0xa6, 0x02, 0xa8, 0x02, 0xaa, + 0x02, 0xac, 0x02, 0xae, 0x02, 0xb0, 0x02, 0xb2, 0x02, 0xb4, + 0x02, 0xb6, 0x02, 0xb8, 0x02, 0xba, 0x02, 0xbc, 0x02, 0xbe, + 0x02, 0xc0, 0x02, 0xc2, 0x02, 0xc4, 0x02, 0xc6, 0x02, 0xc8, + 0x02, 0xca, 0x02, 0xcc, 0x02, 0xce, 0x02, 0xd0, 0x02, 0xd2, + 0x02, 0xd4, 0x02, 0xd6, 0x02, 0xd8, 0x02, 0xda, 0x02, 0xdc, + 0x02, 0xde, 0x02, 0xe0, 0x02, 0xe2, 0x02, 0xe4, 0x02, 0xe6, + 0x02, 0xe8, 0x02, 0xea, 0x02, 0xec, 0x02, 0xee, 0x02, 0xf0, + 0x02, 0xf2, 0x02, 0xf4, 0x02, 0xf6, 0x02, 0xf8, 0x02, 0xfa, + 0x02, 0xfc, 0x02, 0xfe, 0x03, 0x01, 0x03, 0x03, 0x03, 0x05, + 0x03, 0x07, 0x03, 0x09, 0x03, 0x0b, 0x03, 0x0d, 0x03, 0x0f, + 0x03, 0x11, 0x03, 0x13, 0x03, 0x15, 0x03, 0x17, 0x03, 0x19, + 0x03, 0x1b, 0x03, 0x1d, 0x03, 0x1f, 0x03, 0x21, 0x03, 0x23, + 0x03, 0x25, 0x03, 0x27, 0x03, 0x29, 0x03, 0x2b, 0x03, 0x2d, + 0x03, 0x2f, 0x03, 0x31, 0x03, 0x33, 0x03, 0x35, 0x03, 0x37, + 0x03, 0x39, 0x03, 0x3b, 0x03, 0x3d, 0x03, 0x3f, 0x03, 0x41, + 0x03, 0x43, 0x03, 0x45, 0x03, 0x47, 0x03, 0x49, 0x03, 0x4b, + 0x03, 0x4d, 0x03, 0x4f, 0x03, 0x51, 0x03, 0x53, 0x03, 0x55, + 0x03, 0x57, 0x03, 0x59, 0x03, 0x5b, 0x03, 0x5d, 0x03, 0x5f, + 0x03, 0x61, 0x03, 0x63, 0x03, 0x65, 0x03, 0x67, 0x03, 0x69, + 0x03, 0x6b, 0x03, 0x6d, 0x03, 0x6f, 0x03, 0x71, 0x03, 0x73, + 0x03, 0x75, 0x03, 0x77, 0x03, 0x79, 0x03, 0x7b, 0x03, 0x7d, + 0x03, 0x7f, 0x03, 0x81, 0x03, 0x83, 0x03, 0x85, 0x03, 0x87, + 0x03, 0x89, 0x03, 0x8b, 0x03, 0x8d, 0x03, 0x8f, 0x03, 0x91, + 0x03, 0x93, 0x03, 0x95, 0x03, 0x97, 0x03, 0x99, 0x03, 0x9b, + 0x03, 0x9d, 0x03, 0x9f, 0x03, 0xa1, 0x03, 0xa3, 0x03, 0xa5, + 0x03, 0xa7, 0x03, 0xa9, 0x03, 0xab, 0x03, 0xad, 0x03, 0xaf, + 0x03, 0xb1, 0x03, 0xb3, 0x03, 0xb5, 0x03, 0xb7, 0x03, 0xb9, + 0x03, 0xbb, 0x03, 0xbd, 0x03, 0xbf, 0x03, 0xc1, 0x03, 0xc3, + 0x03, 0xc5, 0x03, 0xc7, 0x03, 0xc9, 0x03, 0xcb, 0x03, 0xcd, + 0x03, 0xcf, 0x03, 0xd1, 0x03, 0xd3, 0x03, 0xd5, 0x03, 0xd7, + 0x03, 0xd9, 0x03, 0xdb, 0x03, 0xdd, 0x03, 0xdf, 0x03, 0xe1, + 0x03, 0xe3, 0x03, 0xe5, 0x03, 0xe7, 0x03, 0xe9, 0x03, 0xeb, + 0x03, 0xed, 0x03, 0xef, 0x03, 0xf1, 0x03, 0xf3, 0x03, 0xf5, + 0x03, 0xf7, 0x03, 0xf9, 0x03, 0xfb, 0x03, 0xfd, 0x04, 0x00, + 0x04, 0x02, 0x04, 0x04, 0x04, 0x06, 0x04, 0x08, 0x04, 0x0a, + 0x04, 0x0c, 0x04, 0x0e, 0x04, 0x10, 0x04, 0x12, 0x04, 0x14, + 0x04, 0x16, 0x04, 0x18, 0x04, 0x1a, 0x04, 0x1c, 0x04, 0x1e, + 0x04, 0x20, 0x04, 0x22, 0x04, 0x24, 0x04, 0x26, 0x04, 0x28, + 0x04, 0x2a, 0x04, 0x2c, 0x04, 0x2e, 0x04, 0x30, 0x04, 0x32, + 0x04, 0x34, 0x04, 0x36, 0x04, 0x38, 0x04, 0x3a, 0x04, 0x3c, + 0x04, 0x3e, 0x04, 0x40, 0x04, 0x42, 0x04, 0x44, 0x04, 0x46, + 0x04, 0x48, 0x04, 0x4a, 0x04, 0x4c, 0x04, 0x4e, 0x04, 0x50, + 0x04, 0x52, 0x04, 0x54, 0x04, 0x56, 0x04, 0x58, 0x04, 0x5a, + 0x04, 0x5c, 0x04, 0x5e, 0x04, 0x60, 0x04, 0x62, 0x04, 0x64, + 0x04, 0x66, 0x04, 0x68, 0x04, 0x6a, 0x04, 0x6c, 0x04, 0x6e, + 0x04, 0x70, 0x04, 0x72, 0x04, 0x74, 0x04, 0x76, 0x04, 0x78, + 0x04, 0x7a, 0x04, 0x7c, 0x04, 0x7e, 0x04, 0x80, 0x04, 0x82, + 0x04, 0x84, 0x04, 0x86, 0x04, 0x88, 0x04, 0x8a, 0x04, 0x8c, + 0x04, 0x8e, 0x04, 0x90, 0x04, 0x92, 0x04, 0x94, 0x04, 0x96, + 0x04, 0x98, 0x04, 0x9a, 0x04, 0x9c, 0x04, 0x9e, 0x04, 0xa0, + 0x04, 0xa2, 0x04, 0xa4, 0x04, 0xa6, 0x04, 0xa8, 0x04, 0xaa, + 0x04, 0xac, 0x04, 0xae, 0x04, 0xb0, 0x04, 0xb2, 0x04, 0xb4, + 0x04, 0xb6, 0x04, 0xb8, 0x04, 0xba, 0x04, 0xbc, 0x04, 0xbe, + 0x04, 0xc0, 0x04, 0xc2, 0x04, 0xc4, 0x04, 0xc6, 0x04, 0xc8, + 0x04, 0xca, 0x04, 0xcc, 0x04, 0xce, 0x04, 0xd0, 0x04, 0xd2, + 0x04, 0xd4, 0x04, 0xd6, 0x04, 0xd8, 0x04, 0xda, 0x04, 0xdc, + 0x04, 0xde, 0x04, 0xe0, 0x04, 0xe2, 0x04, 0xe4, 0x04, 0xe6, + 0x04, 0xe8, 0x04, 0xea, 0x04, 0xec, 0x04, 0xee, 0x04, 0xf0, + 0x04, 0xf2, 0x04, 0xf4, 0x04, 0xf6, 0x04, 0xf8, 0x04, 0xfa, + 0x04, 0xfc, 0x04, 0xfe, 0x05, 0x01, 0x05, 0x03, 0x05, 0x05, + 0x05, 0x07, 0x05, 0x09, 0x05, 0x0b, 0x05, 0x0d, 0x05, 0x0f, + 0x05, 0x11, 0x05, 0x13, 0x05, 0x15, 0x05, 0x17, 0x05, 0x19, + 0x05, 0x1b, 0x05, 0x1d, 0x05, 0x1f, 0x05, 0x21, 0x05, 0x23, + 0x05, 0x25, 0x05, 0x27, 0x05, 0x29, 0x05, 0x2b, 0x05, 0x2d, + 0x05, 0x2f, 0x05, 0x31, 0x05, 0x33, 0x05, 0x35, 0x05, 0x37, + 0x05, 0x39, 0x05, 0x3b, 0x05, 0x3d, 0x05, 0x3f, 0x05, 0x41, + 0x05, 0x43, 0x05, 0x45, 0x05, 0x47, 0x05, 0x49, 0x05, 0x4b, + 0x05, 0x4d, 0x05, 0x4f, 0x05, 0x51, 0x05, 0x53, 0x05, 0x55, + 0x05, 0x57, 0x05, 0x59, 0x05, 0x5b, 0x05, 0x5d, 0x05, 0x5f, + 0x05, 0x61, 0x05, 0x63, 0x05, 0x65, 0x05, 0x67, 0x05, 0x69, + 0x05, 0x6b, 0x05, 0x6d, 0x05, 0x6f, 0x05, 0x71, 0x05, 0x73, + 0x05, 0x75, 0x05, 0x77, 0x05, 0x79, 0x05, 0x7b, 0x05, 0x7d, + 0x05, 0x7f, 0x05, 0x81, 0x05, 0x83, 0x05, 0x85, 0x05, 0x87, + 0x05, 0x89, 0x05, 0x8b, 0x05, 0x8d, 0x05, 0x8f, 0x05, 0x91, + 0x05, 0x93, 0x05, 0x95, 0x05, 0x97, 0x05, 0x99, 0x05, 0x9b, + 0x05, 0x9d, 0x05, 0x9f, 0x05, 0xa1, 0x05, 0xa3, 0x05, 0xa5, + 0x05, 0xa7, 0x05, 0xa9, 0x05, 0xab, 0x05, 0xad, 0x05, 0xaf, + 0x05, 0xb1, 0x05, 0xb3, 0x05, 0xb5, 0x05, 0xb7, 0x05, 0xb9, + 0x05, 0xbb, 0x05, 0xbd, 0x05, 0xbf, 0x05, 0xc1, 0x05, 0xc3, + 0x05, 0xc5, 0x05, 0xc7, 0x05, 0xc9, 0x05, 0xcb, 0x05, 0xcd, + 0x05, 0xcf, 0x05, 0xd1, 0x05, 0xd3, 0x05, 0xd5, 0x05, 0xd7, + 0x05, 0xd9, 0x05, 0xdb, 0x05, 0xdd, 0x05, 0xdf, 0x05, 0xe1, + 0x05, 0xe3, 0x05, 0xe5, 0x05, 0xe7, 0x05, 0xe9, 0x05, 0xeb, + 0x05, 0xed, 0x05, 0xef, 0x05, 0xf1, 0x05, 0xf3, 0x05, 0xf5, + 0x05, 0xf7, 0x05, 0xf9, 0x05, 0xfb, 0x05, 0xfd, 0x06, 0x00, + 0x06, 0x02, 0x06, 0x04, 0x06, 0x06, 0x06, 0x08, 0x06, 0x0a, + 0x06, 0x0c, 0x06, 0x0e, 0x06, 0x10, 0x06, 0x12, 0x06, 0x14, + 0x06, 0x16, 0x06, 0x18, 0x06, 0x1a, 0x06, 0x1c, 0x06, 0x1e, + 0x06, 0x20, 0x06, 0x22, 0x06, 0x24, 0x06, 0x26, 0x06, 0x28, + 0x06, 0x2a, 0x06, 0x2c, 0x06, 0x2e, 0x06, 0x30, 0x06, 0x32, + 0x06, 0x34, 0x06, 0x36, 0x06, 0x38, 0x06, 0x3a, 0x06, 0x3c, + 0x06, 0x3e, 0x06, 0x40, 0x06, 0x42, 0x06, 0x44, 0x06, 0x46, + 0x06, 0x48, 0x06, 0x4a, 0x06, 0x4c, 0x06, 0x4e, 0x06, 0x50, + 0x06, 0x52, 0x06, 0x54, 0x06, 0x56, 0x06, 0x58, 0x06, 0x5a, + 0x06, 0x5c, 0x06, 0x5e, 0x06, 0x60, 0x06, 0x62, 0x06, 0x64, + 0x06, 0x66, 0x06, 0x68, 0x06, 0x6a, 0x06, 0x6c, 0x06, 0x6e, + 0x06, 0x70, 0x06, 0x72, 0x06, 0x74, 0x06, 0x76, 0x06, 0x78, +}; + +void stress_clear_ctx_reusable_var(struct com_stress_test_ctx *ctx); + +void com_stress_print_report(const struct com_stress_test_ctx test_ctxs[]); + +int stress_fill_mbuf_with_pattern(struct os_mbuf *om, uint16_t len); + +void stress_l2cap_coc_recv(struct ble_l2cap_chan *chan, struct os_mbuf *sdu); + +void stress_l2cap_coc_accept(uint16_t peer_mtu, struct ble_l2cap_chan *chan); + +void stress_start_timer(uint32_t timeout_ms, os_event_fn *ev_cb); + +int64_t stress_calc_bit_rate(int64_t us, int64_t bytes_num); + +void stress_find_svc_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid, + stress_gatt_disc_end_fn *disc_end_fn); +void stress_find_chr_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid, + const ble_uuid_t *chr_uuid, + stress_gatt_disc_end_fn *disc_end_fn); + +void stress_find_dsc_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid, + const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid, + stress_gatt_disc_end_fn *disc_end_fn); +#ifdef __cplusplus +} +#endif + +#endif //BLE_TGT_STRESS_H diff --git a/src/libs/mynewt-nimble/apps/blestress/src/stress_gatt.c b/src/libs/mynewt-nimble/apps/blestress/src/stress_gatt.c new file mode 100644 index 00000000..a6d845c5 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blestress/src/stress_gatt.c @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "stress_gatt.h" + +uint16_t hrs_hrm_handle = 0xffff; + +static int +stress_gatt_access_cb(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +static const struct ble_gatt_svc_def gatt_svr_svcs[] = { + { + /* Service: Heart-rate */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(STRESS_GATT_UUID), + .characteristics = (struct ble_gatt_chr_def[]) { + { + /* Characteristic: read test */ + .uuid = BLE_UUID16_DECLARE(STRESS_GATT_READ_UUID), + .access_cb = stress_gatt_access_cb, + .val_handle = &hrs_hrm_handle, + .flags = BLE_GATT_CHR_F_READ, + }, + { + /* Characteristic: write test */ + .uuid = BLE_UUID16_DECLARE(STRESS_GATT_WRITE_UUID), + .access_cb = stress_gatt_access_cb, + .val_handle = &hrs_hrm_handle, + .flags = BLE_GATT_CHR_F_WRITE, + }, + { + /* Characteristic: notify test */ + .uuid = BLE_UUID16_DECLARE(STRESS_GATT_NOTIFY_UUID), + .access_cb = stress_gatt_access_cb, + .val_handle = &hrs_hrm_handle, + .flags = BLE_GATT_CHR_F_NOTIFY, + }, + { + /* Characteristic: indicate test */ + .uuid = BLE_UUID16_DECLARE(STRESS_GATT_INDICATE_UUID), + .access_cb = stress_gatt_access_cb, + .val_handle = &hrs_hrm_handle, + .flags = BLE_GATT_CHR_F_INDICATE, + }, + { + 0, /* No more characteristics in this service */ + },} + }, + { + 0, /* No more services */ + }, +}; + +static int +stress_gatt_access_cb(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + /* Sensor location, set to "Chest" */ + static uint8_t chr_value[] = "Hello"; + uint16_t uuid; + int rc; + + //chr_value = (uint8_t)rand() % 256; + uuid = ble_uuid_u16(ctxt->chr->uuid); + + switch(uuid){ + case STRESS_GATT_READ_UUID: + MODLOG_DFLT(INFO, "GATT Read event\n"); + rc = os_mbuf_append(ctxt->om, &chr_value, sizeof(chr_value)); + assert(rc == 0); + //return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + return 0; + case STRESS_GATT_WRITE_UUID: + MODLOG_DFLT(INFO, "GATT Write event\n"); + print_mbuf(ctxt->om); + return 0; + case STRESS_GATT_NOTIFY_UUID: + MODLOG_DFLT(INFO, "GATT Notify event\n"); + return 0; + case STRESS_GATT_INDICATE_UUID: + MODLOG_DFLT(INFO, "GATT Indicate event\n"); + return 0; + default: + MODLOG_DFLT(ERROR, "GATT UUID does not exist\n"); + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +void +gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) +{ + char buf[BLE_UUID_STR_LEN]; + + switch (ctxt->op) { + case BLE_GATT_REGISTER_OP_SVC: + MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n", + ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf), + ctxt->svc.handle); + break; + + case BLE_GATT_REGISTER_OP_CHR: + MODLOG_DFLT(DEBUG, "registering characteristic %s with " + "def_handle=%d val_handle=%d\n", + ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf), + ctxt->chr.def_handle, + ctxt->chr.val_handle); + break; + + case BLE_GATT_REGISTER_OP_DSC: + MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n", + ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf), + ctxt->dsc.handle); + break; + + default: + assert(0); + break; + } +} + +int +gatt_svr_init(void) +{ + int rc; + + rc = ble_gatts_count_cfg(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_add_svcs(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + return 0; +} diff --git a/src/libs/mynewt-nimble/apps/blestress/src/stress_gatt.h b/src/libs/mynewt-nimble/apps/blestress/src/stress_gatt.h new file mode 100644 index 00000000..3344fe2d --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blestress/src/stress_gatt.h @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef BLE_TGT_STRESS_GATT_H +#define BLE_TGT_STRESS_GATT_H + +#include +#include +#include +#include "host/ble_hs.h" +#include "host/ble_uuid.h" +#include "nimble/ble.h" +#include "modlog/modlog.h" +#include "misc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern uint16_t hrs_hrm_handle; + +/* Heart-rate configuration */ +#define STRESS_GATT_UUID 0xC0DE +#define STRESS_GATT_READ_UUID 0xC1DE +#define STRESS_GATT_WRITE_UUID 0xC2DE +#define STRESS_GATT_INDICATE_UUID 0xC3DE +#define STRESS_GATT_NOTIFY_UUID 0xC4DE + +int gatt_svr_init(void); + +void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg); + +#ifdef __cplusplus +} +#endif + + +#endif //BLE_TGT_STRESS_GATT_H diff --git a/src/libs/mynewt-nimble/apps/blestress/src/tx_stress.c b/src/libs/mynewt-nimble/apps/blestress/src/tx_stress.c new file mode 100644 index 00000000..b73adc8a --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blestress/src/tx_stress.c @@ -0,0 +1,1671 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include "tx_stress.h" + +/* Main test task priority. Set a high value so that the task does not + * interfere with event handling */ +#define TX_STRESS_MAIN_TASK_PRIO 0xf0 +#define BASE_UUID_LEN 13 + +/* Contexts for stress tests. */ +static struct com_stress_test_ctx tx_stress_ctxD = { + .conn_handle = 0xffff, + .cur_test_id = 0, +}; + +static struct com_stress_test_ctx *tx_stress_ctx; + +/* Define stack, object and semaphore for test main task. */ +#define TX_STRESS_MAIN_TASK_STACK_SIZE (500) +static struct os_task tx_stress_main_task; +static os_stack_t tx_stress_main_task_stack[TX_STRESS_MAIN_TASK_STACK_SIZE]; +static struct os_sem tx_stress_main_sem; +/* Test use case and address of test advertiser. */ +static int tx_stress_use_case; +static int completed_tests = 0; + +static void +tx_stress_on_test_finish(int test_num) +{ + console_printf("\033[0;32m\nStress test %d completed\033[0m\n", test_num); + ++completed_tests; + tx_stress_ctx->completed[test_num] = true; + os_sem_release(&tx_stress_main_sem); +} + +static void +tx_stress_simple_scan(ble_gap_event_fn *cb, uint16_t duration) +{ + uint8_t own_addr_type; + struct ble_gap_ext_disc_params params = {0}; + int rc; + + /* Figure out address to use while scanning. */ + rc = ble_hs_id_infer_auto(0, &own_addr_type); + if (rc != 0) { + console_printf("\033[0;31mError determining own address type; " + "rc=%d\033[0m\n", rc); + assert(0); + } + + params.itvl = BLE_GAP_SCAN_FAST_INTERVAL_MAX; + params.passive = 1; + params.window = BLE_GAP_SCAN_FAST_WINDOW; + + rc = ble_gap_ext_disc(own_addr_type, duration, 0, 1, 0, 0, ¶ms, NULL, + cb, NULL); + + if (rc != 0) { + console_printf("\033[0;31mError initiating GAP discovery procedure" + "; rc=%d\033[0m\n", rc); + } +} + +static int +tx_stress_simple_connect(ble_gap_event_fn *cb, int test_num) +{ + uint8_t own_addr_type; + int rc; + + /* Set so any PHY mask allowed. */ + ble_gap_set_prefered_default_le_phy(TX_PHY_MASK, RX_PHY_MASK); + + /* Figure out address to use while connecting. */ + rc = ble_hs_id_infer_auto(0, &own_addr_type); + if (rc != 0) { + MODLOG_DFLT(INFO, "\033[0;31mError determining own address type; " + "rc=%d\033[0m\n", rc); + return rc; + } + + MODLOG_DFLT(INFO, "Connection attempt: %d\n", + ++tx_stress_ctx->con_stat[test_num].attempts_num); + + rc = ble_gap_ext_connect(own_addr_type, &tx_stress_ctx->dev_addr, + 10000, + BLE_GAP_LE_PHY_1M_MASK | BLE_GAP_LE_PHY_2M_MASK, + NULL, NULL, NULL, cb, NULL); + + if (rc != 0) { + MODLOG_DFLT(INFO, "\033[0;31mError during connection; rc=%d\033[0m\n", + rc); + } + + return rc; +} + +static int +tx_stress_find_test(struct ble_gap_ext_disc_desc *ext_disc) +{ + struct ble_hs_adv_fields fields; + int data_len; + int rc; + + /* Parser refuses greater length data than 31. But known UUID128 will be + * in first 31 bytes of adv data first packet. */ + if (ext_disc->length_data > 31) { + data_len = 31; + } else { + data_len = ext_disc->length_data; + } + + /* Parse part of adv data. */ + ble_hs_adv_parse_fields(&fields, ext_disc->data, data_len); + print_adv_fields(&fields); + + /* UUID128 service data of stress test advert includes only UUID128. */ + if (fields.svc_data_uuid128_len != 16) { + return -1; + } + + /* Check if service data include known UUID128. */ + rc = memcmp(fields.svc_data_uuid128, (uint8_t[]) {0xC0, 0xDE}, 2); + if (rc) { + return -1; + } + + rc = memcmp(fields.svc_data_uuid128 + 3, MYNEWT_VAL(BLE_STRESS_UUID_BASE), + BASE_UUID_LEN); + + if (rc != 0) { + return -1; + } + + /* This UUID 128 byte indicates the stress test ID to be executed. */ + return fields.svc_data_uuid128[2]; +} + +static int +tx_stress_switcher_gap_event(struct ble_gap_event *event, void *arg) +{ + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Success to connect to device\n"); + return 0; + } else if (event->connect.status == BLE_HS_ETIMEOUT_HCI) { + MODLOG_DFLT(INFO, "Connection timeout\n"); + } else { + MODLOG_DFLT(INFO, "Error: connection attempt failed; status=%d\n", + event->connect.status); + } + /* Connect to rx device just to give it a signal to switch test. */ + tx_stress_simple_connect(tx_stress_switcher_gap_event, + tx_stress_ctx->cur_test_id); + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + os_sem_release(&tx_stress_main_sem); + return 0; + + case BLE_GAP_EVENT_EXT_DISC: + /* Check if caught advert contains known UUID128. The UUID128 + * contains the ID of test use case to be executed. */ + rc = tx_stress_find_test(&event->ext_disc); + if (rc == 0) { + tx_stress_ctx->dev_addr = event->ext_disc.addr; + /* Stop scanning. */ + ble_gap_disc_cancel(); + /* Add token to semaphore. Main task will start the test. */ + os_sem_release(&tx_stress_main_sem); + } + return 0; + + case BLE_GAP_EVENT_DISC_COMPLETE: + MODLOG_DFLT(INFO, "Discovery complete; reason=%d\n", + event->disc_complete.reason); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static void +tx_stress_switch_test() +{ + tx_stress_simple_scan(tx_stress_switcher_gap_event, 0); + os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER); + + tx_stress_simple_connect(tx_stress_switcher_gap_event, 0); +} + +static int +tx_stress_1_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + MODLOG_DFLT(INFO, "Connection %s; status=%d ", + event->connect.status == 0 ? "established" : "failed", + event->connect.status); + + if (event->connect.status == 0) { + /* Connection successfully established. In this use case + * it is error of 'Connect cancel'. Stress test failed. */ + MODLOG_DFLT(INFO, "Success to connect to device\n"); + ++tx_stress_ctx->con_stat[1].num; + + ble_gap_terminate(event->connect.conn_handle, BLE_ERR_NO_PAIRING); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static void +tx_stress_1_test() +{ + int rc; + uint8_t own_addr_type; + ble_addr_t rnd_rx_addr; + int delay_time; + + rc = ble_gap_disc_active(); + assert(rc == 0); + + /* Figure out address to use while advertising. */ + rc = ble_hs_id_infer_auto(0, &own_addr_type); + if (rc != 0) { + MODLOG_DFLT(INFO, "\033[0;31mError determining own address type; " + "rc=%d\033[0m\n", rc); + os_sem_release(&tx_stress_main_sem); + return; + } + + while (tx_stress_ctx->con_stat[1].attempts_num < + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + /* Rand ble address to connect*/ + rc = ble_hs_id_gen_rnd(1, &rnd_rx_addr); + assert (rc == 0); + + MODLOG_DFLT(INFO, "Connection attempt; num=%d\n", + ++tx_stress_ctx->con_stat[1].attempts_num); + + rc = ble_gap_connect(own_addr_type, &rnd_rx_addr, 10000, NULL, + tx_stress_1_gap_event, NULL); + + if (rc != 0) { + MODLOG_DFLT(INFO, "\033[0;31mConnection error; rc=%d\033[0m\n", + rc); + os_sem_release(&tx_stress_main_sem); + return; + } + + MODLOG_DFLT(INFO, "Connect cancel\n"); + ble_gap_conn_cancel(); + console_printf("\033[0;32m>\033[0m"); + } + + console_printf( + "\033[0;32m\nFirst part of test completed\nStart second part: " + "Connect->random delay->cancel\n\033[0m"); + + while (tx_stress_ctx->con_stat[1].attempts_num < + 2 * MYNEWT_VAL(BLE_STRESS_REPEAT)) { + /* Rand ble address to connect*/ + rc = ble_hs_id_gen_rnd(1, &rnd_rx_addr); + assert (rc == 0); + + MODLOG_DFLT(INFO, "Connection attempt; num=%d\n", + ++tx_stress_ctx->con_stat[1].attempts_num); + + delay_time = rand() % 1000; + + MODLOG_DFLT(INFO, "Time to delay=%d\n", delay_time); + + rc = ble_gap_connect(own_addr_type, &rnd_rx_addr, 10000, NULL, + tx_stress_1_gap_event, NULL); + + if (rc != 0) { + MODLOG_DFLT(INFO, "\033[0;31mConnection error; rc=%d\033[0m\n", + rc); + os_sem_release(&tx_stress_main_sem); + return; + } + + os_time_delay(os_time_ms_to_ticks32(delay_time)); + + MODLOG_DFLT(INFO, "Connect cancel\n"); + ble_gap_conn_cancel(); + console_printf("\033[0;32m>\033[0m"); + } + + tx_stress_on_test_finish(1); +} + +static int +tx_stress_2_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Success to connect to device\n"); + + ++tx_stress_ctx->con_stat[2].num; + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + + tx_stress_ctx->conn_handle = desc.conn_handle; + + ble_gap_terminate(event->connect.conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + os_sem_release(&tx_stress_main_sem); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + console_printf("\033[0;32m>\033[0m"); + + if (tx_stress_ctx->con_stat[2].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + tx_stress_on_test_finish(2); + return 0; + } + tx_stress_simple_connect(tx_stress_2_gap_event, + tx_stress_ctx->cur_test_id); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_3_gap_event(struct ble_gap_event *event, void *arg) +{ + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + ++tx_stress_ctx->con_stat[3].num; + MODLOG_DFLT(INFO, "Success to connect to device\n"); + + rc = ble_gap_terminate(event->connect.conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + + MODLOG_DFLT(INFO, "rc=%d\n", rc); + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + os_sem_release(&tx_stress_main_sem); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + console_printf("\033[0;32m>\033[0m"); + + if (tx_stress_ctx->con_stat[3].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + tx_stress_on_test_finish(3); + return 0; + } + tx_stress_simple_connect(tx_stress_3_gap_event, + tx_stress_ctx->cur_test_id); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_4_con_update(void) +{ + int rc; + + /* With every next update at least one param must change. Otherwise no + * event occurs and test will not be continued */ + struct ble_gap_upd_params params = { + .itvl_min = BLE_GAP_INITIAL_CONN_ITVL_MIN, + .itvl_max = BLE_GAP_INITIAL_CONN_ITVL_MAX, + .latency = BLE_GAP_INITIAL_CONN_LATENCY, + /* So let's change e.g. timeout value. Put ...% 2 ? 1 : 2 to make sure + * that value won't grow significantly and will be different with every + * iteration. */ + .supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT + + (tx_stress_ctx->con_stat[4].prms_upd_num % 2 ? + 1 : 2), + .min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN, + .max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN, + }; + + rc = ble_gap_update_params(tx_stress_ctx->conn_handle, ¶ms); + + if (rc == BLE_HS_ENOTCONN) { + MODLOG_DFLT(INFO, "Device disconnected. Connection update failed\n"); + assert(0); + } + + if (rc != 0) { + MODLOG_DFLT(ERROR, "\033[0;31mError during connection update; " + "rc=%d\033[0m\n", rc); + assert(0); + } + + return rc; +} + +static int +tx_stress_4_gap_event(struct ble_gap_event *event, void *arg) +{ + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ++tx_stress_ctx->con_stat[4].attempts_num; + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Success to connect to device\n"); + + ++tx_stress_ctx->con_stat[4].num; + tx_stress_ctx->conn_handle = event->connect.conn_handle; + + tx_stress_4_con_update(); + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + os_sem_release(&tx_stress_main_sem); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + tx_stress_on_test_finish(4); + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE: + if (event->conn_update.status != 0) { + MODLOG_DFLT(INFO, "Connection update failed\n"); + } else { + MODLOG_DFLT(INFO, "Connection updated; num=%d\n", + ++tx_stress_ctx->con_stat[4].prms_upd_num); + console_printf("\033[0;32m>\033[0m"); + } + + if (tx_stress_ctx->con_stat[4].prms_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + ble_gap_terminate(tx_stress_ctx->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + } else { + /* Update connection. */ + rc = tx_stress_4_con_update(); + + if (rc != 0) { + MODLOG_DFLT(INFO, "\033[0;31mError: update fail; " + "rc=%d\033[0m\n", rc); + os_sem_release(&tx_stress_main_sem); + } + } + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE_REQ: + MODLOG_DFLT(INFO, "Connection update request\n"); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_5_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Success to connect to device\n"); + + ++tx_stress_ctx->con_stat[5].num; + tx_stress_ctx->conn_handle = event->connect.conn_handle; + } else { + console_printf("\033[0;31mError: Update fail; " + "status=%d\033[0m\n", event->connect.status); + os_sem_release(&tx_stress_main_sem); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + tx_stress_on_test_finish(5); + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE: + if (event->conn_update.status != 0) { + MODLOG_DFLT(INFO, "Connection update failed\n"); + } else { + MODLOG_DFLT(INFO, "Connection updated; num=%d\n", + ++tx_stress_ctx->con_stat[5].prms_upd_num); + console_printf("\033[0;32m>\033[0m"); + } + + if (tx_stress_ctx->con_stat[5].prms_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + ble_gap_terminate(tx_stress_ctx->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + } + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE_REQ: + MODLOG_DFLT(INFO, "Connection update request\n"); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_6_gap_event(struct ble_gap_event *event, void *arg) +{ + static int start_id = 0; + int use_case = 0; + int adv_pattern_len; + const uint8_t *adv_pattern; + + switch (event->type) { + case BLE_GAP_EVENT_EXT_DISC: + + /* Instance 0 reserved for SWITCH advert. */ + if (event->ext_disc.sid == 0) { + return 0; + } + + /* Check if advertiser is known rx device. */ + if (memcmp(tx_stress_ctx->dev_addr.val, + event->ext_disc.addr.val, 6) != 0) { + return 0; + } + + /* Return -1 if not first package of advert. */ + use_case = tx_stress_find_test(&event->ext_disc); + + if (use_case > 0) { + /* If first package of advert */ + ++tx_stress_ctx->s6_rcv_adv_first; + start_id = 0; + adv_pattern = &event->ext_disc.data[29]; + adv_pattern_len = event->ext_disc.length_data - 29; + } else { + if (start_id == 0) { + return 0; + } + /* If not first package of advert */ + adv_pattern = event->ext_disc.data; + adv_pattern_len = event->ext_disc.length_data; + } + + /* Check data pattern */ + if (memcmp(adv_pattern, test_6_pattern + start_id, + adv_pattern_len) != 0) { + /* Pattern does not match. May lost some data or package. + * Reset data pattern index. */ + start_id = 0; + return 0; + } + + /* At the next adv data package, start comparing from this index.*/ + start_id += adv_pattern_len; + + /* Check if last packet of advert. */ + if (event->ext_disc.data_status == + BLE_GAP_EXT_ADV_DATA_STATUS_COMPLETE) { + /* Got all packets of advert. */ + ++tx_stress_ctx->s6_rcv_adv_suc; + MODLOG_DFLT(INFO, "Got all packets of advert. num=%d\n", + tx_stress_ctx->s6_rcv_adv_suc); + console_printf("\033[0;32m>\033[0m"); + start_id = 0; + + if (tx_stress_ctx->s6_rcv_adv_suc >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + /* Stop scanning. */ + ble_gap_disc_cancel(); + tx_stress_on_test_finish(6); + } + } + return 0; + + case BLE_GAP_EVENT_DISC_COMPLETE: + MODLOG_DFLT(INFO, "Discovery complete; reason=%d\n", + event->disc_complete.reason); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static void +tx_stress_6_perform(void) +{ + tx_stress_simple_scan(tx_stress_6_gap_event, 0); + os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER); + tx_stress_switch_test(); +} + +static int +tx_stress_7_phy_update(void) +{ + int rc; + uint8_t tx_phys_mask; + uint8_t rx_phys_mask; + + ble_gap_read_le_phy(tx_stress_ctx->conn_handle, &tx_phys_mask, + &rx_phys_mask); + + + /* With every next update at least one param must change */ + switch (rx_phys_mask) { + case BLE_GAP_LE_PHY_1M_MASK: + rx_phys_mask = BLE_GAP_LE_PHY_2M_MASK; + break; + case BLE_GAP_LE_PHY_2M_MASK: +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + rx_phys_mask = BLE_GAP_LE_PHY_CODED_MASK; + break; + case BLE_GAP_LE_PHY_CODED_MASK: +#endif + rx_phys_mask = BLE_GAP_LE_PHY_1M_MASK; + break; + default: + rx_phys_mask = BLE_GAP_LE_PHY_1M_MASK; + break; + } + + switch (tx_phys_mask) { + case BLE_GAP_LE_PHY_1M_MASK: + tx_phys_mask = BLE_GAP_LE_PHY_2M_MASK; + break; + case BLE_GAP_LE_PHY_2M_MASK: +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) + tx_phys_mask = BLE_GAP_LE_PHY_CODED_MASK; + break; + case BLE_GAP_LE_PHY_CODED_MASK: +#endif + tx_phys_mask = BLE_GAP_LE_PHY_1M_MASK; + break; + default: + tx_phys_mask = BLE_GAP_LE_PHY_1M_MASK; + break; + } + + rc = ble_gap_set_prefered_le_phy(tx_stress_ctx->conn_handle, + tx_phys_mask, rx_phys_mask, 0); + + if (rc == BLE_HS_ENOTCONN) { + MODLOG_DFLT(INFO, "Device disconnected. Connection update failed\n"); + return rc; + } + + if (rc != 0) { + MODLOG_DFLT(ERROR, "\033[0;31mError during PHY update; " + "rc=%d\033[0m\n", rc); + } + + return rc; +} + +static int +tx_stress_7_gap_event(struct ble_gap_event *event, void *arg) +{ + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Success to connect to device\n"); + + ++tx_stress_ctx->con_stat[7].num; + tx_stress_ctx->conn_handle = event->connect.conn_handle; + + tx_stress_7_phy_update(); + } else { + console_printf("\033[0;31mError: Update fail; " + "status=%d\033[0m\n", event->connect.status); + os_sem_release(&tx_stress_main_sem); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + tx_stress_on_test_finish(7); + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE: + MODLOG_DFLT(INFO, "Connection updated\n"); + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE_REQ: + MODLOG_DFLT(INFO, "Connection update request\n"); + return 0; + + case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: + if (event->phy_updated.status != 0) { + MODLOG_DFLT(INFO, "PHY update failed\n"); + } else { + MODLOG_DFLT(INFO, "PHY updated; num=%d; rx:%d, tx:%d\n", + ++tx_stress_ctx->con_stat[7].phy_upd_num, + event->phy_updated.rx_phy, event->phy_updated.tx_phy); + console_printf("\033[0;32m>\033[0m"); + } + + if (tx_stress_ctx->con_stat[7].phy_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + ble_gap_terminate(tx_stress_ctx->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + } else { + /* Update connection. */ + rc = tx_stress_7_phy_update(); + if (rc != 0) { + console_printf("\033[0;31mError: PHPY update fail; " + "rc=%d\033[0m\n", event->phy_updated.status); + assert(0); + } + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_8_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Success to connect to device\n"); + + ++tx_stress_ctx->con_stat[8].num; + tx_stress_ctx->conn_handle = event->connect.conn_handle; + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + os_sem_release(&tx_stress_main_sem); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + tx_stress_on_test_finish(8); + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE: + if (event->conn_update.status != 0) { + MODLOG_DFLT(INFO, "Connection update failed\n"); + } else { + MODLOG_DFLT(INFO, "Connection updated; num=%d\n", + ++tx_stress_ctx->con_stat[8].prms_upd_num); + } + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE_REQ: + MODLOG_DFLT(INFO, "Connection update request\n"); + return 0; + + case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: + if (event->phy_updated.status != 0) { + MODLOG_DFLT(INFO, "PHY update failed\n"); + } else { + MODLOG_DFLT(INFO, "PHY updated; num=%d\n", + ++tx_stress_ctx->con_stat[8].phy_upd_num); + console_printf("\033[0;32m>\033[0m"); + } + + if (tx_stress_ctx->con_stat[8].phy_upd_num >= + MYNEWT_VAL(BLE_STRESS_REPEAT)) { + ble_gap_terminate(tx_stress_ctx->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + } + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_9_gap_event(struct ble_gap_event *event, void *arg) +{ + ble_addr_t addr; + int test; + + switch (event->type) { + case BLE_GAP_EVENT_EXT_DISC: + /* Looking for next instance of test 9 advert. */ + test = tx_stress_find_test(&event->ext_disc); + /* To avoid messing by rest of test 9 events in queue, check if handle + * filled */ + if (test == 9 && tx_stress_ctx->conn_handle == 0xffff) { + tx_stress_ctx->conn_handle = 0; + ble_gap_disc_cancel(); + tx_stress_ctx->dev_addr = event->ext_disc.addr; + tx_stress_simple_connect(tx_stress_9_gap_event, + tx_stress_ctx->cur_test_id); + } + return 0; + + case BLE_GAP_EVENT_DISC_COMPLETE: + if (event->disc_complete.reason == 0 && !tx_stress_ctx->completed[9]) { + console_printf("\033[0;31mScanning timeout\033[0m"); + tx_stress_ctx->completed[9] = true; + os_sem_release(&tx_stress_main_sem); + return 0; + } + break; + + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Success to connect to device\n"); + MODLOG_DFLT(INFO, "Connections num: %d\n", + ++tx_stress_ctx->con_stat[9].num); + console_printf("\033[0;32m>\033[0m"); + /* Remember max number of handled connections */ + if (tx_stress_ctx->con_stat[9].num > + tx_stress_ctx->con_stat[9].max_num) { + tx_stress_ctx->con_stat[9].max_num = tx_stress_ctx->con_stat[9].num; + } + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + } + break; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason); + console_printf("\033[0;31mX\033[0m"); + MODLOG_DFLT(INFO, "Connections num: %d\n", + --tx_stress_ctx->con_stat[9].num); + + if (tx_stress_ctx->con_stat[9].num == 0) { + os_sem_release(&tx_stress_main_sem); + return 0; + } + break; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } + + if (tx_stress_ctx->completed[9]) { + return 0; + } + + /* End use case after specified number of scan times or max config number + * of connections */ + if (tx_stress_ctx->con_stat[9].attempts_num < + MYNEWT_VAL(BLE_STRESS_REPEAT) && + tx_stress_ctx->con_stat[9].max_num < MYNEWT_VAL(BLE_MAX_CONNECTIONS)) { + if (ble_gap_disc_active() == 0) { + /* Generate and set new random address */ + ble_hs_id_gen_rnd(0, &addr); + ble_hs_id_set_rnd(addr.val); + tx_stress_ctx->conn_handle = 0xffff; + /* Scan for next instance of the test. */ + tx_stress_simple_scan(tx_stress_9_gap_event, 2000); + } + } else { + tx_stress_ctx->completed[9] = true; + os_sem_release(&tx_stress_main_sem); + } + return 0; +} + +static void +tx_stress_9_perform() +{ + int i, rc; + + /* Scan for next instance of the test. */ + tx_stress_simple_scan(tx_stress_9_gap_event, 2000); + + os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER); + + /* On use case finishing terminate all handled connections */ + for (i = 0; i <= MYNEWT_VAL(BLE_MAX_CONNECTIONS); ++i) { + rc = ble_gap_conn_find(i, NULL); + if (rc == 0) { + MODLOG_DFLT(INFO, "Terminating...\n"); + ble_gap_terminate(i, BLE_ERR_REM_USER_CONN_TERM); + } + } + + os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER); + tx_stress_on_test_finish(9); +} + +static int +tx_stress_10_l2cap_send(struct ble_l2cap_chan *chan, uint8_t *data, + int data_len) +{ + struct os_mbuf *data_buf; + int rc; + + /* Get mbuf for adv data */ + data_buf = os_msys_get_pkthdr(data_len, 0); + assert(data != NULL); + + /* Fill mbuf with pattern data */ + rc = os_mbuf_append(data_buf, data, data_len); + + if (rc) { + os_mbuf_free_chain(data_buf); + assert(0); + } + + /* Send data with L2CAP */ + rc = ble_l2cap_send(chan, data_buf); + + return rc; +} + +void tx_stress_10_timer_ev_cb(struct os_event *ev) +{ + assert(ev != NULL); + + if (tx_stress_ctx->rcv_data_flag) { + return; + } + + tx_stress_ctx->timeout_flag = true; + MODLOG_DFLT(INFO, "L2CAP receiving timeout\n"); + ble_gap_terminate(tx_stress_ctx->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); +} + +static void +tx_stress_10_l2cap_send_req() +{ + int rc; + /* Send a request to the RX device */ + + MODLOG_DFLT(INFO, "L2CAP sending request\n"); + tx_stress_ctx->timeout_flag = false; + tx_stress_ctx->rcv_data_flag = false; + stress_start_timer(7000, tx_stress_10_timer_ev_cb); + + /* Get the sending begin time */ + tx_stress_ctx->begin_us = os_get_uptime_usec(); + + /* Send anything just to give a signal to start sending data + * by RX device */ + rc = tx_stress_10_l2cap_send(tx_stress_ctx->chan, (uint8_t *) "S", + sizeof("S")); + assert(rc == 0); +} + +static int +tx_stress_10_l2cap_event(struct ble_l2cap_event *event, void *arg) +{ + static int i = 0; + int64_t us = 0; + struct ble_l2cap_chan_info chan_info; + + switch (event->type) { + case BLE_L2CAP_EVENT_COC_CONNECTED: + /* A new L2CAP connection was established. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Established L2CAP connection\n"); + tx_stress_ctx->chan = event->connect.chan; + + ble_l2cap_get_chan_info(event->connect.chan, &chan_info); + + MODLOG_DFLT(INFO, + "LE COC connected, conn: %d, chan: 0x%08lx, scid: 0x%04x, " + "dcid: 0x%04x, our_mtu: 0x%04x, peer_mtu: 0x%04x\n", + event->connect.conn_handle, + (uint32_t) event->connect.chan, + chan_info.scid, + chan_info.dcid, + chan_info.our_l2cap_mtu, + chan_info.peer_l2cap_mtu); + + tx_stress_10_l2cap_send_req(); + } + return 0; + + case BLE_L2CAP_EVENT_COC_DISCONNECTED: + MODLOG_DFLT(INFO, "Remote device disconnected from L2CAP server\n"); + return 0; + + case BLE_L2CAP_EVENT_COC_ACCEPT: + stress_l2cap_coc_accept(event->accept.peer_sdu_size, + event->accept.chan); + return 0; + + case BLE_L2CAP_EVENT_COC_DATA_RECEIVED: + /* Get the time of data receive */ + tx_stress_ctx->end_us = os_get_uptime_usec(); + + /* And test after timeout */ + if (tx_stress_ctx->timeout_flag) { + ble_gap_terminate(tx_stress_ctx->conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + return 0; + } + + tx_stress_ctx->rcv_data_flag = true; + + stress_l2cap_coc_recv(event->receive.chan, event->receive.sdu_rx); + + /* Time of data sending */ + us = tx_stress_ctx->end_us - tx_stress_ctx->begin_us; + MODLOG_DFLT(INFO, "Time of receiving L2CAP data: %ld \n", + tx_stress_ctx->end_us); + + /* Remember size of entire mbuf chain */ + tx_stress_ctx->rcv_data_bytes = OS_MBUF_PKTLEN( + event->receive.sdu_rx); + MODLOG_DFLT(INFO, "Num of received bytes: %lld\n", + tx_stress_ctx->rcv_data_bytes); + + /* Calculate the bit rate of this send */ + tx_stress_ctx->s10_bit_rate = + stress_calc_bit_rate(us, tx_stress_ctx->rcv_data_bytes); + MODLOG_DFLT(INFO, "Bit rate: %d B/s\n", tx_stress_ctx->s10_bit_rate); + + /* Remember the sum of bytes and the time to calculate the average + * bit rate. */ + tx_stress_ctx->bytes_sum += tx_stress_ctx->rcv_data_bytes; + tx_stress_ctx->time_sum += us; + + /* Remember max received MTU */ + if (tx_stress_ctx->s10_max_mtu < tx_stress_ctx->rcv_data_bytes) { + tx_stress_ctx->s10_max_mtu = tx_stress_ctx->rcv_data_bytes; + } + console_printf("\033[0;32m>\033[0m"); + MODLOG_DFLT(INFO, "Loop nr: %d\n", ++i); + + tx_stress_10_l2cap_send_req(); + return 0; + + case BLE_L2CAP_EVENT_COC_TX_UNSTALLED: + MODLOG_DFLT(INFO, "L2CAP event unstalled\n"); + return 0; + + default: + MODLOG_DFLT(INFO, "Other L2CAP event occurs; rc=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_10_gap_event(struct ble_gap_event *event, void *arg) +{ + int rc; + struct os_mbuf *sdu_rx; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Success to connect to device; num: %d\n", + ++tx_stress_ctx->con_stat[10].num); + + sdu_rx = os_msys_get_pkthdr(STRESS_COC_MTU, 0); + assert(sdu_rx != NULL); + + tx_stress_ctx->conn_handle = event->connect.conn_handle; + rc = ble_l2cap_connect(event->connect.conn_handle, 1, + STRESS_COC_MTU, sdu_rx, + tx_stress_10_l2cap_event, NULL); + assert(rc == 0); + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + tx_stress_ctx->s10_bit_rate = 1000000 * tx_stress_ctx->bytes_sum / + tx_stress_ctx->time_sum; + + MODLOG_DFLT(INFO, "Average bit rate: %d B/s\n", + tx_stress_ctx->s10_bit_rate); + tx_stress_on_test_finish(10); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_11_gap_event(struct ble_gap_event *event, void *arg) +{ + int test; + + switch (event->type) { + case BLE_GAP_EVENT_EXT_DISC: + /* Looking for next instance of test 9 advert. */ + test = tx_stress_find_test(&event->ext_disc); + + /* To avoid messing by rest of test 9 events in queue, check if handle + * filled */ + if (test == 11 && tx_stress_ctx->conn_handle == 0xffff) { + tx_stress_ctx->conn_handle = 0; + ble_gap_disc_cancel(); + tx_stress_ctx->dev_addr = event->ext_disc.addr; + tx_stress_simple_connect(tx_stress_11_gap_event, + tx_stress_ctx->cur_test_id); + } + return 0; + + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + ++tx_stress_ctx->con_stat[11].num; + MODLOG_DFLT(INFO, "Success to connect to device\n"); + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + break; + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + console_printf("\033[0;32m>\033[0m"); + + if (tx_stress_ctx->con_stat[11].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + tx_stress_on_test_finish(11); + return 0; + } + break; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } + + tx_stress_ctx->conn_handle = 0xffff; + + /* Scan for next instance of the test. */ + tx_stress_simple_scan(tx_stress_11_gap_event, 750); + + return 0; +} + +static int +tx_stress_12_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + ++tx_stress_ctx->con_stat[12].num; + MODLOG_DFLT(INFO, "Success to connect to device\n"); + tx_stress_ctx->conn_handle = event->connect.conn_handle; + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + /* Finish test after first disconnection */ + tx_stress_on_test_finish(12); + return 0; + + case BLE_GAP_EVENT_NOTIFY_RX: + /* Received indication */ + MODLOG_DFLT(INFO, "Notify RX event\n"); + console_printf("\033[0;32m>\033[0m"); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_13_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + ++tx_stress_ctx->con_stat[13].num; + MODLOG_DFLT(INFO, "Success to connect to device\n"); + tx_stress_ctx->conn_handle = event->connect.conn_handle; + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + assert(0); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + /* Finish test after disconnection */ + tx_stress_on_test_finish(13); + return 0; + + case BLE_GAP_EVENT_NOTIFY_RX: + MODLOG_DFLT(INFO, "Notify RX event\n"); + console_printf("\033[0;32m>\033[0m"); + os_mbuf_free_chain(event->notify_rx.om); + ++tx_stress_ctx->rcv_num; + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_14_subs_cb(uint16_t conn_handle, const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg) +{ + struct os_mbuf *om; + bool *sub; + int rc; + + assert(error->status == 0); + + /* If the first subscription after finding cccd */ + if(arg == NULL) { + return 0; + } + + sub = (bool*)arg; + + /* Enable notifications */ + if(*sub == 0) { + *sub = true; + om = ble_hs_mbuf_from_flat( + (uint8_t[]) {0x01, 0x00}, 2); + + tx_stress_ctx->begin_us = tx_stress_ctx->end_us; + + rc = ble_gattc_write(tx_stress_ctx->conn_handle, + tx_stress_ctx->dsc_handle, om, + tx_stress_14_subs_cb, arg); + assert(rc == 0); + } + + return 0; +} + +static void +tx_stress_14_disc_cccd_fn(struct stress_gatt_search_ctx *search_ctx) +{ + int rc; + struct os_mbuf *om; + MODLOG_DFLT(INFO, "CCCD found\n"); + + /* Enable notifications */ + om = ble_hs_mbuf_from_flat((uint8_t[]) {0x01, 0x00}, 2); + tx_stress_ctx->begin_us = os_get_uptime_usec(); + tx_stress_ctx->dsc_handle = search_ctx->dsc_handle; + + rc = ble_gattc_write(tx_stress_ctx->conn_handle, + tx_stress_ctx->dsc_handle, om, + tx_stress_14_subs_cb, NULL); + assert(rc == 0); +} + +static int +tx_stress_14_gap_event(struct ble_gap_event *event, void *arg) +{ + int64_t us = 0; + struct os_mbuf *om; + int rc; + static bool subscribed = true; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + ++tx_stress_ctx->con_stat[14].num; + MODLOG_DFLT(INFO, "Success to connect to device\n"); + tx_stress_ctx->conn_handle = event->connect.conn_handle; + + /* Find CCCD handle (with default UUID16 = 0x2902) */ + stress_find_dsc_handle(event->connect.conn_handle, + BLE_UUID16_DECLARE(STRESS_GATT_UUID), + BLE_UUID16_DECLARE(STRESS_GATT_NOTIFY_UUID), + BLE_UUID16_DECLARE(0x2902), + &tx_stress_14_disc_cccd_fn); + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + assert(0); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", + event->disconnect.reason); + /* Calc average notifying time */ + if (tx_stress_ctx->rcv_num > 0) { + tx_stress_ctx->s14_notif_time = tx_stress_ctx->time_sum / + tx_stress_ctx->rcv_num; + } + MODLOG_DFLT(INFO, "Average notification time: %d\n", + tx_stress_ctx->s14_notif_time); + /* Finish test after first disconnection */ + tx_stress_on_test_finish(14); + return 0; + + case BLE_GAP_EVENT_NOTIFY_RX: + tx_stress_ctx->end_us = os_get_uptime_usec(); + MODLOG_DFLT(INFO, "Notify RX event\n"); + + /* Time of data sending */ + us = tx_stress_ctx->end_us - tx_stress_ctx->begin_us; + MODLOG_DFLT(INFO, "Notification time: %lld\n us", us); + + tx_stress_ctx->time_sum += us; + console_printf("\033[0;32m>\033[0m"); + + /* Perform use case specified number of times */ + if (++tx_stress_ctx->rcv_num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + rc = ble_gap_terminate(event->notify_rx.conn_handle, + BLE_ERR_REM_USER_CONN_TERM); + MODLOG_DFLT(INFO, "rc=%d\n"); + assert(rc == 0); + return 0; + } + + /* Disable notifications */ + subscribed = false; + om = ble_hs_mbuf_from_flat( + (uint8_t[]) {0x00, 0x00}, 2); + + rc = ble_gattc_write(tx_stress_ctx->conn_handle, + tx_stress_ctx->dsc_handle, om, + tx_stress_14_subs_cb, &subscribed); + assert(rc == 0); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +tx_stress_15_write_cb(uint16_t conn_handle, const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg) +{ + /* Disconnect */ + ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM); + console_printf("\033[0;32m>\033[0m"); + return 0; +} + +static void +tx_stress_15_disc_chr_fn(struct stress_gatt_search_ctx *search_ctx) +{ + int rc; + struct os_mbuf *om; + + /* Send some data */ + MODLOG_DFLT(INFO, "Write to chr\n"); + om = ble_hs_mbuf_from_flat(test_6_pattern, 20); + + rc = ble_gattc_write(tx_stress_ctx->conn_handle, + search_ctx->chr_start_handle, om, + tx_stress_15_write_cb, NULL); + assert(rc == 0); +} + +static int +tx_stress_15_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed. */ + if (event->connect.status == 0) { + MODLOG_DFLT(INFO, "Success to connect to device; num: %d\n", + ++tx_stress_ctx->con_stat[15].num); + tx_stress_ctx->conn_handle = event->connect.conn_handle; + + /* Find characteristic handle */ + stress_find_chr_handle(event->connect.conn_handle, + BLE_UUID16_DECLARE(STRESS_GATT_UUID), + BLE_UUID16_DECLARE(STRESS_GATT_WRITE_UUID), + &tx_stress_15_disc_chr_fn); + } else { + MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " + "status=%d\033[0m\n", event->connect.status); + assert(0); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + /* Perform use case specified number of times */ + if(tx_stress_ctx->con_stat[15].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { + tx_stress_on_test_finish(15); + return 0; + } + /* Reconnect */ + tx_stress_simple_connect(tx_stress_15_gap_event, 15); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static int +scan_for_test_gap_event(struct ble_gap_event *event, void *arg) +{ + int use_case; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_EXT_DISC: + /* Check if caught advert contains known UUID128. The UUID128 + * contains the ID of test use case to be executed. */ + use_case = tx_stress_find_test(&event->ext_disc); + if (use_case > 0) { + rc = ble_gap_disc_cancel(); + tx_stress_ctx->dev_addr = event->ext_disc.addr; + + /* After discovery cancel there are still some events in queue. */ + if (rc == 0) { + tx_stress_use_case = use_case; + /* Add token to semaphore. Main task will start the test. */ + os_sem_release(&tx_stress_main_sem); + } + } + return 0; + + case BLE_GAP_EVENT_DISC_COMPLETE: + /* On timeout */ + tx_stress_ctx->scan_timeout = true; + console_printf("\033[1;36mDiscover complete\033[0m\n"); + os_sem_release(&tx_stress_main_sem); + return 0; + + default: + MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); + return 0; + } +} + +static void +tx_stress_test_perform(int test_num) +{ + /* Perform every test only once */ +// if (test_num <= 0 || tx_stress_ctx->completed[test_num] == true) { +// return; +// } + + tx_stress_ctx->cur_test_id = test_num; + tx_stress_ctx->completed[test_num] = false; + tx_stress_ctx->conn_handle = 0xffff; + + console_printf("\033[1;36mStart test num %d - ", test_num); + + /* Start test */ + switch (test_num) { + case 0: + return; + case 1: + console_printf("Stress Connect -> Connect Cancel\033[0m\n"); + tx_stress_1_test(); + break; + case 2: + console_printf("Stress Connect/Disconnect legacy\033[0m\n"); + tx_stress_simple_connect(&tx_stress_2_gap_event, 2); + break; + case 3: + console_printf("Stress Connect/Disconnect ext adv\033[0m\n"); + tx_stress_simple_connect(&tx_stress_3_gap_event, 3); + break; + case 4: + console_printf("Stress connection params update (TX)\033[0m\n"); + tx_stress_simple_connect(&tx_stress_4_gap_event, 4); + break; + case 5: + console_printf("Stress connection params update (RX)\033[0m\n"); + tx_stress_simple_connect(&tx_stress_5_gap_event, 5); + break; + case 6: + console_printf("Stress Scan\033[0m\n"); + tx_stress_6_perform(); + break; + case 7: + console_printf("Stress PHY Update (TX)\033[0m\n"); + tx_stress_simple_connect(&tx_stress_7_gap_event, 7); + break; + case 8: + console_printf("Stress PHY Update (RX)\033[0m\n"); + tx_stress_simple_connect(&tx_stress_8_gap_event, 8); + break; + case 9: + console_printf("Stress multi connection\033[0m\n"); + tx_stress_9_perform(); + break; + case 10: + console_printf("Stress L2CAP send\033[0m\n"); + tx_stress_simple_connect(&tx_stress_10_gap_event, 10); + break; + case 11: + console_printf("Stress Advertise/Connect/Disconnect\033[0m\n"); + tx_stress_simple_connect(&tx_stress_11_gap_event, 11); + break; + case 12: + console_printf("Stress GATT indication\033[0m\n"); + tx_stress_simple_connect(&tx_stress_12_gap_event, 12); + break; + case 13: + console_printf("Stress GATT notification\033[0m\n"); + tx_stress_simple_connect(&tx_stress_13_gap_event, 13); + break; + case 14: + console_printf("Stress GATT Subscribe/Notify/Unsubscribe\033[0m\n"); + tx_stress_simple_connect(&tx_stress_14_gap_event, 14); + break; + case 15: + console_printf("Stress Connect/Send/Disconnect\033[0m\n"); + tx_stress_simple_connect(&tx_stress_15_gap_event, 15); + break; + default: + console_printf("\033[0;31mFound test, but do not know how to perform." + "\033[0m\n"); + assert(0); + } + + /* Wait for the test to finish. Then 1 token will be released + * allowing to pass through semaphore. */ + os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER); + + stress_clear_ctx_reusable_var(tx_stress_ctx); +} + +static void +tx_stress_read_command_cb(void) { + console_printf("Start testing\n"); + os_sem_release(&tx_stress_main_sem); +} + +static void +tx_stress_main_task_fn(void *arg) +{ + int rc; + + tx_stress_ctx = &tx_stress_ctxD; + + console_printf("\033[1;36mTX device\033[0m\n"); + console_printf("Press ENTER to start: \n"); + console_init(&tx_stress_read_command_cb); + + /* Waite for pressing ENTER in console */ + os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER); + + /* Init semaphore with 0 tokens. */ + rc = os_sem_init(&tx_stress_main_sem, 0); + assert(rc == 0); + + /* Start test 1 - Connect/Connect cancel */ + //tx_stress_test_perform(1); + + while (1) { + console_printf("\033[0;36mStart scan for test\033[0m\n"); + + /* Scan for known UUID128 of one of the stress tests. */ + tx_stress_simple_scan(scan_for_test_gap_event, 2000); + + /* Wait for the scan to find the test. Then 1 token will be + * released allowing to pass through semaphore. */ + os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER); + if(tx_stress_ctx->scan_timeout) { + break; + } + + /* Start test. */ + tx_stress_test_perform(tx_stress_use_case); + tx_stress_use_case = -1; + } + + /* Print tests results */ + com_stress_print_report(tx_stress_ctx); + + /* Task should never return */ + while (1) { + /* Delay used only to prevent watchdog to reset the device. */ + os_time_delay(os_time_ms_to_ticks32(2000)); + } +} + +void tx_stress_start_auto() +{ + /* Start task that will run all stress tests one by one. */ + os_task_init(&tx_stress_main_task, "tx_stress_main_task", + tx_stress_main_task_fn, NULL, TX_STRESS_MAIN_TASK_PRIO, + OS_WAIT_FOREVER, tx_stress_main_task_stack, + TX_STRESS_MAIN_TASK_STACK_SIZE); +} diff --git a/src/libs/mynewt-nimble/apps/blestress/src/tx_stress.h b/src/libs/mynewt-nimble/apps/blestress/src/tx_stress.h new file mode 100644 index 00000000..83ed3020 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blestress/src/tx_stress.h @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef _BLE_STRESS_TX_H +#define _BLE_STRESS_TX_H + +#include +#include +#include +#include + +/* BLE */ +#include "nimble/ble.h" +#include "host/ble_hs.h" + +#include "misc.h" +#include "stress.h" +#include "stress_gatt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Scan and execute tests one by one. + */ +void tx_stress_start_auto(); + +#ifdef __cplusplus +} +#endif + +#endif //_BLE_STRESS_TX_H diff --git a/src/libs/mynewt-nimble/apps/blestress/syscfg.yml b/src/libs/mynewt-nimble/apps/blestress/syscfg.yml new file mode 100644 index 00000000..344466ce --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blestress/syscfg.yml @@ -0,0 +1,74 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Settings this app defines. +syscfg.defs: + BLE_STRESS_TEST_ROLE: + description: 0 - TX device, 1 - RX device + value: 0 + + BLE_STRESS_REPEAT: + description: Number of times to repeat each stress test use case + value: 50 + + BLE_STRESS_UUID_BASE: + description: Part of the test UUID that is specific to the current + device couple. + value: ((uint8_t[13]){0xA5, 0x4A, 0xB4, 0x44, 0xC3, 0xBF, 0xB5, + 0xF8, 0xF9, 0x42, 0x83, 0xA1, 0xDA}) + +# Settings this app overrides. +syscfg.vals: + # Change these settings: + + # Set 0 to print all debug logs, but keep in mind that plenty of + # adv packets will be lost during scan tests. + LOG_LEVEL: 1 + + # The maximum number of concurrent connections. For some devices, this + # value will need to be reduced due to the RAM capacity. + BLE_MAX_CONNECTIONS: 50 + + # Should not change these settings: + + # Enable Extended Advertising + BLE_EXT_ADV: 1 + + # Max advertising data size + BLE_EXT_ADV_MAX_SIZE: 1650 + + # Number of multi-advertising instances. Note that due + # to historical reasons total number of advertising + # instances is BLE_MULTI_ADV_INSTANCES + 1 as instance + # 0 is always available + BLE_MULTI_ADV_INSTANCES: 2 + + # Controller uses msys pool for storing advertising data and scan responses. + # Since we advertise a lot of data (~4k in total) at the same time we need + # to increase block count. + MSYS_1_BLOCK_COUNT: 50 + + # + BLE_L2CAP_COC_MAX_NUM: 2 + + # Enable 2M PHY + BLE_LL_CFG_FEAT_LE_2M_PHY: 1 + + # Enable CODED PHY + BLE_LL_CFG_FEAT_LE_CODED_PHY: 1 diff --git a/src/libs/mynewt-nimble/apps/btshell/pkg.yml b/src/libs/mynewt-nimble/apps/btshell/pkg.yml new file mode 100644 index 00000000..29541b74 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/btshell/pkg.yml @@ -0,0 +1,39 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: apps/btshell +pkg.type: app +pkg.description: Shell application exposing the nimble GAP and GATT. +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/shell" + - nimble/host + - nimble/host/services/gap + - nimble/host/services/gatt + - nimble/host/store/ram + - nimble/transport + +pkg.deps.BTSHELL_ANS: + - nimble/host/services/ans diff --git a/src/libs/mynewt-nimble/apps/btshell/src/btshell.h b/src/libs/mynewt-nimble/apps/btshell/src/btshell.h new file mode 100644 index 00000000..7c978221 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/btshell/src/btshell.h @@ -0,0 +1,212 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BTSHELL_PRIV_ +#define H_BTSHELL_PRIV_ + +#include +#include "os/mynewt.h" +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "modlog/modlog.h" + +#include "host/ble_gatt.h" +#include "host/ble_gap.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_gap_white_entry; +struct ble_hs_adv_fields; +struct ble_gap_upd_params; +struct ble_gap_conn_params; +struct hci_adv_params; +struct ble_l2cap_sig_update_req; +struct ble_l2cap_sig_update_params; +union ble_store_value; +union ble_store_key; +struct ble_gap_adv_params; +struct ble_gap_conn_desc; +struct ble_gap_disc_params; + +struct btshell_dsc { + SLIST_ENTRY(btshell_dsc) next; + struct ble_gatt_dsc dsc; +}; +SLIST_HEAD(btshell_dsc_list, btshell_dsc); + +struct btshell_chr { + SLIST_ENTRY(btshell_chr) next; + struct ble_gatt_chr chr; + + struct btshell_dsc_list dscs; +}; +SLIST_HEAD(btshell_chr_list, btshell_chr); + +struct btshell_svc { + SLIST_ENTRY(btshell_svc) next; + struct ble_gatt_svc svc; + struct btshell_chr_list chrs; + bool discovered; +}; + +SLIST_HEAD(btshell_svc_list, btshell_svc); + +struct btshell_l2cap_coc { + SLIST_ENTRY(btshell_l2cap_coc) next; + struct ble_l2cap_chan *chan; + bool stalled; +}; + +SLIST_HEAD(btshell_l2cap_coc_list, btshell_l2cap_coc); + +struct btshell_conn { + uint16_t handle; + struct btshell_svc_list svcs; + struct btshell_l2cap_coc_list coc_list; +}; + +struct btshell_scan_opts { + uint16_t limit; + uint8_t ignore_legacy:1; + uint8_t periodic_only:1; +}; + +extern struct btshell_conn btshell_conns[MYNEWT_VAL(BLE_MAX_CONNECTIONS)]; +extern int btshell_num_conns; + +int btshell_exchange_mtu(uint16_t conn_handle); +int btshell_disc_svcs(uint16_t conn_handle); +int btshell_disc_svc_by_uuid(uint16_t conn_handle, const ble_uuid_t *uuid); +int btshell_disc_all_chrs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle); +int btshell_disc_all_chrs_in_svc(uint16_t conn_handle, struct btshell_svc *svc); +int btshell_disc_chrs_by_uuid(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, const ble_uuid_t *uuid); +int btshell_disc_all_dscs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle); +int btshell_disc_full(uint16_t conn_handle); +int btshell_find_inc_svcs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle); +int btshell_read(uint16_t conn_handle, uint16_t attr_handle); +int btshell_read_long(uint16_t conn_handle, uint16_t attr_handle, + uint16_t offset); +int btshell_read_by_uuid(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, const ble_uuid_t *uuid); +int btshell_read_mult(uint16_t conn_handle, uint16_t *attr_handles, + int num_attr_handles); +int btshell_write(uint16_t conn_handle, uint16_t attr_handle, + struct os_mbuf *om); +int btshell_write_no_rsp(uint16_t conn_handle, uint16_t attr_handle, + struct os_mbuf *om); +int btshell_write_long(uint16_t conn_handle, uint16_t attr_handle, + uint16_t offset, struct os_mbuf *om); +int btshell_write_reliable(uint16_t conn_handle, + struct ble_gatt_attr *attrs, int num_attrs); +#if MYNEWT_VAL(BLE_EXT_ADV) +int btshell_ext_adv_configure(uint8_t instance, + const struct ble_gap_ext_adv_params *params, + int8_t *selected_tx_power); +int btshell_ext_adv_start(uint8_t instance, int duration, + int max_events, bool restart); +int btshell_ext_adv_stop(uint8_t instance); +#endif +int btshell_adv_start(uint8_t own_addr_type, const ble_addr_t *direct_addr, + int32_t duration_ms, + const struct ble_gap_adv_params *params, + bool restart); +int btshell_adv_stop(void); +int btshell_conn_initiate(uint8_t own_addr_type, const ble_addr_t *peer_addr, + int32_t duration_ms, + struct ble_gap_conn_params *params); +int btshell_ext_conn_initiate(uint8_t own_addr_type, + const ble_addr_t *peer_addr, + int32_t duration_ms, + struct ble_gap_conn_params *phy_1m_params, + struct ble_gap_conn_params *phy_2m_params, + struct ble_gap_conn_params *phy_coded_params); +int btshell_conn_cancel(void); +int btshell_term_conn(uint16_t conn_handle, uint8_t reason); +int btshell_wl_set(ble_addr_t *addrs, int addrs_count); +int btshell_scan(uint8_t own_addr_type, int32_t duration_ms, + const struct ble_gap_disc_params *disc_params, void *cb_args); +int btshell_ext_scan(uint8_t own_addr_type, uint16_t duration, uint16_t period, + uint8_t filter_duplicates, uint8_t filter_policy, + uint8_t limited, + const struct ble_gap_ext_disc_params *uncoded_params, + const struct ble_gap_ext_disc_params *coded_params, + void *cb_args); +int btshell_scan_cancel(void); +int btshell_update_conn(uint16_t conn_handle, + struct ble_gap_upd_params *params); +void btshell_notify(uint16_t attr_handle); +int btshell_datalen(uint16_t conn_handle, uint16_t tx_octets, + uint16_t tx_time); +int btshell_l2cap_update(uint16_t conn_handle, + struct ble_l2cap_sig_update_params *params); +int btshell_sec_start(uint16_t conn_handle); +int btshell_sec_pair(uint16_t conn_handle); +int btshell_sec_unpair(ble_addr_t *peer_addr); +int btshell_sec_restart(uint16_t conn_handle, uint8_t key_size, + uint8_t *ltk, uint16_t ediv, + uint64_t rand_val, int auth); +int btshell_tx_start(uint16_t conn_handle, uint16_t len, uint16_t rate, + uint16_t num); +void btshell_tx_stop(void); +int btshell_rssi(uint16_t conn_handle, int8_t *out_rssi); +int btshell_l2cap_create_srv(uint16_t psm, uint16_t mtu, int accept_response); +int btshell_l2cap_connect(uint16_t conn, uint16_t psm, uint16_t mtu, uint8_t num); +int btshell_l2cap_disconnect(uint16_t conn, uint16_t idx); +int btshell_l2cap_send(uint16_t conn, uint16_t idx, uint16_t bytes); +int btshell_l2cap_reconfig(uint16_t conn_handle, uint16_t mtu, + uint8_t num, uint8_t idxs[]); + +int btshell_gap_event(struct ble_gap_event *event, void *arg); +void btshell_sync_stats(uint16_t handle); + +/** GATT server. */ +#define GATT_SVR_SVC_ALERT_UUID 0x1811 +#define GATT_SVR_CHR_SUP_NEW_ALERT_CAT_UUID 0x2A47 +#define GATT_SVR_CHR_NEW_ALERT 0x2A46 +#define GATT_SVR_CHR_SUP_UNR_ALERT_CAT_UUID 0x2A48 +#define GATT_SVR_CHR_UNR_ALERT_STAT_UUID 0x2A45 +#define GATT_SVR_CHR_ALERT_NOT_CTRL_PT 0x2A44 + +void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg); +int gatt_svr_init(void); +void gatt_svr_print_svcs(void); + +/** Misc. */ +void print_bytes(const uint8_t *bytes, int len); +void print_mbuf(const struct os_mbuf *om); +void print_addr(const void *addr); +void print_uuid(const ble_uuid_t *uuid); +int svc_is_empty(const struct btshell_svc *svc); +uint16_t chr_end_handle(const struct btshell_svc *svc, + const struct btshell_chr *chr); +int chr_is_empty(const struct btshell_svc *svc, const struct btshell_chr *chr); +void print_conn_desc(const struct ble_gap_conn_desc *desc); +void print_svc(struct btshell_svc *svc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libs/mynewt-nimble/apps/btshell/src/cmd.c b/src/libs/mynewt-nimble/apps/btshell/src/cmd.c new file mode 100644 index 00000000..8a878756 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/btshell/src/cmd.c @@ -0,0 +1,4659 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include "os/mynewt.h" +#include "bsp/bsp.h" + +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "nimble/hci_common.h" +#include "host/ble_gap.h" +#include "host/ble_hs_adv.h" +#include "host/ble_sm.h" +#include "host/ble_eddystone.h" +#include "host/ble_hs_id.h" +#include "services/gatt/ble_svc_gatt.h" +#include "../src/ble_hs_priv.h" + +#include "console/console.h" +#include "shell/shell.h" + +#include "cmd.h" +#include "btshell.h" +#include "cmd_gatt.h" +#include "cmd_l2cap.h" + +#define BTSHELL_MODULE "btshell" + +int +cmd_parse_conn_start_end(uint16_t *out_conn, uint16_t *out_start, + uint16_t *out_end) +{ + int rc; + + *out_conn = parse_arg_uint16("conn", &rc); + if (rc != 0) { + return rc; + } + + *out_start = parse_arg_uint16("start", &rc); + if (rc != 0) { + return rc; + } + + *out_end = parse_arg_uint16("end", &rc); + if (rc != 0) { + return rc; + } + + return 0; +} + +static const struct kv_pair cmd_own_addr_types[] = { + { "public", BLE_OWN_ADDR_PUBLIC }, + { "random", BLE_OWN_ADDR_RANDOM }, + { "rpa_pub", BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT }, + { "rpa_rnd", BLE_OWN_ADDR_RPA_RANDOM_DEFAULT }, + { NULL } +}; + +static const struct kv_pair cmd_peer_addr_types[] = { + { "public", BLE_ADDR_PUBLIC }, + { "random", BLE_ADDR_RANDOM }, + { "public_id", BLE_ADDR_PUBLIC_ID }, + { "random_id", BLE_ADDR_RANDOM_ID }, + { NULL } +}; + +static const struct kv_pair cmd_addr_type[] = { + { "public", BLE_ADDR_PUBLIC }, + { "random", BLE_ADDR_RANDOM }, + { NULL } +}; + + +static int +parse_dev_addr(const char *prefix, const struct kv_pair *addr_types, + ble_addr_t *addr) +{ + char name[32]; + int rc; + + /* XXX string operations below are not quite safe, but do we care? */ + + if (!prefix) { + name[0] = '\0'; + } else { + strcpy(name, prefix); + } + + strcat(name, "addr"); + rc = parse_arg_addr(name, addr); + if (rc == ENOENT) { + /* not found */ + return rc; + } else if (rc == EAGAIN) { + /* address found, but no type provided */ + strcat(name, "_type"); + addr->type = parse_arg_kv(name, addr_types, &rc); + if (rc == ENOENT) { + addr->type = BLE_ADDR_PUBLIC; + } else if (rc != 0) { + return rc; + } + } else if (rc != 0) { + /* error parsing address */ + return rc; + } else { + /* full address found, but let's just make sure there is no type arg */ + strcat(name, "_type"); + if (parse_arg_extract(name)) { + return E2BIG; + } + } + + return 0; +} + +/***************************************************************************** + * $advertise * + *****************************************************************************/ +static const struct kv_pair cmd_adv_filt_types[] = { + { "none", BLE_HCI_ADV_FILT_NONE }, + { "scan", BLE_HCI_ADV_FILT_SCAN }, + { "conn", BLE_HCI_ADV_FILT_CONN }, + { "both", BLE_HCI_ADV_FILT_BOTH }, + { NULL } +}; + +#if MYNEWT_VAL(BLE_EXT_ADV) +static struct kv_pair cmd_ext_adv_phy_opts[] = { + { "1M", 0x01 }, + { "2M", 0x02 }, + { "coded", 0x03 }, + { NULL } +}; + +static int +cmd_advertise_configure(int argc, char **argv) +{ + struct ble_gap_ext_adv_params params = {0}; + int8_t selected_tx_power; + uint8_t instance; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + instance = parse_arg_uint8_dflt("instance", 0, &rc); + if (rc != 0 || instance >= BLE_ADV_INSTANCES) { + console_printf("invalid instance\n"); + return rc; + } + + memset(¶ms, 0, sizeof(params)); + + params.legacy_pdu = parse_arg_bool_dflt("legacy", 0, &rc); + if (rc != 0) { + console_printf("invalid 'legacy' parameter\n"); + return rc; + } + + if (params.legacy_pdu) { + params.connectable = 1; + params.scannable = 1; + } + + params.connectable = parse_arg_bool_dflt("connectable", params.connectable, &rc); + if (rc != 0) { + console_printf("invalid 'connectable' parameter\n"); + return rc; + } + + params.scannable = parse_arg_bool_dflt("scannable", params.scannable, &rc); + if (rc != 0) { + console_printf("invalid 'scannable' parameter\n"); + return rc; + } + + params.high_duty_directed = parse_arg_bool_dflt("high_duty", 0, &rc); + if (rc != 0) { + console_printf("invalid 'high_duty' parameter\n"); + return rc; + } + + params.anonymous = parse_arg_bool_dflt("anonymous", 0, &rc); + if (rc != 0) { + console_printf("invalid 'anonymous' parameter\n"); + return rc; + } + + params.include_tx_power = parse_arg_bool_dflt("include_tx_power", 0, &rc); + if (rc != 0) { + console_printf("invalid 'include_tx_power' parameter\n"); + return rc; + } + + params.scan_req_notif = parse_arg_bool_dflt("scan_req_notif", 0, &rc); + if (rc != 0) { + console_printf("invalid 'scan_req_notif' parameter\n"); + return rc; + } + + rc = parse_dev_addr("peer_", cmd_peer_addr_types, ¶ms.peer); + if (rc == 0) { + params.directed = 1; + } else if (rc == ENOENT) { + /* skip, no peer address provided */ + } else { + console_printf("invalid 'peer_addr' parameter\n"); + return rc; + } + + + params.directed = parse_arg_bool_dflt("directed", params.directed, &rc); + if (rc != 0) { + console_printf("invalid 'directed' parameter\n"); + return rc; + } + + if (params.directed && params.legacy_pdu) { + params.scannable = 0; + } + + params.own_addr_type = parse_arg_kv_dflt("own_addr_type", + cmd_own_addr_types, + BLE_OWN_ADDR_PUBLIC, &rc); + if (rc != 0) { + console_printf("invalid 'own_addr_type' parameter\n"); + return rc; + } + + params.channel_map = parse_arg_uint8_dflt("channel_map", 0, &rc); + if (rc != 0) { + console_printf("invalid 'channel_map' parameter\n"); + return rc; + } + + params.filter_policy = parse_arg_kv_dflt("filter", cmd_adv_filt_types, + BLE_HCI_ADV_FILT_NONE, &rc); + if (rc != 0) { + console_printf("invalid 'filter' parameter\n"); + return rc; + } + + params.itvl_min = parse_arg_time_dflt("interval_min", 625, 0, &rc); + if (rc != 0) { + console_printf("invalid 'interval_min' parameter\n"); + return rc; + } + + params.itvl_max = parse_arg_time_dflt("interval_max", 625, 0, &rc); + if (rc != 0) { + console_printf("invalid 'interval_max' parameter\n"); + return rc; + } + + params.tx_power = parse_arg_long_bounds_dflt("tx_power", + -127, 127, 127, &rc); + if (rc != 0) { + console_printf("invalid 'tx_power' parameter\n"); + return rc; + } + + params.primary_phy = parse_arg_kv_dflt("primary_phy", cmd_ext_adv_phy_opts, + 1, &rc); + if (rc != 0) { + console_printf("invalid 'primary_phy' parameter\n"); + return rc; + } + + params.secondary_phy = parse_arg_kv_dflt("secondary_phy", + cmd_ext_adv_phy_opts, + params.primary_phy, &rc); + if (rc != 0) { + console_printf("invalid 'secondary_phy' parameter\n"); + return rc; + } + + params.sid = parse_arg_uint8_dflt("sid", 0, &rc); + if (rc != 0) { + console_printf("invalid 'sid' parameter\n"); + return rc; + } + + rc = btshell_ext_adv_configure(instance, ¶ms, &selected_tx_power); + if (rc) { + console_printf("failed to configure advertising instance\n"); + return rc; + } + + console_printf("Instance %u configured (selected tx power: %d)\n", + instance, selected_tx_power); + + return 0; +} + +static int +cmd_advertise_set_addr(int argc, char **argv) +{ + ble_addr_t addr; + uint8_t instance; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + instance = parse_arg_uint8_dflt("instance", 0, &rc); + if (rc != 0 || instance >= BLE_ADV_INSTANCES) { + console_printf("invalid instance\n"); + return rc; + } + + rc = parse_arg_mac("addr", addr.val); + if (rc != 0) { + console_printf("invalid 'addr' parameter\n"); + return rc; + } + + addr.type = BLE_ADDR_RANDOM; + + rc = ble_gap_ext_adv_set_addr(instance, &addr); + if (rc) { + console_printf("failed to start advertising instance\n"); + return rc; + } + + return 0; +} + +static int +cmd_advertise_start(int argc, char **argv) +{ + int max_events; + uint8_t instance; + int duration; + bool restart; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + instance = parse_arg_uint8_dflt("instance", 0, &rc); + if (rc != 0 || instance >= BLE_ADV_INSTANCES) { + console_printf("invalid instance\n"); + return rc; + } + + duration = parse_arg_uint16_dflt("duration", 0, &rc); + if (rc != 0) { + console_printf("invalid 'duration' parameter\n"); + return rc; + } + + max_events = parse_arg_uint8_dflt("max_events", 0, &rc); + if (rc != 0) { + console_printf("invalid 'max_events' parameter\n"); + return rc; + } + + restart = parse_arg_bool_dflt("restart", 0, &rc); + if (rc != 0) { + console_printf("invalid 'restart' parameter\n"); + return rc; + } + + rc = btshell_ext_adv_start(instance, duration, max_events, restart); + if (rc) { + console_printf("failed to start advertising instance\n"); + return rc; + } + + return 0; +} + +static int +cmd_advertise_stop(int argc, char **argv) +{ + uint8_t instance; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + instance = parse_arg_uint8_dflt("instance", 0, &rc); + if (rc != 0 || instance >= BLE_ADV_INSTANCES) { + console_printf("invalid instance\n"); + return rc; + } + + rc = btshell_ext_adv_stop(instance); + if (rc) { + console_printf("failed to stop advertising instance\n"); + return rc; + } + + return 0; +} + +static int +cmd_advertise_remove(int argc, char **argv) +{ + uint8_t instance; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + instance = parse_arg_uint8_dflt("instance", 0, &rc); + if (rc != 0 || instance >= BLE_ADV_INSTANCES) { + console_printf("invalid instance\n"); + return rc; + } + + rc = ble_gap_ext_adv_remove(instance); + if (rc) { + console_printf("failed to remove advertising instance\n"); + return rc; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param advertise_configure_params[] = { + {"instance", "default: 0"}, + {"connectable", "connectable advertising, usage: =[0-1], default: 0"}, + {"scannable", "scannable advertising, usage: =[0-1], default: 0"}, + {"directed", "directed advertising, usage: =[0-1], default: 0"}, + {"peer_addr_type", "usage: =[public|random|public_id|random_id], default: public"}, + {"peer_addr", "usage: =[XX:XX:XX:XX:XX:XX]"}, + {"own_addr_type", "usage: =[public|random|rpa_pub|rpa_rnd], default: public"}, + {"channel_map", "usage: =[0x00-0xff], default: 0"}, + {"filter", "usage: =[none|scan|conn|both], default: none"}, + {"interval_min", "usage: =[0-UINT32_MAX], default: 0"}, + {"interval_max", "usage: =[0-UINT32_MAX], default: 0"}, + {"tx_power", "usage: =[-127-127], default: 127"}, + {"primary_phy", "usage: =[1M|coded], default: 1M"}, + {"secondary_phy", "usage: =[1M|2M|coded], default: primary_phy"}, + {"sid", "usage: =[0-UINT8_MAX], default: 0"}, + {"high_duty", "usage: =[0-1], default: 0"}, + {"anonymous", "enable anonymous advertising, usage: =[0-1], default: 0"}, + {"legacy", "use legacy PDUs, usage: =[0-1], default: 0"}, + {"include_tx_power", "include TX power in PDU, usage: =[0-1], default: 0"}, + {"scan_req_notif", "enable Scan Request notification usage: =[0-1], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help advertise_configure_help = { + .summary = "configure new advertising instance", + .usage = NULL, + .params = advertise_configure_params, +}; + +static const struct shell_param advertise_set_addr_params[] = { + {"instance", "default: 0"}, + {"addr", "usage: =[XX:XX:XX:XX:XX:XX]"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help advertise_set_addr_help = { + .summary = "set advertising instance random address", + .usage = NULL, + .params = advertise_set_addr_params, +}; + +static const struct shell_param advertise_start_params[] = { + {"instance", "default: 0"}, + {"duration", "advertising duration in 10ms units, default: 0 (forever)"}, + {"max_events", "max number of advertising events, default: 0 (no limit)"}, + {"restart", "restart advertising after disconnect, usage: =[0-1], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help advertise_start_help = { + .summary = "start advertising instance", + .usage = NULL, + .params = advertise_start_params, +}; + +static const struct shell_param advertise_stop_params[] = { + {"instance", "default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help advertise_stop_help = { + .summary = "stop advertising instance", + .usage = NULL, + .params = advertise_stop_params, +}; + +static const struct shell_param advertise_remove_params[] = { + {"instance", "default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help advertise_remove_help = { + .summary = "remove advertising instance", + .usage = NULL, + .params = advertise_remove_params, +}; +#endif + +#else +static const struct kv_pair cmd_adv_conn_modes[] = { + { "non", BLE_GAP_CONN_MODE_NON }, + { "und", BLE_GAP_CONN_MODE_UND }, + { "dir", BLE_GAP_CONN_MODE_DIR }, + { NULL } +}; + +static const struct kv_pair cmd_adv_disc_modes[] = { + { "non", BLE_GAP_DISC_MODE_NON }, + { "ltd", BLE_GAP_DISC_MODE_LTD }, + { "gen", BLE_GAP_DISC_MODE_GEN }, + { NULL } +}; + +static int +cmd_advertise(int argc, char **argv) +{ + struct ble_gap_adv_params params; + int32_t duration_ms; + ble_addr_t peer_addr; + ble_addr_t *peer_addr_param = &peer_addr; + uint8_t own_addr_type; + bool restart; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + if (argc > 1 && strcmp(argv[1], "stop") == 0) { + rc = btshell_adv_stop(); + if (rc != 0) { + console_printf("advertise stop fail: %d\n", rc); + return rc; + } + + return 0; + } + + params.conn_mode = parse_arg_kv_dflt("conn", cmd_adv_conn_modes, + BLE_GAP_CONN_MODE_UND, &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + params.disc_mode = parse_arg_kv_dflt("discov", cmd_adv_disc_modes, + BLE_GAP_DISC_MODE_GEN, &rc); + if (rc != 0) { + console_printf("invalid 'discov' parameter\n"); + return rc; + } + + rc = parse_dev_addr("peer_", cmd_peer_addr_types, &peer_addr); + if (rc == ENOENT) { + peer_addr_param = NULL; + } else if (rc != 0) { + console_printf("invalid 'peer_addr' parameter\n"); + return rc; + } + + restart = parse_arg_bool_dflt("restart", 0, &rc); + if (rc != 0) { + console_printf("invalid 'restart' parameter\n"); + return rc; + } + + own_addr_type = parse_arg_kv_dflt("own_addr_type", cmd_own_addr_types, + BLE_OWN_ADDR_PUBLIC, &rc); + if (rc != 0) { + console_printf("invalid 'own_addr_type' parameter\n"); + return rc; + } + + params.channel_map = parse_arg_uint8_dflt("channel_map", 0, &rc); + if (rc != 0) { + console_printf("invalid 'channel_map' parameter\n"); + return rc; + } + + params.filter_policy = parse_arg_kv_dflt("filter", cmd_adv_filt_types, + BLE_HCI_ADV_FILT_NONE, &rc); + if (rc != 0) { + console_printf("invalid 'filter' parameter\n"); + return rc; + } + + params.itvl_min = parse_arg_time_dflt("interval_min", 625, 0, &rc); + if (rc != 0) { + console_printf("invalid 'interval_min' parameter\n"); + return rc; + } + + params.itvl_max = parse_arg_time_dflt("interval_max", 625, 0, &rc); + if (rc != 0) { + console_printf("invalid 'interval_max' parameter\n"); + return rc; + } + + params.high_duty_cycle = parse_arg_bool_dflt("high_duty", 0, &rc); + if (rc != 0) { + console_printf("invalid 'high_duty' parameter\n"); + return rc; + } + + duration_ms = parse_arg_long_bounds_dflt("duration", 1, INT32_MAX, + BLE_HS_FOREVER, &rc); + if (rc != 0) { + console_printf("invalid 'duration' parameter\n"); + return rc; + } + + rc = btshell_adv_start(own_addr_type, peer_addr_param, duration_ms, + ¶ms, restart); + if (rc != 0) { + console_printf("advertise fail: %d\n", rc); + return rc; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param advertise_params[] = { + {"stop", "stop advertising procedure"}, + {"conn", "connectable mode, usage: =[non|und|dir], default: und"}, + {"discov", "discoverable mode, usage: =[non|ltd|gen], default: gen"}, + {"peer_addr_type", "usage: =[public|random|public_id|random_id], default: public"}, + {"peer_addr", "usage: =[XX:XX:XX:XX:XX:XX]"}, + {"own_addr_type", "usage: =[public|random|rpa_pub|rpa_rnd], default: public"}, + {"channel_map", "usage: =[0x00-0xff], default: 0"}, + {"filter", "usage: =[none|scan|conn|both], default: none"}, + {"interval_min", "usage: =[0-UINT16_MAX], default: 0"}, + {"interval_max", "usage: =[0-UINT16_MAX], default: 0"}, + {"high_duty", "usage: =[0-1], default: 0"}, + {"duration", "usage: =[1-INT32_MAX], default: INT32_MAX"}, + {"restart", "restart advertising after disconnect, usage: =[0-1], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help advertise_help = { + .summary = "start/stop advertising with specific parameters", + .usage = NULL, + .params = advertise_params, +}; +#endif +#endif + +/***************************************************************************** + * $connect * + *****************************************************************************/ + +static struct kv_pair cmd_ext_conn_phy_opts[] = { + { "none", 0x00 }, + { "1M", 0x01 }, + { "coded", 0x02 }, + { "both", 0x03 }, + { "all", 0x04 }, + { NULL } +}; + +static int +cmd_connect(int argc, char **argv) +{ + struct ble_gap_conn_params phy_1M_params = {0}; + struct ble_gap_conn_params phy_coded_params = {0}; + struct ble_gap_conn_params phy_2M_params = {0}; + uint8_t ext; + int32_t duration_ms; + ble_addr_t peer_addr; + ble_addr_t *peer_addr_param = &peer_addr; + int own_addr_type; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + if (argc > 1 && strcmp(argv[1], "cancel") == 0) { + rc = btshell_conn_cancel(); + if (rc != 0) { + console_printf("connection cancel fail: %d\n", rc); + return rc; + } + + return 0; + } + + ext = parse_arg_kv_dflt("extended", cmd_ext_conn_phy_opts, 0, &rc); + if (rc != 0) { + console_printf("invalid 'extended' parameter\n"); + return rc; + } + + rc = parse_dev_addr("peer_", cmd_peer_addr_types, &peer_addr); + if (rc == ENOENT) { + /* With no "peer_addr" specified we'll use white list */ + peer_addr_param = NULL; + } else if (rc != 0) { + console_printf("invalid 'peer_addr' parameter\n"); + return rc; + } + + own_addr_type = parse_arg_kv_dflt("own_addr_type", cmd_own_addr_types, + BLE_OWN_ADDR_PUBLIC, &rc); + if (rc != 0) { + console_printf("invalid 'own_addr_type' parameter\n"); + return rc; + } + + duration_ms = parse_arg_long_bounds_dflt("duration", 1, INT32_MAX, 0, &rc); + if (rc != 0) { + console_printf("invalid 'duration' parameter\n"); + return rc; + } + + phy_1M_params.scan_itvl = parse_arg_time_dflt("scan_interval", 625, 0x0010, &rc); + if (rc != 0) { + console_printf("invalid 'scan_interval' parameter\n"); + return rc; + } + + phy_1M_params.scan_window = parse_arg_time_dflt("scan_window", 625, 0x0010, &rc); + if (rc != 0) { + console_printf("invalid 'scan_window' parameter\n"); + return rc; + } + + phy_1M_params.itvl_min = parse_arg_time_dflt("interval_min", 1250, + BLE_GAP_INITIAL_CONN_ITVL_MIN, + &rc); + if (rc != 0) { + console_printf("invalid 'interval_min' parameter\n"); + return rc; + } + + phy_1M_params.itvl_max = parse_arg_time_dflt("interval_max", 1250, + BLE_GAP_INITIAL_CONN_ITVL_MAX, + &rc); + if (rc != 0) { + console_printf("invalid 'interval_max' parameter\n"); + return rc; + } + + phy_1M_params.latency = parse_arg_uint16_dflt("latency", 0, &rc); + if (rc != 0) { + console_printf("invalid 'latency' parameter\n"); + return rc; + } + + phy_1M_params.supervision_timeout = parse_arg_time_dflt("timeout", 10000, + 0x0100, &rc); + if (rc != 0) { + console_printf("invalid 'timeout' parameter\n"); + return rc; + } + + phy_1M_params.min_ce_len = parse_arg_time_dflt("min_conn_event_len", 625, + 0x0010, &rc); + if (rc != 0) { + console_printf("invalid 'min_conn_event_len' parameter\n"); + return rc; + } + + phy_1M_params.max_ce_len = parse_arg_time_dflt("max_conn_event_len", 625, + 0x0300, &rc); + if (rc != 0) { + console_printf("invalid 'max_conn_event_len' parameter\n"); + return rc; + } + + if (ext == 0x00) { + rc = btshell_conn_initiate(own_addr_type, peer_addr_param, duration_ms, + &phy_1M_params); + if (rc) { + console_printf("error connecting; rc=%d\n", rc); + } + return rc; + } + + if (ext == 0x01) { + rc = btshell_ext_conn_initiate(own_addr_type, peer_addr_param, + duration_ms, &phy_1M_params, + NULL, NULL); + if (rc) { + console_printf("error connecting; rc=%d\n", rc); + } + return rc; + } + + /* Get coded params */ + phy_coded_params.scan_itvl = parse_arg_time_dflt("coded_scan_interval", + 625, 0x0010, &rc); + if (rc != 0) { + console_printf("invalid 'coded_scan_interval' parameter\n"); + return rc; + } + + phy_coded_params.scan_window = parse_arg_time_dflt("coded_scan_window", + 625, 0x0010, &rc); + if (rc != 0) { + console_printf("invalid 'coded_scan_window' parameter\n"); + return rc; + } + + phy_coded_params.itvl_min = parse_arg_time_dflt("coded_interval_min", 1250, + BLE_GAP_INITIAL_CONN_ITVL_MIN, + &rc); + if (rc != 0) { + console_printf("invalid 'coded_interval_min' parameter\n"); + return rc; + } + + phy_coded_params.itvl_max = parse_arg_time_dflt("coded_interval_max", 1250, + BLE_GAP_INITIAL_CONN_ITVL_MAX, + &rc); + if (rc != 0) { + console_printf("invalid 'coded_interval_max' parameter\n"); + return rc; + } + + phy_coded_params.latency = + parse_arg_uint16_dflt("coded_latency", 0, &rc); + if (rc != 0) { + console_printf("invalid 'coded_latency' parameter\n"); + return rc; + } + + phy_coded_params.supervision_timeout = + parse_arg_time_dflt("coded_timeout", 10000, 0x0100, &rc); + + if (rc != 0) { + console_printf("invalid 'coded_timeout' parameter\n"); + return rc; + } + + phy_coded_params.min_ce_len = + parse_arg_time_dflt("coded_min_conn_event", 625, 0x0010, &rc); + if (rc != 0) { + console_printf("invalid 'coded_min_conn_event' parameter\n"); + return rc; + } + + phy_coded_params.max_ce_len = parse_arg_time_dflt("coded_max_conn_event", + 625, 0x0300, &rc); + if (rc != 0) { + console_printf("invalid 'coded_max_conn_event' parameter\n"); + return rc; + } + + /* Get 2M params */ + phy_2M_params.itvl_min = parse_arg_time_dflt("2M_interval_min", 1250, + BLE_GAP_INITIAL_CONN_ITVL_MIN, + &rc); + if (rc != 0) { + console_printf("invalid '2M_interval_min' parameter\n"); + return rc; + } + + phy_2M_params.itvl_max = parse_arg_time_dflt("2M_interval_max", 1250, + BLE_GAP_INITIAL_CONN_ITVL_MAX, &rc); + if (rc != 0) { + console_printf("invalid '2M_interval_max' parameter\n"); + return rc; + } + + phy_2M_params.latency = + parse_arg_uint16_dflt("2M_latency", 0, &rc); + if (rc != 0) { + console_printf("invalid '2M_latency' parameter\n"); + return rc; + } + + phy_2M_params.supervision_timeout = parse_arg_time_dflt("2M_timeout", 10000, + 0x0100, &rc); + + if (rc != 0) { + console_printf("invalid '2M_timeout' parameter\n"); + return rc; + } + + phy_2M_params.min_ce_len = parse_arg_time_dflt("2M_min_conn_event", 625, + 0x0010, &rc); + if (rc != 0) { + console_printf("invalid '2M_min_conn_event' parameter\n"); + return rc; + } + + phy_2M_params.max_ce_len = parse_arg_time_dflt("2M_max_conn_event", 625, + 0x0300, &rc); + if (rc != 0) { + console_printf("invalid '2M_max_conn_event' parameter\n"); + return rc; + } + + if (ext == 0x02) { + rc = btshell_ext_conn_initiate(own_addr_type, peer_addr_param, + duration_ms, NULL, NULL, &phy_coded_params); + return rc; + } + + if (ext == 0x03) { + rc = btshell_ext_conn_initiate(own_addr_type, peer_addr_param, + duration_ms, &phy_1M_params, NULL, + &phy_coded_params); + return rc; + } + + rc = btshell_ext_conn_initiate(own_addr_type, peer_addr_param, + duration_ms, &phy_1M_params, + &phy_2M_params, + &phy_coded_params); + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param connect_params[] = { + {"cancel", "cancel connection procedure"}, + {"extended", "usage: =[none|1M|coded|both|all], default: none"}, + {"peer_addr_type", "usage: =[public|random|public_id|random_id], default: public"}, + {"peer_addr", "usage: =[XX:XX:XX:XX:XX:XX]"}, + {"own_addr_type", "usage: =[public|random|rpa_pub|rpa_rnd], default: public"}, + {"duration", "usage: =[1-INT32_MAX], default: 0"}, + {"scan_interval", "usage: =[0-UINT16_MAX], default: 0x0010"}, + {"scan_window", "usage: =[0-UINT16_MAX], default: 0x0010"}, + {"interval_min", "usage: =[0-UINT16_MAX], default: 30"}, + {"interval_max", "usage: =[0-UINT16_MAX], default: 50"}, + {"latency", "usage: =[UINT16], default: 0"}, + {"timeout", "usage: =[UINT16], default: 0x0100"}, + {"min_conn_event_len", "usage: =[UINT16], default: 0x0010"}, + {"max_conn_event_len", "usage: =[UINT16], default: 0x0300"}, + {"coded_scan_interval", "usage: =[0-UINT16_MAX], default: 0x0010"}, + {"coded_scan_window", "usage: =[0-UINT16_MAX], default: 0x0010"}, + {"coded_interval_min", "usage: =[0-UINT16_MAX], default: 30"}, + {"coded_interval_max", "usage: =[0-UINT16_MAX], default: 50"}, + {"coded_latency", "usage: =[UINT16], default: 0"}, + {"coded_timeout", "usage: =[UINT16], default: 0x0100"}, + {"coded_min_conn_event_len", "usage: =[UINT16], default: 0x0010"}, + {"coded_max_conn_event_len", "usage: =[UINT16], default: 0x0300"}, + {"2M_interval_min", "usage: =[0-UINT16_MAX], default: 30"}, + {"2M_interval_max", "usage: =[0-UINT16_MAX], default: 50"}, + {"2M_latency", "usage: =[UINT16], default: 0"}, + {"2M_timeout", "usage: =[UINT16], default: 0x0100"}, + {"2M_min_conn_event_len", "usage: =[UINT16], default: 0x0010"}, + {"2M_max_conn_event_len", "usage: =[UINT16], default: 0x0300"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help connect_help = { + .summary = "start/stop connection procedure with specific parameters", + .usage = NULL, + .params = connect_params, +}; +#endif + +/***************************************************************************** + * $disconnect * + *****************************************************************************/ + +static int +cmd_disconnect(int argc, char **argv) +{ + uint16_t conn_handle; + uint8_t reason; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + reason = parse_arg_uint8_dflt("reason", BLE_ERR_REM_USER_CONN_TERM, &rc); + if (rc != 0) { + console_printf("invalid 'reason' parameter\n"); + return rc; + } + + rc = btshell_term_conn(conn_handle, reason); + if (rc != 0) { + console_printf("error terminating connection; rc=%d\n", rc); + return rc; + } + + return 0; +} + +static int +cmd_show_conn(int argc, char **argv) +{ + struct ble_gap_conn_desc conn_desc; + struct btshell_conn *conn; + int rc; + int i; + + for (i = 0; i < btshell_num_conns; i++) { + conn = btshell_conns + i; + + rc = ble_gap_conn_find(conn->handle, &conn_desc); + if (rc == 0) { + print_conn_desc(&conn_desc); + } + } + + return 0; +} + +static int +cmd_show_addr(int argc, char **argv) +{ + uint8_t id_addr[6]; + int rc; + + console_printf("public_id_addr="); + rc = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, id_addr, NULL); + if (rc == 0) { + print_addr(id_addr); + } else { + console_printf("none"); + } + + console_printf(" random_id_addr="); + rc = ble_hs_id_copy_addr(BLE_ADDR_RANDOM, id_addr, NULL); + if (rc == 0) { + print_addr(id_addr); + } else { + console_printf("none"); + } + console_printf("\n"); + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param disconnect_params[] = { + {"conn", "connection handle parameter, usage: ="}, + {"reason", "disconnection reason, usage: =[UINT8], default: 19 (remote user terminated connection)"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help disconnect_help = { + .summary = "disconnect command", + .usage = NULL, + .params = disconnect_params, +}; +#endif + +/***************************************************************************** + * $set-scan-opts * + *****************************************************************************/ + +static struct btshell_scan_opts g_scan_opts = { + .limit = UINT16_MAX, + .ignore_legacy = 0, +}; + +static int +cmd_set_scan_opts(int argc, char **argv) +{ + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + g_scan_opts.limit = parse_arg_uint16_dflt("decode_limit", UINT16_MAX, &rc); + if (rc != 0) { + console_printf("invalid 'decode_limit' parameter\n"); + return rc; + } + + g_scan_opts.ignore_legacy = parse_arg_bool_dflt("ignore_legacy", 0, &rc); + if (rc != 0) { + console_printf("invalid 'ignore_legacy' parameter\n"); + return rc; + } + + g_scan_opts.periodic_only = parse_arg_bool_dflt("periodic_only", 0, &rc); + if (rc != 0) { + console_printf("invalid 'periodic_only' parameter\n"); + return rc; + } + + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param set_scan_opts_params[] = { + {"decode_limit", "usage: =[0-UINT16_MAX], default: UINT16_MAX"}, + {"ignore_legacy", "usage: =[0-1], default: 0"}, + {"periodic_only", "usage: =[0-1], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help set_scan_opts_help = { + .summary = "set scan options", + .usage = NULL, + .params = set_scan_opts_params, +}; +#endif + +/***************************************************************************** + * $scan * + *****************************************************************************/ + +static const struct kv_pair cmd_scan_filt_policies[] = { + { "no_wl", BLE_HCI_SCAN_FILT_NO_WL }, + { "use_wl", BLE_HCI_SCAN_FILT_USE_WL }, + { "no_wl_inita", BLE_HCI_SCAN_FILT_NO_WL_INITA }, + { "use_wl_inita", BLE_HCI_SCAN_FILT_USE_WL_INITA }, + { NULL } +}; + +static struct kv_pair cmd_scan_ext_types[] = { + { "none", 0x00 }, + { "1M", 0x01 }, + { "coded", 0x02 }, + { "both", 0x03 }, + { NULL } +}; + +static struct btshell_scan_opts g_scan_opts; + +static int +cmd_scan(int argc, char **argv) +{ + struct ble_gap_disc_params params = {0}; + struct ble_gap_ext_disc_params uncoded = {0}; + struct ble_gap_ext_disc_params coded = {0}; + uint8_t extended; + int32_t duration_ms; + uint8_t own_addr_type; + uint16_t duration; + uint16_t period; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + if (argc > 1 && strcmp(argv[1], "cancel") == 0) { + rc = btshell_scan_cancel(); + if (rc != 0) { + console_printf("scan cancel fail: %d\n", rc); + return rc; + } + return 0; + } + + extended = parse_arg_kv_dflt("extended", cmd_scan_ext_types, 0, &rc); + if (rc != 0) { + console_printf("invalid 'extended' parameter\n"); + return rc; + } + + duration_ms = parse_arg_time_dflt("duration", 10000, BLE_HS_FOREVER, &rc); + if (rc != 0) { + console_printf("invalid 'duration' parameter\n"); + return rc; + } + + params.limited = parse_arg_bool_dflt("limited", 0, &rc); + if (rc != 0) { + console_printf("invalid 'limited' parameter\n"); + return rc; + } + + params.passive = parse_arg_bool_dflt("passive", 0, &rc); + if (rc != 0) { + console_printf("invalid 'passive' parameter\n"); + return rc; + } + + params.itvl = parse_arg_time_dflt("interval", 625, 0, &rc); + if (rc != 0) { + console_printf("invalid 'interval' parameter\n"); + return rc; + } + + params.window = parse_arg_time_dflt("window", 625, 0, &rc); + if (rc != 0) { + console_printf("invalid 'window' parameter\n"); + return rc; + } + + params.filter_policy = parse_arg_kv_dflt("filter", cmd_scan_filt_policies, + BLE_HCI_SCAN_FILT_NO_WL, &rc); + if (rc != 0) { + console_printf("invalid 'filter' parameter\n"); + return rc; + } + + params.filter_duplicates = parse_arg_bool_dflt("nodups", 0, &rc); + if (rc != 0) { + console_printf("invalid 'nodups' parameter\n"); + return rc; + } + + own_addr_type = parse_arg_kv_dflt("own_addr_type", cmd_own_addr_types, + BLE_OWN_ADDR_PUBLIC, &rc); + if (rc != 0) { + console_printf("invalid 'own_addr_type' parameter\n"); + return rc; + } + + if (extended == 0) { + rc = btshell_scan(own_addr_type, duration_ms, ¶ms, &g_scan_opts); + if (rc != 0) { + console_printf("error scanning; rc=%d\n", rc); + return rc; + } + + return 0; + } + + /* Copy above parameters to uncoded params */ + uncoded.passive = params.passive; + uncoded.itvl = params.itvl; + uncoded.window = params.window; + + duration = parse_arg_time_dflt("extended_duration", 10000, 0, &rc); + if (rc != 0) { + console_printf("invalid 'extended_duration' parameter\n"); + return rc; + } + + period = parse_arg_time_dflt("extended_period", 1280000, 0, &rc); + if (rc != 0) { + console_printf("invalid 'extended_period' parameter\n"); + return rc; + } + + coded.itvl = parse_arg_time_dflt("longrange_interval", 625, 0, &rc); + if (rc != 0) { + console_printf("invalid 'longrange_interval' parameter\n"); + return rc; + } + + coded.window = parse_arg_time_dflt("longrange_window", 625, 0, &rc); + if (rc != 0) { + console_printf("invalid 'longrange_window' parameter\n"); + return rc; + } + + coded.passive = parse_arg_uint16_dflt("longrange_passive", 0, &rc); + if (rc != 0) { + console_printf("invalid 'longrange_passive' parameter\n"); + return rc; + } + + switch (extended) { + case 0x01: + rc = btshell_ext_scan(own_addr_type, duration, period, + params.filter_duplicates, params.filter_policy, + params.limited, &uncoded, NULL, + &g_scan_opts); + break; + case 0x02: + rc = btshell_ext_scan(own_addr_type, duration, period, + params.filter_duplicates, params.filter_policy, + params.limited, NULL, &coded, + &g_scan_opts); + break; + case 0x03: + rc = btshell_ext_scan(own_addr_type, duration, period, + params.filter_duplicates, params.filter_policy, + params.limited, &uncoded, &coded, + &g_scan_opts); + break; + default: + assert(0); + break; + } + + if (rc != 0) { + console_printf("error scanning; rc=%d\n", rc); + } + + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param scan_params[] = { + {"cancel", "cancel scan procedure"}, + {"extended", "usage: =[none|1M|coded|both], default: none"}, + {"duration", "usage: =[1-INT32_MAX], default: INT32_MAX"}, + {"limited", "usage: =[0-1], default: 0"}, + {"passive", "usage: =[0-1], default: 0"}, + {"interval", "usage: =[0-UINT16_MAX], default: 0"}, + {"window", "usage: =[0-UINT16_MAX], default: 0"}, + {"filter", "usage: =[no_wl|use_wl|no_wl_inita|use_wl_inita], default: no_wl"}, + {"nodups", "usage: =[0-1], default: 0"}, + {"own_addr_type", "usage: =[public|random|rpa_pub|rpa_rnd], default: public"}, + {"extended_duration", "usage: =[0-UINT16_MAX], default: 0"}, + {"extended_period", "usage: =[0-UINT16_MAX], default: 0"}, + {"longrange_interval", "usage: =[0-UINT16_MAX], default: 0"}, + {"longrange_window", "usage: =[0-UINT16_MAX], default: 0"}, + {"longrange_passive", "usage: =[0-1], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help scan_help = { + .summary = "start/stop scan procedure with specific parameters", + .usage = NULL, + .params = scan_params, +}; +#endif + +/***************************************************************************** + * $set * + *****************************************************************************/ + +static int +cmd_set_addr(void) +{ + ble_addr_t addr; + int rc; + + rc = parse_dev_addr(NULL, cmd_addr_type, &addr); + if (rc != 0) { + console_printf("invalid 'addr' parameter\n"); + return rc; + } + + switch (addr.type) { +#if MYNEWT_VAL(BLE_CONTROLLER) + case BLE_ADDR_PUBLIC: + /* We shouldn't be writing to the controller's address (g_dev_addr). + * There is no standard way to set the local public address, so this is + * our only option at the moment. + */ + memcpy(g_dev_addr, addr.val, 6); + ble_hs_id_set_pub(g_dev_addr); + break; +#endif + + case BLE_ADDR_RANDOM: + rc = ble_hs_id_set_rnd(addr.val); + if (rc != 0) { + return rc; + } + break; + + default: + return BLE_HS_EUNKNOWN; + } + + return 0; +} + +static int +cmd_set(int argc, char **argv) +{ + uint16_t mtu; + uint8_t irk[16]; + int good = 0; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + rc = parse_arg_find_idx("addr"); + if (rc != -1) { + rc = cmd_set_addr(); + if (rc != 0) { + return rc; + } + good = 1; + } + + mtu = parse_arg_uint16("mtu", &rc); + if (rc == 0) { + rc = ble_att_set_preferred_mtu(mtu); + if (rc == 0) { + good = 1; + } + } else if (rc != ENOENT) { + console_printf("invalid 'mtu' parameter\n"); + return rc; + } + + rc = parse_arg_byte_stream_exact_length("irk", irk, 16); + if (rc == 0) { + good = 1; + ble_hs_pvcy_set_our_irk(irk); + } else if (rc != ENOENT) { + console_printf("invalid 'irk' parameter\n"); + return rc; + } + + if (!good) { + console_printf("Error: no valid settings specified\n"); + return -1; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param set_params[] = { + {"addr", "set device address, usage: =[XX:XX:XX:XX:XX:XX]"}, + {"addr_type", "set device address type, usage: =[public|random], default: public"}, + {"mtu", "Maximum Transimssion Unit, usage: =[0-UINT16_MAX]"}, + {"irk", "Identity Resolving Key, usage: =[XX:XX...], len=16 octets"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help set_help = { + .summary = "set device parameters", + .usage = NULL, + .params = set_params, +}; +#endif + +/***************************************************************************** + * $set-adv-data * + *****************************************************************************/ + +#define CMD_ADV_DATA_MAX_UUIDS16 8 +#define CMD_ADV_DATA_MAX_UUIDS32 8 +#define CMD_ADV_DATA_MAX_UUIDS128 2 +#define CMD_ADV_DATA_MAX_PUBLIC_TGT_ADDRS 8 +#define CMD_ADV_DATA_SVC_DATA_UUID16_MAX_LEN BLE_HS_ADV_MAX_FIELD_SZ +#define CMD_ADV_DATA_SVC_DATA_UUID32_MAX_LEN BLE_HS_ADV_MAX_FIELD_SZ +#define CMD_ADV_DATA_SVC_DATA_UUID128_MAX_LEN BLE_HS_ADV_MAX_FIELD_SZ +#define CMD_ADV_DATA_URI_MAX_LEN BLE_HS_ADV_MAX_FIELD_SZ +#define CMD_ADV_DATA_MFG_DATA_MAX_LEN BLE_HS_ADV_MAX_FIELD_SZ + +#if MYNEWT_VAL(BLE_EXT_ADV) +static void +update_pattern(uint8_t *buf, int counter) +{ + int i; + + for (i = 0; i < 10; i += 2) { + counter += 2; + buf[i] = (counter / 1000) << 4 | (counter / 100 % 10); + buf[i + 1] = (counter / 10 % 10) << 4 | (counter % 10); + } +} +#endif + +static int +cmd_set_adv_data_or_scan_rsp(int argc, char **argv, bool scan_rsp, + bool periodic) +{ + static bssnz_t ble_uuid16_t uuids16[CMD_ADV_DATA_MAX_UUIDS16]; + static bssnz_t ble_uuid32_t uuids32[CMD_ADV_DATA_MAX_UUIDS32]; + static bssnz_t ble_uuid128_t uuids128[CMD_ADV_DATA_MAX_UUIDS128]; + static bssnz_t uint8_t + public_tgt_addrs[CMD_ADV_DATA_MAX_PUBLIC_TGT_ADDRS] + [BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN]; + static bssnz_t uint8_t slave_itvl_range[BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN]; + static bssnz_t uint8_t + svc_data_uuid16[CMD_ADV_DATA_SVC_DATA_UUID16_MAX_LEN]; + static bssnz_t uint8_t + svc_data_uuid32[CMD_ADV_DATA_SVC_DATA_UUID32_MAX_LEN]; + static bssnz_t uint8_t + svc_data_uuid128[CMD_ADV_DATA_SVC_DATA_UUID128_MAX_LEN]; + static bssnz_t uint8_t uri[CMD_ADV_DATA_URI_MAX_LEN]; + static bssnz_t uint8_t mfg_data[CMD_ADV_DATA_MFG_DATA_MAX_LEN]; + struct ble_hs_adv_fields adv_fields; + uint32_t uuid32; + uint16_t uuid16; + uint8_t uuid128[16]; + uint8_t public_tgt_addr[BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN]; + uint8_t eddystone_url_body_len; + uint8_t eddystone_url_suffix; + uint8_t eddystone_url_scheme; + int8_t eddystone_measured_power = 0; + char eddystone_url_body[BLE_EDDYSTONE_URL_MAX_LEN]; + char *eddystone_url_full; + int svc_data_uuid16_len; + int svc_data_uuid32_len; + int svc_data_uuid128_len; + int uri_len; + int mfg_data_len; + int tmp; + int rc; +#if MYNEWT_VAL(BLE_EXT_ADV) + uint8_t instance; + uint8_t extra_data[10]; + uint16_t counter; + uint16_t extra_data_len; + struct os_mbuf *adv_data; +#endif + + /* cannot set scan rsp for periodic */ + if (scan_rsp && periodic) { + return -1; + } + + memset(&adv_fields, 0, sizeof adv_fields); + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + +#if MYNEWT_VAL(BLE_EXT_ADV) + instance = parse_arg_uint8_dflt("instance", 0, &rc); + if (rc != 0 || instance >= BLE_ADV_INSTANCES) { + console_printf("invalid instance\n"); + return rc; + } +#endif + + tmp = parse_arg_uint8("flags", &rc); + if (rc == 0) { + adv_fields.flags = tmp; + } else if (rc != ENOENT) { + console_printf("invalid 'flags' parameter\n"); + return rc; + } + + while (1) { + uuid16 = parse_arg_uint16("uuid16", &rc); + if (rc == 0) { + if (adv_fields.num_uuids16 >= CMD_ADV_DATA_MAX_UUIDS16) { + console_printf("invalid 'uuid16' parameter\n"); + return EINVAL; + } + uuids16[adv_fields.num_uuids16] = (ble_uuid16_t) BLE_UUID16_INIT(uuid16); + adv_fields.num_uuids16++; + } else if (rc == ENOENT) { + break; + } else { + console_printf("invalid 'uuid16' parameter\n"); + return rc; + } + } + if (adv_fields.num_uuids16 > 0) { + adv_fields.uuids16 = uuids16; + } + + tmp = parse_arg_bool_dflt("uuids16_is_complete", 0, &rc); + if (rc != 0) { + console_printf("invalid 'uuids16_is_complete' parameter\n"); + return rc; + } + + while (1) { + uuid32 = parse_arg_uint32("uuid32", &rc); + if (rc == 0) { + if (adv_fields.num_uuids32 >= CMD_ADV_DATA_MAX_UUIDS32) { + console_printf("invalid 'uuid32' parameter\n"); + return EINVAL; + } + uuids32[adv_fields.num_uuids32] = (ble_uuid32_t) BLE_UUID32_INIT(uuid32); + adv_fields.num_uuids32++; + } else if (rc == ENOENT) { + break; + } else { + console_printf("invalid 'uuid32' parameter\n"); + return rc; + } + } + if (adv_fields.num_uuids32 > 0) { + adv_fields.uuids32 = uuids32; + } + + tmp = parse_arg_bool_dflt("uuids32_is_complete", 0, &rc); + if (rc != 0) { + console_printf("invalid 'uuids32_is_complete' parameter\n"); + return rc; + } + + while (1) { + rc = parse_arg_byte_stream_exact_length("uuid128", uuid128, 16); + if (rc == 0) { + if (adv_fields.num_uuids128 >= CMD_ADV_DATA_MAX_UUIDS128) { + console_printf("invalid 'uuid128' parameter\n"); + return EINVAL; + } + ble_uuid_init_from_buf((ble_uuid_any_t *) &uuids128[adv_fields.num_uuids128], + uuid128, 16); + adv_fields.num_uuids128++; + } else if (rc == ENOENT) { + break; + } else { + console_printf("invalid 'uuid128' parameter\n"); + return rc; + } + } + if (adv_fields.num_uuids128 > 0) { + adv_fields.uuids128 = uuids128; + } + + tmp = parse_arg_bool_dflt("uuids128_is_complete", 0, &rc); + if (rc != 0) { + console_printf("invalid 'uuids128_is_complete' parameter\n"); + return rc; + } + + adv_fields.name = (uint8_t *)parse_arg_extract("name"); + if (adv_fields.name != NULL) { + adv_fields.name_len = strlen((char *)adv_fields.name); + } + + tmp = parse_arg_long_bounds("tx_power_level", INT8_MIN, INT8_MAX, &rc); + if (rc == 0) { + adv_fields.tx_pwr_lvl = tmp; + adv_fields.tx_pwr_lvl_is_present = 1; + } else if (rc != ENOENT) { + console_printf("invalid 'tx_power_level' parameter\n"); + return rc; + } + + rc = parse_arg_byte_stream_exact_length("slave_interval_range", + slave_itvl_range, + BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN); + if (rc == 0) { + adv_fields.slave_itvl_range = slave_itvl_range; + } else if (rc != ENOENT) { + console_printf("invalid 'slave_interval_range' parameter\n"); + return rc; + } + + rc = parse_arg_byte_stream("service_data_uuid16", + CMD_ADV_DATA_SVC_DATA_UUID16_MAX_LEN, + svc_data_uuid16, &svc_data_uuid16_len); + if (rc == 0) { + adv_fields.svc_data_uuid16 = svc_data_uuid16; + adv_fields.svc_data_uuid16_len = svc_data_uuid16_len; + } else if (rc != ENOENT) { + console_printf("invalid 'service_data_uuid16' parameter\n"); + return rc; + } + + while (1) { + rc = parse_arg_byte_stream_exact_length( + "public_target_address", public_tgt_addr, + BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN); + if (rc == 0) { + if (adv_fields.num_public_tgt_addrs >= + CMD_ADV_DATA_MAX_PUBLIC_TGT_ADDRS) { + + console_printf("invalid 'public_target_address' parameter\n"); + return EINVAL; + } + memcpy(public_tgt_addrs[adv_fields.num_public_tgt_addrs], + public_tgt_addr, BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN); + adv_fields.num_public_tgt_addrs++; + } else if (rc == ENOENT) { + break; + } else { + console_printf("invalid 'public_target_address' parameter\n"); + return rc; + } + } + if (adv_fields.num_public_tgt_addrs > 0) { + adv_fields.public_tgt_addr = (void *)public_tgt_addrs; + } + + adv_fields.appearance = parse_arg_uint16("appearance", &rc); + if (rc == 0) { + adv_fields.appearance_is_present = 1; + } else if (rc != ENOENT) { + console_printf("invalid 'appearance' parameter\n"); + return rc; + } + + adv_fields.adv_itvl = parse_arg_uint16("advertising_interval", &rc); + if (rc == 0) { + adv_fields.adv_itvl_is_present = 1; + } else if (rc != ENOENT) { + console_printf("invalid 'advertising_interval' parameter\n"); + return rc; + } + + rc = parse_arg_byte_stream("service_data_uuid32", + CMD_ADV_DATA_SVC_DATA_UUID32_MAX_LEN, + svc_data_uuid32, &svc_data_uuid32_len); + if (rc == 0) { + adv_fields.svc_data_uuid32 = svc_data_uuid32; + adv_fields.svc_data_uuid32_len = svc_data_uuid32_len; + } else if (rc != ENOENT) { + console_printf("invalid 'service_data_uuid32' parameter\n"); + return rc; + } + + rc = parse_arg_byte_stream("service_data_uuid128", + CMD_ADV_DATA_SVC_DATA_UUID128_MAX_LEN, + svc_data_uuid128, &svc_data_uuid128_len); + if (rc == 0) { + adv_fields.svc_data_uuid128 = svc_data_uuid128; + adv_fields.svc_data_uuid128_len = svc_data_uuid128_len; + } else if (rc != ENOENT) { + console_printf("invalid 'service_data_uuid128' parameter\n"); + return rc; + } + + rc = parse_arg_byte_stream("uri", CMD_ADV_DATA_URI_MAX_LEN, uri, &uri_len); + if (rc == 0) { + adv_fields.uri = uri; + adv_fields.uri_len = uri_len; + } else if (rc != ENOENT) { + console_printf("invalid 'uri' parameter\n"); + return rc; + } + + rc = parse_arg_byte_stream("mfg_data", CMD_ADV_DATA_MFG_DATA_MAX_LEN, + mfg_data, &mfg_data_len); + if (rc == 0) { + adv_fields.mfg_data = mfg_data; + adv_fields.mfg_data_len = mfg_data_len; + } else if (rc != ENOENT) { + console_printf("invalid 'mfg_data' parameter\n"); + return rc; + } + + tmp = parse_arg_long_bounds("eddystone_measured_power", -100, 20, &rc); + if (rc == 0) { + eddystone_measured_power = tmp; + } else if (rc != ENOENT) { + console_printf("invalid 'eddystone_measured_power' parameter\n"); + return rc; + } + + eddystone_url_full = parse_arg_extract("eddystone_url"); + if (eddystone_url_full != NULL) { + rc = parse_eddystone_url(eddystone_url_full, &eddystone_url_scheme, + eddystone_url_body, + &eddystone_url_body_len, + &eddystone_url_suffix); + if (rc != 0) { + goto done; + } + + rc = ble_eddystone_set_adv_data_url(&adv_fields, eddystone_url_scheme, + eddystone_url_body, + eddystone_url_body_len, + eddystone_url_suffix, + eddystone_measured_power); + } else { +#if MYNEWT_VAL(BLE_EXT_ADV) + /* Default to legacy PDUs size, mbuf chain will be increased if needed + */ + adv_data = os_msys_get_pkthdr(BLE_HCI_MAX_ADV_DATA_LEN, 0); + if (!adv_data) { + rc = ENOMEM; + goto done; + } + + rc = ble_hs_adv_set_fields_mbuf(&adv_fields, adv_data); + if (rc) { + os_mbuf_free_chain(adv_data); + goto done; + } + + /* Append some extra data, if requested */ + extra_data_len = parse_arg_uint16("extra_data_len", &rc); + if (rc == 0) { + counter = 0; + extra_data_len = min(extra_data_len, 1650); + while (counter < extra_data_len) { + update_pattern(extra_data, counter); + + rc = os_mbuf_append(adv_data, extra_data, + min(extra_data_len - counter, 10)); + if (rc) { + os_mbuf_free_chain(adv_data); + goto done; + } + + counter += 10; + } + } + + if (scan_rsp) { + rc = ble_gap_ext_adv_rsp_set_data(instance, adv_data); +#if MYNEWT_VAL(BLE_PERIODIC_ADV) + } else if (periodic) { + rc = ble_gap_periodic_adv_set_data(instance, adv_data); +#endif + } else { + rc = ble_gap_ext_adv_set_data(instance, adv_data); + } +#else + if (scan_rsp) { + rc = ble_gap_adv_rsp_set_fields(&adv_fields); + } else { + rc = ble_gap_adv_set_fields(&adv_fields); + } +#endif + } +done: + if (rc != 0) { + console_printf("error setting advertisement data; rc=%d\n", rc); + return rc; + } + + return 0; +} + +static int +cmd_set_adv_data(int argc, char **argv) +{ + return cmd_set_adv_data_or_scan_rsp(argc, argv, false, false); +} + +static int +cmd_set_scan_rsp(int argc, char **argv) +{ + return cmd_set_adv_data_or_scan_rsp(argc, argv, true, false); +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param set_adv_data_params[] = { + {"instance", "default: 0"}, + {"flags", "usage: =[0-UINT8_MAX]"}, + {"uuid16", "usage: =[UINT16]"}, + {"uuid16_is_complete", "usage: =[0-1], default=0"}, + {"uuid32", "usage: =[UINT32]"}, + {"uuid32_is_complete", "usage: =[0-1], default=0"}, + {"uuid128", "usage: =[XX:XX...], len=16 octets"}, + {"uuid128_is_complete", "usage: =[0-1], default=0"}, + {"tx_power_level", "usage: =[INT8_MIN-INT8_MAX]"}, + {"slave_interval_range", "usage: =[XX:XX:XX:XX]"}, + {"public_target_address", "usage: =[XX:XX:XX:XX:XX:XX]"}, + {"appearance", "usage: =[UINT16]"}, + {"name", "usage: =[string]"}, + {"advertising_interval", "usage: =[UINT16]"}, + {"service_data_uuid16", "usage: =[XX:XX...]"}, + {"service_data_uuid32", "usage: =[XX:XX...]"}, + {"service_data_uuid128", "usage: =[XX:XX...]"}, + {"uri", "usage: =[XX:XX...]"}, + {"mfg_data", "usage: =[XX:XX...]"}, + {"measured_power", "usage: =[-100-20]"}, + {"eddystone_url", "usage: =[string]"}, +#if MYNEWT_VAL(BLE_EXT_ADV) + {"extra_data_len", "usage: =[UINT16]"}, +#endif + {NULL, NULL} +}; + +static const struct shell_cmd_help set_adv_data_help = { + .summary = "set advertising data", + .usage = NULL, + .params = set_adv_data_params, +}; + +static const struct shell_cmd_help set_scan_rsp_help = { + .summary = "set scan response", + .usage = NULL, + .params = set_adv_data_params, +}; +#endif + +/***************************************************************************** + * $set-priv-mode * + *****************************************************************************/ + +static int +cmd_set_priv_mode(int argc, char **argv) +{ + ble_addr_t addr; + uint8_t priv_mode; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + rc = parse_dev_addr(NULL, cmd_addr_type, &addr); + if (rc != 0) { + console_printf("invalid 'addr' parameter\n"); + return rc; + } + + priv_mode = parse_arg_uint8("mode", &rc); + if (rc != 0) { + console_printf("missing mode\n"); + return rc; + } + + return ble_gap_set_priv_mode(&addr, priv_mode); +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param set_priv_mode_params[] = { + {"addr", "set priv mode for device address, usage: =[XX:XX:XX:XX:XX:XX]"}, + {"addr_type", "set priv mode for device address type, usage: =[public|random], default: public"}, + {"mode", "set priv mode, usage: =[0-UINT8_MAX]"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help set_priv_mode_help = { + .summary = "set priv mode", + .usage = NULL, + .params = set_priv_mode_params, +}; +#endif + +/***************************************************************************** + * $white-list * + *****************************************************************************/ + +#define CMD_WL_MAX_SZ 8 + +static int +cmd_white_list(int argc, char **argv) +{ + static ble_addr_t addrs[CMD_WL_MAX_SZ]; + int addrs_cnt; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + addrs_cnt = 0; + while (1) { + if (addrs_cnt >= CMD_WL_MAX_SZ) { + return EINVAL; + } + + rc = parse_dev_addr(NULL, cmd_addr_type, &addrs[addrs_cnt]); + if (rc == ENOENT) { + break; + } else if (rc != 0) { + console_printf("invalid 'addr' parameter #%d\n", addrs_cnt + 1); + return rc; + } + + addrs_cnt++; + } + + if (addrs_cnt == 0) { + return EINVAL; + } + + btshell_wl_set(addrs, addrs_cnt); + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param white_list_params[] = { + {"addr", "white-list device addresses, usage: =[XX:XX:XX:XX:XX:XX]"}, + {"addr_type", "white-list address types, usage: =[public|random]"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help white_list_help = { + .summary = "set white-list addresses", + .usage = NULL, + .params = white_list_params, +}; +#endif + +/***************************************************************************** + * $conn-rssi * + *****************************************************************************/ + +static int +cmd_conn_rssi(int argc, char **argv) +{ + uint16_t conn_handle; + int8_t rssi; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + rc = btshell_rssi(conn_handle, &rssi); + if (rc != 0) { + console_printf("error reading rssi; rc=%d\n", rc); + return rc; + } + + console_printf("conn=%d rssi=%d\n", conn_handle, rssi); + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param conn_rssi_params[] = { + {"conn", "connection handle parameter, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help conn_rssi_help = { + .summary = "check connection rssi", + .usage = NULL, + .params = conn_rssi_params, +}; +#endif + +/***************************************************************************** + * $conn-update-params * + *****************************************************************************/ + +static int +cmd_conn_update_params(int argc, char **argv) +{ + struct ble_gap_upd_params params; + uint16_t conn_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + params.itvl_min = parse_arg_time_dflt("interval_min", 1250, + BLE_GAP_INITIAL_CONN_ITVL_MIN, + &rc); + if (rc != 0) { + console_printf("invalid 'interval_min' parameter\n"); + return rc; + } + + params.itvl_max = parse_arg_time_dflt("interval_max", 1250, + BLE_GAP_INITIAL_CONN_ITVL_MAX, + &rc); + if (rc != 0) { + console_printf("invalid 'interval_max' parameter\n"); + return rc; + } + + params.latency = parse_arg_uint16_dflt("latency", 0, &rc); + if (rc != 0) { + console_printf("invalid 'latency' parameter\n"); + return rc; + } + + params.supervision_timeout = parse_arg_time_dflt("timeout", 10000, 0x0100, + &rc); + if (rc != 0) { + console_printf("invalid 'timeout' parameter\n"); + return rc; + } + + params.min_ce_len = parse_arg_time_dflt("min_conn_event_len", 625, + 0x0010, &rc); + if (rc != 0) { + console_printf("invalid 'min_conn_event_len' parameter\n"); + return rc; + } + + params.max_ce_len = parse_arg_time_dflt("max_conn_event_len", 625, + 0x0300, &rc); + if (rc != 0) { + console_printf("invalid 'max_conn_event_len' parameter\n"); + return rc; + } + + rc = btshell_update_conn(conn_handle, ¶ms); + if (rc != 0) { + console_printf("error updating connection; rc=%d\n", rc); + return rc; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param conn_update_params_params[] = { + {"conn", "conn_update_paramsion handle, usage: ="}, + {"interval_min", "usage: =[0-UINT16_MAX], default: 30"}, + {"interval_max", "usage: =[0-UINT16_MAX], default: 50"}, + {"latency", "usage: =[UINT16], default: 0"}, + {"timeout", "usage: =[UINT16], default: 0x0100"}, + {"min_conn_event_len", "usage: =[UINT16], default: 0x0010"}, + {"max_conn_event_len", "usage: =[UINT16], default: 0x0300"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help conn_update_params_help = { + .summary = "update connection parameters", + .usage = "conn_update_params usage", + .params = conn_update_params_params, +}; +#endif + +/***************************************************************************** + * $conn-datalen * + *****************************************************************************/ + +static int +cmd_conn_datalen(int argc, char **argv) +{ + uint16_t conn_handle; + uint16_t tx_octets; + uint16_t tx_time; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + tx_octets = parse_arg_uint16("octets", &rc); + if (rc != 0) { + console_printf("invalid 'octets' parameter\n"); + return rc; + } + + tx_time = parse_arg_uint16("time", &rc); + if (rc != 0) { + console_printf("invalid 'time' parameter\n"); + return rc; + } + + rc = btshell_datalen(conn_handle, tx_octets, tx_time); + if (rc != 0) { + console_printf("error setting data length; rc=%d\n", rc); + return rc; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param conn_datalen_params[] = { + {"conn", "Connection handle, usage: ="}, + {"octets", "Max payload size to include in LL Data PDU, " + "range=<27-251>, usage: ="}, + {"time", "Max number of microseconds the controller should use to tx " + "single LL packet, range=<328-17040>, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help conn_datalen_help = { + .summary = "set data length parameters for connection", + .usage = NULL, + .params = conn_datalen_params, +}; +#endif + +/***************************************************************************** + * keystore * + *****************************************************************************/ + +static const struct kv_pair cmd_keystore_entry_type[] = { + { "msec", BLE_STORE_OBJ_TYPE_PEER_SEC }, + { "ssec", BLE_STORE_OBJ_TYPE_OUR_SEC }, + { "cccd", BLE_STORE_OBJ_TYPE_CCCD }, + { NULL } +}; + +static int +cmd_keystore_parse_keydata(int argc, char **argv, union ble_store_key *out, + int *obj_type) +{ + int rc; + + memset(out, 0, sizeof(*out)); + *obj_type = parse_arg_kv("type", cmd_keystore_entry_type, &rc); + if (rc != 0) { + console_printf("invalid 'type' parameter\n"); + return rc; + } + + switch (*obj_type) { + case BLE_STORE_OBJ_TYPE_PEER_SEC: + case BLE_STORE_OBJ_TYPE_OUR_SEC: + rc = parse_dev_addr(NULL, cmd_addr_type, &out->sec.peer_addr); + if (rc != 0) { + console_printf("invalid 'addr' parameter\n"); + return rc; + } + + out->sec.ediv = parse_arg_uint16("ediv", &rc); + if (rc != 0) { + console_printf("invalid 'ediv' parameter\n"); + return rc; + } + + out->sec.rand_num = parse_arg_uint64("rand", &rc); + if (rc != 0) { + console_printf("invalid 'rand' parameter\n"); + return rc; + } + return 0; + + default: + return EINVAL; + } +} + +static int +cmd_keystore_parse_valuedata(int argc, char **argv, + int obj_type, + union ble_store_key *key, + union ble_store_value *out) +{ + int rc; + int valcnt = 0; + memset(out, 0, sizeof(*out)); + + switch (obj_type) { + case BLE_STORE_OBJ_TYPE_PEER_SEC: + case BLE_STORE_OBJ_TYPE_OUR_SEC: + rc = parse_arg_byte_stream_exact_length("ltk", out->sec.ltk, 16); + if (rc == 0) { + out->sec.ltk_present = 1; + swap_in_place(out->sec.ltk, 16); + valcnt++; + } else if (rc != ENOENT) { + console_printf("invalid 'ltk' parameter\n"); + return rc; + } + rc = parse_arg_byte_stream_exact_length("irk", out->sec.irk, 16); + if (rc == 0) { + out->sec.irk_present = 1; + swap_in_place(out->sec.irk, 16); + valcnt++; + } else if (rc != ENOENT) { + console_printf("invalid 'irk' parameter\n"); + return rc; + } + rc = parse_arg_byte_stream_exact_length("csrk", out->sec.csrk, 16); + if (rc == 0) { + out->sec.csrk_present = 1; + swap_in_place(out->sec.csrk, 16); + valcnt++; + } else if (rc != ENOENT) { + console_printf("invalid 'csrk' parameter\n"); + return rc; + } + out->sec.peer_addr = key->sec.peer_addr; + out->sec.ediv = key->sec.ediv; + out->sec.rand_num = key->sec.rand_num; + break; + } + + if (valcnt) { + return 0; + } + return -1; +} + +/***************************************************************************** + * keystore-add * + *****************************************************************************/ + +static int +cmd_keystore_add(int argc, char **argv) +{ + union ble_store_key key; + union ble_store_value value; + int obj_type; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + rc = cmd_keystore_parse_keydata(argc, argv, &key, &obj_type); + + if (rc) { + return rc; + } + + rc = cmd_keystore_parse_valuedata(argc, argv, obj_type, &key, &value); + + if (rc) { + return rc; + } + + switch(obj_type) { + case BLE_STORE_OBJ_TYPE_PEER_SEC: + rc = ble_store_write_peer_sec(&value.sec); + break; + case BLE_STORE_OBJ_TYPE_OUR_SEC: + rc = ble_store_write_our_sec(&value.sec); + break; + case BLE_STORE_OBJ_TYPE_CCCD: + rc = ble_store_write_cccd(&value.cccd); + break; + default: + rc = ble_store_write(obj_type, &value); + } + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param keystore_add_params[] = { + {"type", "entry type, usage: ="}, + {"addr_type", "usage: ="}, + {"addr", "usage: ="}, + {"ediv", "usage: ="}, + {"rand", "usage: ="}, + {"ltk", "usage: =, len=16 octets"}, + {"irk", "usage: =, len=16 octets"}, + {"csrk", "usage: =, len=16 octets"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help keystore_add_help = { + .summary = "add data to keystore", + .usage = NULL, + .params = keystore_add_params, +}; +#endif + +/***************************************************************************** + * keystore-del * + *****************************************************************************/ + +static int +cmd_keystore_del(int argc, char **argv) +{ + union ble_store_key key; + int obj_type; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + rc = cmd_keystore_parse_keydata(argc, argv, &key, &obj_type); + + if (rc) { + return rc; + } + rc = ble_store_delete(obj_type, &key); + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param keystore_del_params[] = { + {"type", "entry type, usage: ="}, + {"addr_type", "usage: ="}, + {"addr", "usage: ="}, + {"ediv", "usage: ="}, + {"rand", "usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help keystore_del_help = { + .summary = "remove data from keystore", + .usage = NULL, + .params = keystore_del_params, +}; +#endif + +/***************************************************************************** + * keystore-show * + *****************************************************************************/ + +static int +cmd_keystore_iterator(int obj_type, + union ble_store_value *val, + void *cookie) { + + switch (obj_type) { + case BLE_STORE_OBJ_TYPE_PEER_SEC: + case BLE_STORE_OBJ_TYPE_OUR_SEC: + console_printf("Key: "); + if (ble_addr_cmp(&val->sec.peer_addr, BLE_ADDR_ANY) == 0) { + console_printf("ediv=%u ", val->sec.ediv); + console_printf("ediv=%llu ", val->sec.rand_num); + } else { + console_printf("addr_type=%u ", val->sec.peer_addr.type); + print_addr(val->sec.peer_addr.val); + } + console_printf("\n"); + + if (val->sec.ltk_present) { + console_printf(" LTK: "); + print_bytes(val->sec.ltk, 16); + console_printf("\n"); + } + if (val->sec.irk_present) { + console_printf(" IRK: "); + print_bytes(val->sec.irk, 16); + console_printf("\n"); + } + if (val->sec.csrk_present) { + console_printf(" CSRK: "); + print_bytes(val->sec.csrk, 16); + console_printf("\n"); + } + break; + case BLE_STORE_OBJ_TYPE_CCCD: + console_printf("Key: "); + console_printf("addr_type=%u ", val->cccd.peer_addr.type); + print_addr(val->cccd.peer_addr.val); + console_printf("\n"); + + console_printf(" char_val_handle: %d\n", val->cccd.chr_val_handle); + console_printf(" flags: 0x%02x\n", val->cccd.flags); + console_printf(" changed: %d\n", val->cccd.value_changed); + break; + } + return 0; +} + +static int +cmd_keystore_show(int argc, char **argv) +{ + int type; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + type = parse_arg_kv("type", cmd_keystore_entry_type, &rc); + if (rc != 0) { + console_printf("invalid 'type' parameter\n"); + return rc; + } + + ble_store_iterate(type, &cmd_keystore_iterator, NULL); + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param keystore_show_params[] = { + {"type", "entry type, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help keystore_show_help = { + .summary = "show data in keystore", + .usage = NULL, + .params = keystore_show_params, +}; +#endif + +#if NIMBLE_BLE_SM +/***************************************************************************** + * $show-oob-sc * + *****************************************************************************/ + +extern struct ble_sm_sc_oob_data oob_data_local; +extern struct ble_sm_sc_oob_data oob_data_remote; + +static int +cmd_show_oob_sc(int argc, char **argv) +{ + console_printf("Local OOB Data: r="); + print_bytes(oob_data_local.r, 16); + console_printf(" c="); + print_bytes(oob_data_local.c, 16); + console_printf("\n"); + + console_printf("Remote OOB Data: r="); + print_bytes(oob_data_remote.r, 16); + console_printf(" c="); + print_bytes(oob_data_remote.c, 16); + console_printf("\n"); + + return 0; +} + +/***************************************************************************** + * $auth-passkey * + *****************************************************************************/ + +static int +cmd_auth_passkey(int argc, char **argv) +{ + uint16_t conn_handle; + struct ble_sm_io pk; + char *yesno; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + pk.action = parse_arg_uint16("action", &rc); + if (rc != 0) { + console_printf("invalid 'action' parameter\n"); + return rc; + } + + switch (pk.action) { + case BLE_SM_IOACT_INPUT: + case BLE_SM_IOACT_DISP: + /* passkey is 6 digit number */ + pk.passkey = parse_arg_long_bounds("key", 0, 999999, &rc); + if (rc != 0) { + console_printf("invalid 'key' parameter\n"); + return rc; + } + break; + + case BLE_SM_IOACT_OOB: + rc = parse_arg_byte_stream_exact_length("oob", pk.oob, 16); + if (rc != 0) { + console_printf("invalid 'oob' parameter\n"); + return rc; + } + break; + + case BLE_SM_IOACT_NUMCMP: + yesno = parse_arg_extract("yesno"); + if (yesno == NULL) { + console_printf("invalid 'yesno' parameter\n"); + return EINVAL; + } + + switch (yesno[0]) { + case 'y': + case 'Y': + pk.numcmp_accept = 1; + break; + case 'n': + case 'N': + pk.numcmp_accept = 0; + break; + + default: + console_printf("invalid 'yesno' parameter\n"); + return EINVAL; + } + break; + + case BLE_SM_IOACT_OOB_SC: + rc = parse_arg_byte_stream_exact_length("r", oob_data_remote.r, 16); + if (rc != 0 && rc != ENOENT) { + console_printf("invalid 'r' parameter\n"); + return rc; + } + + rc = parse_arg_byte_stream_exact_length("c", oob_data_remote.c, 16); + if (rc != 0 && rc != ENOENT) { + console_printf("invalid 'c' parameter\n"); + return rc; + } + pk.oob_sc_data.local = &oob_data_local; + if (ble_hs_cfg.sm_oob_data_flag) { + pk.oob_sc_data.remote = &oob_data_remote; + } else { + pk.oob_sc_data.remote = NULL; + } + break; + + default: + console_printf("invalid passkey action action=%d\n", pk.action); + return EINVAL; + } + + rc = ble_sm_inject_io(conn_handle, &pk); + if (rc != 0) { + console_printf("error providing passkey; rc=%d\n", rc); + return rc; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param auth_passkey_params[] = { + {"conn", "connection handle, usage: ="}, + {"action", "auth action type, usage: ="}, + {"key", "usage: =[0-999999]"}, + {"oob", "usage: =[XX:XX...], len=16 octets"}, + {"yesno", "usage: =[string]"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help auth_passkey_help = { + .summary = "set authorization passkey options", + .usage = NULL, + .params = auth_passkey_params, +}; +#endif + +/***************************************************************************** + * $security-pair * + *****************************************************************************/ + +static int +cmd_security_pair(int argc, char **argv) +{ + uint16_t conn_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + rc = btshell_sec_pair(conn_handle); + if (rc != 0) { + console_printf("error initiating pairing; rc=%d\n", rc); + return rc; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param security_pair_params[] = { + {"conn", "connection handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help security_pair_help = { + .summary = "start pairing procedure for connection", + .usage = NULL, + .params = security_pair_params, +}; +#endif + +/***************************************************************************** + * $security-unpair * + *****************************************************************************/ + +static int +cmd_security_unpair(int argc, char **argv) +{ + ble_addr_t peer; + int rc; + int oldest; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + rc = parse_arg_bool_dflt("oldest", 0, &oldest); + if (rc != 0) { + console_printf("invalid 'oldest' parameter\n"); + return rc; + } + + if (oldest) { + rc = ble_gap_unpair_oldest_peer(); + console_printf("Unpair oldest status: 0x%02x\n", rc); + return 0; + } + + rc = parse_dev_addr("peer_", cmd_peer_addr_types, &peer); + if (rc != 0) { + console_printf("invalid 'peer_addr' parameter\n"); + return rc; + } + + rc = ble_gap_unpair(&peer); + if (rc != 0) { + console_printf("error unpairing; rc=%d\n", rc); + return rc; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param security_unpair_params[] = { + {"oldest", "usage: =[true|false], default: false"}, + {"peer_addr_type", "usage: =[public|random|public_id|random_id], default: public"}, + {"peer_addr", "usage: =[XX:XX:XX:XX:XX:XX]"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help security_unpair_help = { + .summary = "unpair a peer device", + .usage = NULL, + .params = security_unpair_params, +}; +#endif + +/***************************************************************************** + * $security-start * + *****************************************************************************/ + +static int +cmd_security_start(int argc, char **argv) +{ + uint16_t conn_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + rc = btshell_sec_start(conn_handle); + if (rc != 0) { + console_printf("error starting security; rc=%d\n", rc); + return rc; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param security_start_params[] = { + {"conn", "connection handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help security_start_help = { + .summary = "start security procedure for connection", + .usage = NULL, + .params = security_start_params, +}; +#endif + +/***************************************************************************** + * $security-encryption * + *****************************************************************************/ + +static int +cmd_security_encryption(int argc, char **argv) +{ + uint16_t conn_handle; + uint16_t ediv; + uint64_t rand_val; + uint8_t ltk[16]; + uint8_t key_size; + int rc; + int auth; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + ediv = parse_arg_uint16("ediv", &rc); + if (rc == ENOENT) { + rc = btshell_sec_restart(conn_handle, 0, NULL, 0, 0, 0); + } else { + rand_val = parse_arg_uint64("rand", &rc); + if (rc != 0) { + console_printf("invalid 'rand' parameter\n"); + return rc; + } + + auth = parse_arg_bool("auth", &rc); + if (rc != 0) { + console_printf("invalid 'auth' parameter\n"); + return rc; + } + + key_size = parse_arg_uint8("key_size", &rc); + if (rc != 0) { + console_printf("invalid 'key_size' parameter\n"); + return rc; + } + + rc = parse_arg_byte_stream_exact_length("ltk", ltk, 16); + if (rc != 0) { + console_printf("invalid 'ltk' parameter\n"); + return rc; + } + + rc = btshell_sec_restart(conn_handle, key_size, + ltk, ediv, rand_val, auth); + } + + if (rc != 0) { + console_printf("error initiating encryption; rc=%d\n", rc); + return rc; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param security_encryption_params[] = { + {"conn", "connection handle, usage: ="}, + {"ediv", "usage: =[UINT16]"}, + {"rand", "usage: =[UINT64]"}, + {"auth", "usage: =[0-1]"}, + {"ltk", "usage: =[XX:XX...], len=16 octets"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help security_encryption_help = { + .summary = "start encryption procedure for connection", + .usage = NULL, + .params = security_encryption_params, +}; +#endif + +/***************************************************************************** + * $security-set-data * + *****************************************************************************/ + +static int +cmd_security_set_data(int argc, char **argv) +{ + uint8_t tmp; + int good; + int rc; + + good = 0; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + tmp = parse_arg_bool("oob_flag", &rc); + if (rc == 0) { + ble_hs_cfg.sm_oob_data_flag = tmp; + good++; + } else if (rc != ENOENT) { + console_printf("invalid 'oob_flag' parameter\n"); + return rc; + } + + tmp = parse_arg_bool("mitm_flag", &rc); + if (rc == 0) { + good++; + ble_hs_cfg.sm_mitm = tmp; + } else if (rc != ENOENT) { + console_printf("invalid 'mitm_flag' parameter\n"); + return rc; + } + + tmp = parse_arg_uint8("io_capabilities", &rc); + if (rc == 0) { + good++; + ble_hs_cfg.sm_io_cap = tmp; + } else if (rc != ENOENT) { + console_printf("invalid 'io_capabilities' parameter\n"); + return rc; + } + + tmp = parse_arg_uint8("our_key_dist", &rc); + if (rc == 0) { + good++; + ble_hs_cfg.sm_our_key_dist = tmp; + } else if (rc != ENOENT) { + console_printf("invalid 'our_key_dist' parameter\n"); + return rc; + } + + tmp = parse_arg_uint8("their_key_dist", &rc); + if (rc == 0) { + good++; + ble_hs_cfg.sm_their_key_dist = tmp; + } else if (rc != ENOENT) { + console_printf("invalid 'their_key_dist' parameter\n"); + return rc; + } + + tmp = parse_arg_bool("bonding", &rc); + if (rc == 0) { + good++; + ble_hs_cfg.sm_bonding = tmp; + } else if (rc != ENOENT) { + console_printf("invalid 'bonding' parameter\n"); + return rc; + } + + tmp = parse_arg_bool("sc", &rc); + if (rc == 0) { + good++; + ble_hs_cfg.sm_sc = tmp; + } else if (rc != ENOENT) { + console_printf("invalid 'sc' parameter\n"); + return rc; + } + + if (!good) { + console_printf("Error: no valid settings specified\n"); + return -1; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param security_set_data_params[] = { + {"oob_flag", "usage: =[0-1]"}, + {"mitm_flag", "usage: =[0-1]"}, + {"io_capabilities", "usage: =[UINT8]"}, + {"our_key_dist", "usage: =[UINT8]"}, + {"their_key_dist", "usage: =[UINT8]"}, + {"bonding", "usage: =[0-1]"}, + {"sc", "usage: =[0-1]"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help security_set_data_help = { + .summary = "set security data", + .usage = NULL, + .params = security_set_data_params, +}; +#endif +#endif + +/***************************************************************************** + * $test-tx * + * * + * Command to transmit 'num' packets of size 'len' at rate 'r' to + * handle 'h' Note that length must be <= 251. The rate is in msecs. + * + *****************************************************************************/ + +static int +cmd_test_tx(int argc, char **argv) +{ + int rc; + uint16_t conn; + uint16_t len; + uint16_t rate; + uint16_t num; + uint8_t stop; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + stop = parse_arg_uint8_dflt("stop", 0, &rc); + if (rc != 0) { + console_printf("invalid 'stop' parameter\n"); + return rc; + } + + if (stop) { + btshell_tx_stop(); + return 0; + } + + conn = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + len = parse_arg_uint16("length", &rc); + if (rc != 0) { + console_printf("invalid 'length' parameter\n"); + return rc; + } + if ((len > 251) || (len < 4)) { + console_printf("error: len must be between 4 and 251, inclusive"); + } + + rate = parse_arg_uint16_dflt("rate", 1, &rc); + if (rc != 0) { + console_printf("invalid 'rate' parameter\n"); + return rc; + } + + num = parse_arg_uint16_dflt("num", 1, &rc); + if (rc != 0) { + console_printf("invalid 'num' parameter\n"); + return rc; + } + + rc = btshell_tx_start(conn, len, rate, num); + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param test_tx_params[] = { + {"conn", "handle to tx to, usage: ="}, + {"length", "size of packet, usage: ="}, + {"rate", "rate of tx, usage: =, default=1"}, + {"num", "number of packets, usage: =, default=1"}, + {"stop", "stop sending, usage: 1 to stop, default 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help test_tx_help = { + .summary = "test packet transmission", + .usage = NULL, + .params = test_tx_params, +}; +#endif + +/***************************************************************************** + * $phy-set * + *****************************************************************************/ + +static int +cmd_phy_set(int argc, char **argv) +{ + uint16_t conn; + uint8_t tx_phys_mask; + uint8_t rx_phys_mask; + uint16_t phy_opts; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + tx_phys_mask = parse_arg_uint8("tx_phys_mask", &rc); + if (rc != 0) { + console_printf("invalid 'tx_phys_mask' parameter\n"); + return rc; + } + + rx_phys_mask = parse_arg_uint8("rx_phys_mask", &rc); + if (rc != 0) { + console_printf("invalid 'rx_phys_mask' parameter\n"); + return rc; + } + + phy_opts = parse_arg_uint16("phy_opts", &rc); + if (rc != 0) { + console_printf("invalid 'phy_opts' parameter\n"); + return rc; + } + + return ble_gap_set_prefered_le_phy(conn, tx_phys_mask, rx_phys_mask, + phy_opts); +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param phy_set_params[] = { + {"conn", "connection handle, usage: ="}, + {"tx_phys_mask", "usage: ="}, + {"rx_phys_mask", "usage: ="}, + {"phy_opts", "usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help phy_set_help = { + .summary = "set preferred PHYs", + .usage = NULL, + .params = phy_set_params, +}; +#endif + +/***************************************************************************** + * $phy-set-default * + *****************************************************************************/ + +static int +cmd_phy_set_default(int argc, char **argv) +{ + uint8_t tx_phys_mask; + uint8_t rx_phys_mask; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + tx_phys_mask = parse_arg_uint8("tx_phys_mask", &rc); + if (rc != 0) { + console_printf("invalid 'tx_phys_mask' parameter\n"); + return rc; + } + + rx_phys_mask = parse_arg_uint8("rx_phys_mask", &rc); + if (rc != 0) { + console_printf("invalid 'rx_phys_mask' parameter\n"); + return rc; + } + + return ble_gap_set_prefered_default_le_phy(tx_phys_mask, rx_phys_mask); +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param phy_set_default_params[] = { + {"tx_phys_mask", "usage: ="}, + {"rx_phys_mask", "usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help phy_set_default_help = { + .summary = "set preferred default PHYs", + .usage = NULL, + .params = phy_set_default_params, +}; +#endif + +/***************************************************************************** + * $phy-read * + *****************************************************************************/ + +static int +cmd_phy_read(int argc, char **argv) +{ + uint16_t conn = 0; + uint8_t tx_phy; + uint8_t rx_phy; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + rc = ble_gap_read_le_phy(conn, &tx_phy, &rx_phy); + if (rc != 0) { + console_printf("Could not read PHY error: %d\n", rc); + return rc; + } + + console_printf("TX_PHY: %d\n", tx_phy); + console_printf("RX_PHY: %d\n", tx_phy); + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param phy_read_params[] = { + {"conn", "connection handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help phy_read_help = { + .summary = "read PHYs", + .usage = NULL, + .params = phy_read_params, +}; +#endif + +/***************************************************************************** + * $host-enable * + *****************************************************************************/ + +static int +cmd_host_enable(int argc, char **argv) +{ + int rc; + + rc = gatt_svr_init(); + assert(rc == 0); + + ble_hs_sched_start(); + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_cmd_help host_enable_help = { + .summary = "start the NimBLE host", + .usage = NULL, + .params = NULL, +}; +#endif + +/***************************************************************************** + * $host-disable * + *****************************************************************************/ + +static void +on_stop(int status, void *arg) +{ + if (status == 0) { + console_printf("host stopped\n"); + } else { + console_printf("host failed to stop; rc=%d\n", status); + } +} + +static int +cmd_host_disable(int argc, char **argv) +{ + static struct ble_hs_stop_listener listener; + int rc; + + rc = ble_hs_stop(&listener, on_stop, NULL); + if (rc) { + return rc; + } + + ble_gatts_reset(); + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_cmd_help host_disable_help = { + .summary = "stop the NimBLE host", + .usage = NULL, + .params = NULL, +}; + +/***************************************************************************** + * $gatt-discover * + *****************************************************************************/ + +static const struct shell_param gatt_discover_characteristic_params[] = { + {"conn", "connection handle, usage: ="}, + {"uuid", "discover by uuid, usage: =[UUID]"}, + {"start", "start handle, usage: ="}, + {"end", "end handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_discover_characteristic_help = { + .summary = "perform characteristic discovery procedure", + .usage = NULL, + .params = gatt_discover_characteristic_params, +}; + +static const struct shell_param gatt_discover_descriptor_params[] = { + {"conn", "connection handle, usage: ="}, + {"start", "start handle, usage: ="}, + {"end", "end handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_discover_descriptor_help = { + .summary = "perform descriptor discovery procedure", + .usage = NULL, + .params = gatt_discover_descriptor_params, +}; + +static const struct shell_param gatt_discover_service_params[] = { + {"conn", "connection handle, usage: ="}, + {"uuid", "discover by uuid, usage: =[UUID]"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_discover_service_help = { + .summary = "perform service discovery procedure", + .usage = NULL, + .params = gatt_discover_service_params, +}; + +static const struct shell_param gatt_discover_full_params[] = { + {"conn", "connection handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_discover_full_help = { + .summary = "perform full discovery procedure", + .usage = NULL, + .params = gatt_discover_full_params, +}; + +/***************************************************************************** + * $gatt-exchange-mtu * + *****************************************************************************/ + +static const struct shell_param gatt_exchange_mtu_params[] = { + {"conn", "connection handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_exchange_mtu_help = { + .summary = "perform mtu exchange procedure", + .usage = NULL, + .params = gatt_exchange_mtu_params, +}; + +/***************************************************************************** + * $gatt-find-included-services * + *****************************************************************************/ + +static const struct shell_param gatt_find_included_services_params[] = { + {"conn", "connection handle, usage: ="}, + {"start", "start handle, usage: ="}, + {"end", "end handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_find_included_services_help = { + .summary = "perform find included services procedure", + .usage = NULL, + .params = gatt_find_included_services_params, +}; + +/***************************************************************************** + * $gatt-notify * + *****************************************************************************/ + +static const struct shell_param gatt_notify_params[] = { + {"attr", "attribute handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_notify_help = { + .summary = "notify about attribute value changed", + .usage = NULL, + .params = gatt_notify_params, +}; + +/***************************************************************************** + * $gatt-read * + *****************************************************************************/ + +static const struct shell_param gatt_read_params[] = { + {"conn", "connection handle, usage: ="}, + {"long", "is read long, usage: =[0-1], default=0"}, + {"attr", "attribute handle, usage: ="}, + {"offset", "offset value, usage: ="}, + {"uuid", "read by uuid, usage: =[UUID]"}, + {"start", "start handle, usage: ="}, + {"end", "end handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_read_help = { + .summary = "perform gatt read procedure", + .usage = NULL, + .params = gatt_read_params, +}; + +/***************************************************************************** + * $gatt-service-changed * + *****************************************************************************/ + +static const struct shell_param gatt_service_changed_params[] = { + {"start", "start handle, usage: ="}, + {"end", "end handle, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_service_changed_help = { + .summary = "send service changed indication", + .usage = NULL, + .params = gatt_service_changed_params, +}; + +/***************************************************************************** + * $gatt-service-visibility * + *****************************************************************************/ + +static const struct shell_param gatt_service_visibility_params[] = { + {"handle", "usage: ="}, + {"visibility", "usage: =<0-1>"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_service_visibility_help = { + .summary = "change service visibility", + .usage = NULL, + .params = gatt_service_visibility_params, +}; + +/***************************************************************************** + * $gatt-show * + *****************************************************************************/ + +static const struct shell_param gatt_show_params[] = { + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_show_help = { + .summary = "show discovered gatt database", + .usage = NULL, + .params = gatt_show_params, +}; + +static const struct shell_cmd_help gatt_show_local_help = { + .summary = "show local gatt database", + .usage = NULL, + .params = gatt_show_params, +}; + +static const struct shell_cmd_help gatt_show_addr_help = { + .summary = "show device address", + .usage = NULL, + .params = gatt_show_params, +}; + +static const struct shell_cmd_help gatt_show_conn_help = { + .summary = "show connections information", + .usage = NULL, + .params = gatt_show_params, +}; + +/***************************************************************************** + * $gatt-write * + *****************************************************************************/ + +static const struct shell_param gatt_write_params[] = { + {"conn", "connection handle, usage: ="}, + {"no_rsp", "write without response, usage: =[0-1], default=0"}, + {"long", "is write long, usage: =[0-1], default=0"}, + {"attr", "attribute handle, usage: ="}, + {"offset", "attribute handle, usage: ="}, + {"value", "usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help gatt_write_help = { + .summary = "perform gatt write procedure", + .usage = NULL, + .params = gatt_write_params, +}; +#endif + +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) +#if MYNEWT_VAL(SHELL_CMD_HELP) +/***************************************************************************** + * $l2cap-update * + *****************************************************************************/ + +static const struct shell_param l2cap_update_params[] = { + {"conn", "connection handle, usage: ="}, + {"interval_min", "usage: =[0-UINT16_MAX], default: 30"}, + {"interval_max", "usage: =[0-UINT16_MAX], default: 50"}, + {"latency", "usage: =[UINT16], default: 0"}, + {"timeout", "usage: =[UINT16], default: 0x0100"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help l2cap_update_help = { + .summary = "update l2cap parameters for connection", + .usage = NULL, + .params = l2cap_update_params, +}; + +/***************************************************************************** + * $l2cap-create-server * + *****************************************************************************/ + +static const struct shell_param l2cap_create_server_params[] = { + {"psm", "usage: ="}, + {"mtu", "usage: = not more than BTSHELL_COC_MTU, default BTSHELL_COC_MTU"}, + {"error", "usage: used for PTS testing:"}, + {"", "0 - always accept"}, + {"", "1 - reject with insufficient authentication"}, + {"", "2 - reject with insufficient authorization"}, + {"", "3 - reject with insufficient key size"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help l2cap_create_server_help = { + .summary = "create l2cap server", + .usage = NULL, + .params = l2cap_create_server_params, +}; + +/***************************************************************************** + * $l2cap-connect * + *****************************************************************************/ + +static const struct shell_param l2cap_connect_params[] = { + {"conn", "connection handle, usage: ="}, + {"psm", "usage: ="}, + {"num", "usage: number of connection created in a row: [1-5]"}, + {"mtu", "usage: = not more than BTSHELL_COC_MTU, default BTSHELL_COC_MTU"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help l2cap_connect_help = { + .summary = "perform l2cap connect procedure", + .usage = NULL, + .params = l2cap_connect_params, +}; + +/***************************************************************************** + * $l2cap-disconnect * + *****************************************************************************/ + +static const struct shell_param l2cap_disconnect_params[] = { + {"conn", "connection handle, usage: ="}, + {"idx", "usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help l2cap_disconnect_help = { + .summary = "perform l2cap disconnect procedure", + .usage = "use gatt-show-coc to get the parameters", + .params = l2cap_disconnect_params, +}; + +/***************************************************************************** + * $l2cap-reconfig * + *****************************************************************************/ + +static const struct shell_param l2cap_reconfig_params[] = { + {"conn", "connection handle, usage: ="}, + {"mtu", "new mtu, usage: =, default: 0 (no change)"}, + {"idxs", "list of channel indexes, usage: idxs=1,3"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help l2cap_reconfig_help = { + .summary = "perform l2cap reconfigure procedure", + .usage = "use gatt-show-coc to get the parameters", + .params = l2cap_reconfig_params, +}; + +/***************************************************************************** + * $l2cap-send * + *****************************************************************************/ + +static const struct shell_param l2cap_send_params[] = { + {"conn", "connection handle, usage: ="}, + {"idx", "usage: ="}, + {"bytes", "number of bytes to send, usage: ="}, + {NULL, NULL} +}; + +static const struct shell_cmd_help l2cap_send_help = { + .summary = "perform l2cap send procedure", + .usage = "use l2cap-show-coc to get the parameters", + .params = l2cap_send_params, +}; + +/***************************************************************************** + * $l2cap-show-coc * + *****************************************************************************/ + +static const struct shell_param l2cap_show_coc_params[] = { + {NULL, NULL} +}; + +static const struct shell_cmd_help l2cap_show_coc_help = { + .summary = "show coc information", + .usage = NULL, + .params = l2cap_show_coc_params, +}; + +#endif +#endif + +#if MYNEWT_VAL(BLE_PERIODIC_ADV) +static int +cmd_periodic_configure(int argc, char **argv) +{ + struct ble_gap_periodic_adv_params params = {0}; + uint8_t instance; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + instance = parse_arg_uint8_dflt("instance", 0, &rc); + if (rc != 0 || instance >= BLE_ADV_INSTANCES) { + console_printf("invalid instance\n"); + return rc; + } + + memset(¶ms, 0, sizeof(params)); + + params.include_tx_power = parse_arg_bool_dflt("include_tx_power", 0, &rc); + if (rc != 0) { + console_printf("invalid 'include_tx_power' parameter\n"); + return rc; + } + + params.itvl_min = parse_arg_time_dflt("interval_min", 1250, 0, &rc); + if (rc != 0) { + console_printf("invalid 'interval_min' parameter\n"); + return rc; + } + + params.itvl_max = parse_arg_time_dflt("interval_max", 1250, params.itvl_min, + &rc); + if (rc != 0) { + console_printf("invalid 'interval_max' parameter\n"); + return rc; + } + + rc = ble_gap_periodic_adv_configure(instance, ¶ms); + if (rc) { + console_printf("failed to configure periodic advertising\n"); + return rc; + } + + console_printf("Instance %u configured for periodic advertising\n", + instance); + + return 0; +} + +static int +cmd_periodic_set_adv_data(int argc, char **argv) +{ + return cmd_set_adv_data_or_scan_rsp(argc, argv, false, true); +} + +static int +cmd_periodic_start(int argc, char **argv) +{ + uint8_t instance; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + instance = parse_arg_uint8_dflt("instance", 0, &rc); + if (rc != 0 || instance >= BLE_ADV_INSTANCES) { + console_printf("invalid instance\n"); + return rc; + } + + rc = ble_gap_periodic_adv_start(instance); + if (rc) { + console_printf("failed to start periodic advertising\n"); + return rc; + } + + return 0; +} + +static int +cmd_periodic_stop(int argc, char **argv) +{ + uint8_t instance; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + instance = parse_arg_uint8_dflt("instance", 0, &rc); + if (rc != 0 || instance >= BLE_ADV_INSTANCES) { + console_printf("invalid instance\n"); + return rc; + } + + rc = ble_gap_periodic_adv_stop(instance); + if (rc) { + console_printf("failed to stop periodic advertising\n"); + return rc; + } + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param periodic_configure_params[] = { + {"instance", "default: 0"}, + {"interval_min", "usage: =[0-UINT32_MAX], default: 0"}, + {"interval_max", "usage: =[0-UINT32_MAX], default: interval_min"}, + {"tx_power", "include TX power, usage: =[0-1], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help periodic_configure_help = { + .summary = "configure periodic advertising for instance", + .usage = NULL, + .params = periodic_configure_params, +}; + +static const struct shell_param periodic_start_params[] = { + {"instance", "default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help periodic_start_help = { + .summary = "start periodic advertising for instance", + .usage = NULL, + .params = periodic_start_params, +}; + +static const struct shell_param periodic_stop_params[] = { + {"instance", "default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help periodic_stop_help = { + .summary = "stop periodic advertising for instance", + .usage = NULL, + .params = periodic_stop_params, +}; +#endif + +static int +cmd_sync_create(int argc, char **argv) +{ + struct ble_gap_periodic_sync_params params = { 0 }; + ble_addr_t addr; + ble_addr_t *addr_param = &addr; + uint8_t sid; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + if (argc > 1 && strcmp(argv[1], "cancel") == 0) { + rc = ble_gap_periodic_adv_sync_create_cancel(); + if (rc != 0) { + console_printf("Sync create cancel fail: %d\n", rc); + return rc; + } + + return 0; + } + + rc = parse_dev_addr("peer_", cmd_addr_type, &addr); + if (rc == ENOENT) { + /* With no "peer_addr" specified we'll use periodic list */ + addr_param = NULL; + } else if (rc != 0) { + console_printf("invalid 'addr' parameter\n"); + return rc; + } + + sid = parse_arg_uint8_dflt("sid", 0, &rc); + if (rc != 0) { + console_printf("invalid 'sid' parameter\n"); + return rc; + } + + params.skip = parse_arg_uint16_dflt("skip", 0, &rc); + if (rc != 0) { + console_printf("invalid 'skip' parameter\n"); + return rc; + } + + params.sync_timeout = parse_arg_time_dflt("sync_timeout", 10000, 2000, &rc); + if (rc != 0) { + console_printf("invalid 'sync_timeout' parameter\n"); + return rc; + } + + params.reports_disabled = parse_arg_bool_dflt("reports_disabled", false, &rc); + if (rc != 0) { + console_printf("invalid 'reports_disabled' parameter\n"); + return rc; + } + + rc = ble_gap_periodic_adv_sync_create(addr_param, sid, ¶ms, + btshell_gap_event, NULL); + if (rc) { + console_printf("Failed to create sync (%d)\n", rc); + } + + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param sync_create_params[] = { + {"cancel", "cancel periodic sync establishment procedure"}, + {"peer_addr_type", "usage: =[public|random], default: public"}, + {"peer_addr", "usage: =[XX:XX:XX:XX:XX:XX]"}, + {"sid", "usage: =[UINT8], default: 0"}, + {"skip", "usage: =[0-0x01F3], default: 0x0000"}, + {"sync_timeout", "usage: =[0x000A-0x4000], default: 0x07D0"}, + {"reports_disabled", "disable reports, usage: =[0-1], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help sync_create_help = { + .summary = "start/stop periodic sync procedure with specific parameters", + .usage = NULL, + .params = sync_create_params, +}; +#endif + +#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_TRANSFER) +static int +cmd_sync_transfer(int argc, char **argv) +{ + uint16_t service_data; + uint16_t conn_handle; + uint16_t sync_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + sync_handle = parse_arg_uint16_dflt("sync_handle", 0, &rc); + if (rc != 0) { + console_printf("invalid 'sync_handle' parameter\n"); + return rc; + } + + service_data = parse_arg_uint16_dflt("service_data", 0, &rc); + if (rc != 0) { + console_printf("invalid 'service_data' parameter\n"); + return rc; + } + + rc = ble_gap_periodic_adv_sync_transfer(sync_handle, conn_handle, + service_data); + if (rc) { + console_printf("Failed to transfer sync (%d)\n", rc); + } + + return rc; +} + +static int +cmd_sync_reporting(int argc, char **argv) +{ + uint16_t sync_handle; + bool enable; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + sync_handle = parse_arg_uint16_dflt("sync_handle", 0, &rc); + if (rc != 0) { + console_printf("invalid 'sync_handle' parameter\n"); + return rc; + } + + enable = parse_arg_bool_dflt("enabled", 0, &rc); + if (rc != 0) { + console_printf("invalid 'enabled' parameter\n"); + return rc; + } + + rc = ble_gap_periodic_adv_sync_reporting(sync_handle, enable); + if (rc) { + console_printf("Failed to %s reporting (%d)\n", + enable ? "enable" : "disable", rc); + } + + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param sync_transfer_params[] = { + {"conn", "connection handle, usage: ="}, + {"sync_handle", "sync handle, usage: =[UINT16], default: 0"}, + {"service_data", "service data, usage: =[UINT16], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help sync_transfer_help = { + .summary = "start periodic sync transfer procedure with specific parameters", + .usage = NULL, + .params = sync_transfer_params, +}; + +static const struct shell_param sync_reporting_params[] = { + {"sync_handle", "sync handle, usage: =[UINT16], default: 0"}, + {"enabled", "toggle reporting, usage: =[0-1], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help sync_reporting_help = { + .summary = "configure periodic advertising sync reporting", + .usage = NULL, + .params = sync_reporting_params, +}; +#endif + +static int +cmd_sync_transfer_set_info(int argc, char **argv) +{ + uint16_t service_data; + uint16_t conn_handle; + uint8_t instance; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + instance = parse_arg_uint8_dflt("instance", 0, &rc); + if (rc != 0) { + console_printf("invalid 'instance' parameter\n"); + return rc; + } + + service_data = parse_arg_uint16_dflt("service_data", 0, &rc); + if (rc != 0) { + console_printf("invalid 'service_data' parameter\n"); + return rc; + } + + rc = ble_gap_periodic_adv_sync_set_info(instance, conn_handle, + service_data); + if (rc) { + console_printf("Failed to transfer sync (%d)\n", rc); + } + + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param sync_transfer_set_info_params[] = { + {"conn", "connection handle, usage: ="}, + {"instance", "advertising instance, usage: =[UINT8], default: 0"}, + {"service_data", "service data, usage: =[UINT16], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help sync_transfer_set_info_help = { + .summary = "start periodic sync transfer procedure with specific parameters", + .usage = NULL, + .params = sync_transfer_set_info_params, +}; +#endif + +static int +cmd_sync_transfer_receive(int argc, char **argv) +{ + struct ble_gap_periodic_sync_params params = { 0 }; + uint16_t conn_handle; + bool disable; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + disable = parse_arg_bool_dflt("disable", false, &rc); + if (rc != 0) { + console_printf("invalid 'disable' parameter\n"); + return rc; + } + + if (disable) { + rc = ble_gap_periodic_adv_sync_receive(conn_handle, NULL, NULL, NULL); + if (rc) { + console_printf("Failed to disable sync transfer reception (%d)\n", rc); + } + + return rc; + } + + params.skip = parse_arg_uint16_dflt("skip", 0, &rc); + if (rc != 0) { + console_printf("invalid 'skip' parameter\n"); + return rc; + } + + params.sync_timeout = parse_arg_time_dflt("sync_timeout", 10000, 10, &rc); + if (rc != 0) { + console_printf("invalid 'sync_timeout' parameter\n"); + return rc; + } + + params.reports_disabled = parse_arg_bool_dflt("reports_disabled", false, &rc); + if (rc != 0) { + console_printf("invalid 'reports_disabled' parameter\n"); + return rc; + } + + rc = ble_gap_periodic_adv_sync_receive(conn_handle, ¶ms, btshell_gap_event, + NULL); + if (rc) { + console_printf("Failed to enable sync transfer reception (%d)\n", rc); + } + + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param sync_transfer_receive_params[] = { + {"conn", "connection handle, usage: ="}, + {"disable", "disable transfer reception, usage: =[0-1], default: 0"}, + {"skip", "usage: =[0-0x01F3], default: 0x0000"}, + {"sync_timeout", "usage: =[0x000A-0x4000], default: 0x000A"}, + {"reports_disabled", "disable reports, usage: =[0-1], default: 0"}, + {NULL, NULL} +}; +#endif + +static const struct shell_cmd_help sync_transfer_receive_help = { + .summary = "start/stop periodic sync reception with specific parameters", + .usage = NULL, + .params = sync_transfer_receive_params, +}; +#endif + +static int +cmd_sync_terminate(int argc, char **argv) +{ + uint16_t sync_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + sync_handle = parse_arg_uint16_dflt("sync_handle", 0, &rc); + if (rc != 0) { + console_printf("invalid 'sync_handle' parameter\n"); + return rc; + } + + rc = ble_gap_periodic_adv_sync_terminate(sync_handle); + if (rc) { + console_printf("Failed to terminate sync (%d)\n", rc); + } + + return rc; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param sync_terminate_params[] = { + {"sync_handle", "usage: =[UINT16], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help sync_terminate_help = { + .summary = "terminate periodic sync", + .usage = NULL, + .params = sync_terminate_params, +}; +#endif + +static int +cmd_sync_stats(int argc, char **argv) +{ + uint16_t sync_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + sync_handle = parse_arg_uint16_dflt("sync_handle", 0, &rc); + if (rc != 0) { + console_printf("invalid 'sync_handle' parameter\n"); + return rc; + } + + btshell_sync_stats(sync_handle); + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param sync_stats_params[] = { + {"sync_handle", "usage: =[UINT16], default: 0"}, + {NULL, NULL} +}; + +static const struct shell_cmd_help sync_stats_help = { + .summary = "show sync stats", + .usage = NULL, + .params = sync_stats_params, +}; +#endif +#endif + +static const struct shell_cmd btshell_commands[] = { +#if MYNEWT_VAL(BLE_EXT_ADV) + { + .sc_cmd = "advertise-configure", + .sc_cmd_func = cmd_advertise_configure, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &advertise_configure_help, +#endif + }, + { + .sc_cmd = "advertise-set-addr", + .sc_cmd_func = cmd_advertise_set_addr, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &advertise_set_addr_help, +#endif + }, + { + .sc_cmd = "advertise-set-adv-data", + .sc_cmd_func = cmd_set_adv_data, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &set_adv_data_help, +#endif + }, + { + .sc_cmd = "advertise-set-scan-rsp", + .sc_cmd_func = cmd_set_scan_rsp, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &set_scan_rsp_help, +#endif + }, + { + .sc_cmd = "advertise-start", + .sc_cmd_func = cmd_advertise_start, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &advertise_start_help, +#endif + }, + { + .sc_cmd = "advertise-stop", + .sc_cmd_func = cmd_advertise_stop, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &advertise_stop_help, +#endif + }, + { + .sc_cmd = "advertise-remove", + .sc_cmd_func = cmd_advertise_remove, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &advertise_remove_help, +#endif + }, +#else + { + .sc_cmd = "advertise", + .sc_cmd_func = cmd_advertise, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &advertise_help, +#endif + }, +#endif + { + .sc_cmd = "connect", + .sc_cmd_func = cmd_connect, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &connect_help, +#endif + }, + { + .sc_cmd = "disconnect", + .sc_cmd_func = cmd_disconnect, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &disconnect_help, +#endif + }, + { + .sc_cmd = "show-addr", + .sc_cmd_func = cmd_show_addr, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_show_addr_help, +#endif + }, + { + .sc_cmd = "show-conn", + .sc_cmd_func = cmd_show_conn, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_show_conn_help, +#endif + }, + { + .sc_cmd = "set-scan-opts", + .sc_cmd_func = cmd_set_scan_opts, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &set_scan_opts_help, +#endif + }, + { + .sc_cmd = "scan", + .sc_cmd_func = cmd_scan, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &scan_help, +#endif + }, + { + .sc_cmd = "set", + .sc_cmd_func = cmd_set, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &set_help, +#endif + }, +#if !MYNEWT_VAL(BLE_EXT_ADV) + { + .sc_cmd = "set-adv-data", + .sc_cmd_func = cmd_set_adv_data, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &set_adv_data_help, +#endif + }, + { + .sc_cmd = "set-scan-rsp", + .sc_cmd_func = cmd_set_scan_rsp, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &set_scan_rsp_help, +#endif + }, +#endif + { + .sc_cmd = "set-priv-mode", + .sc_cmd_func = cmd_set_priv_mode, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &set_priv_mode_help, +#endif + }, + { + .sc_cmd = "white-list", + .sc_cmd_func = cmd_white_list, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &white_list_help, +#endif + }, + { + .sc_cmd = "conn-rssi", + .sc_cmd_func = cmd_conn_rssi, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &conn_rssi_help, +#endif + }, + { + .sc_cmd = "conn-update-params", + .sc_cmd_func = cmd_conn_update_params, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &conn_update_params_help, +#endif + }, + { + .sc_cmd = "conn-datalen", + .sc_cmd_func = cmd_conn_datalen, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &conn_datalen_help, +#endif + }, + { + .sc_cmd = "gatt-discover-characteristic", + .sc_cmd_func = cmd_gatt_discover_characteristic, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_discover_characteristic_help, +#endif + }, + { + .sc_cmd = "gatt-discover-descriptor", + .sc_cmd_func = cmd_gatt_discover_descriptor, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_discover_descriptor_help, +#endif + }, + { + .sc_cmd = "gatt-discover-service", + .sc_cmd_func = cmd_gatt_discover_service, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_discover_service_help, +#endif + }, + { + .sc_cmd = "gatt-discover-full", + .sc_cmd_func = cmd_gatt_discover_full, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_discover_full_help, +#endif + }, + { + .sc_cmd = "gatt-find-included-services", + .sc_cmd_func = cmd_gatt_find_included_services, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_find_included_services_help, +#endif + }, + { + .sc_cmd = "gatt-exchange-mtu", + .sc_cmd_func = cmd_gatt_exchange_mtu, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_exchange_mtu_help, +#endif + }, + { + .sc_cmd = "gatt-read", + .sc_cmd_func = cmd_gatt_read, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_read_help, +#endif + }, + { + .sc_cmd = "gatt-notify", + .sc_cmd_func = cmd_gatt_notify, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_notify_help, +#endif + }, + { + .sc_cmd = "gatt-service-changed", + .sc_cmd_func = cmd_gatt_service_changed, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_service_changed_help, +#endif + }, + { + .sc_cmd = "gatt-service-visibility", + .sc_cmd_func = cmd_gatt_service_visibility, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_service_visibility_help, +#endif + }, + { + .sc_cmd = "gatt-show", + .sc_cmd_func = cmd_gatt_show, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_show_help, +#endif + }, + { + .sc_cmd = "gatt-show-local", + .sc_cmd_func = cmd_gatt_show_local, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_show_local_help, +#endif + }, + { + .sc_cmd = "gatt-write", + .sc_cmd_func = cmd_gatt_write, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &gatt_write_help, +#endif + }, +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) + { + .sc_cmd = "l2cap-update", + .sc_cmd_func = cmd_l2cap_update, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &l2cap_update_help, +#endif + }, + { + .sc_cmd = "l2cap-create-server", + .sc_cmd_func = cmd_l2cap_create_server, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &l2cap_create_server_help, +#endif + }, + { + .sc_cmd = "l2cap-connect", + .sc_cmd_func = cmd_l2cap_connect, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &l2cap_connect_help, +#endif + }, + { + .sc_cmd = "l2cap-reconfig", + .sc_cmd_func = cmd_l2cap_reconfig, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &l2cap_reconfig_help, +#endif + }, + { + .sc_cmd = "l2cap-disconnect", + .sc_cmd_func = cmd_l2cap_disconnect, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &l2cap_disconnect_help, +#endif + }, + { + .sc_cmd = "l2cap-send", + .sc_cmd_func = cmd_l2cap_send, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &l2cap_send_help, +#endif + }, + { + .sc_cmd = "l2cap-show-coc", + .sc_cmd_func = cmd_l2cap_show_coc, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &l2cap_show_coc_help, +#endif + }, +#endif + { + .sc_cmd = "keystore-add", + .sc_cmd_func = cmd_keystore_add, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &keystore_add_help, +#endif + }, + { + .sc_cmd = "keystore-del", + .sc_cmd_func = cmd_keystore_del, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &keystore_del_help, +#endif + }, + { + .sc_cmd = "keystore-show", + .sc_cmd_func = cmd_keystore_show, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &keystore_show_help, +#endif + }, +#if NIMBLE_BLE_SM + { + .sc_cmd = "show-oob-sc", + .sc_cmd_func = cmd_show_oob_sc, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = NULL, +#endif + }, + { + .sc_cmd = "auth-passkey", + .sc_cmd_func = cmd_auth_passkey, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &auth_passkey_help, +#endif + }, + { + .sc_cmd = "security-pair", + .sc_cmd_func = cmd_security_pair, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &security_pair_help, +#endif + }, + { + .sc_cmd = "security-unpair", + .sc_cmd_func = cmd_security_unpair, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &security_unpair_help, +#endif + }, + { + .sc_cmd = "security-start", + .sc_cmd_func = cmd_security_start, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &security_start_help, +#endif + }, + { + .sc_cmd = "security-encryption", + .sc_cmd_func = cmd_security_encryption, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &security_encryption_help, +#endif + }, + { + .sc_cmd = "security-set-data", + .sc_cmd_func = cmd_security_set_data, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &security_set_data_help, +#endif + }, +#endif + { + .sc_cmd = "test-tx", + .sc_cmd_func = cmd_test_tx, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &test_tx_help, +#endif + }, + { + .sc_cmd = "phy-set", + .sc_cmd_func = cmd_phy_set, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &phy_set_help, +#endif + }, + { + .sc_cmd = "phy-set-default", + .sc_cmd_func = cmd_phy_set_default, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &phy_set_default_help, +#endif + }, + { + .sc_cmd = "phy-read", + .sc_cmd_func = cmd_phy_read, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &phy_read_help, +#endif + }, + { + .sc_cmd = "host-enable", + .sc_cmd_func = cmd_host_enable, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &host_enable_help, +#endif + }, + { + .sc_cmd = "host-disable", + .sc_cmd_func = cmd_host_disable, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &host_disable_help, +#endif + }, +#if MYNEWT_VAL(BLE_PERIODIC_ADV) + { + .sc_cmd = "periodic-configure", + .sc_cmd_func = cmd_periodic_configure, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &periodic_configure_help, +#endif + }, + { + .sc_cmd = "periodic-set-adv-data", + .sc_cmd_func = cmd_periodic_set_adv_data, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &set_adv_data_help, +#endif + }, + { + .sc_cmd = "periodic-start", + .sc_cmd_func = cmd_periodic_start, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &periodic_start_help, +#endif + }, + { + .sc_cmd = "periodic-stop", + .sc_cmd_func = cmd_periodic_stop, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &periodic_stop_help, +#endif + }, + { + .sc_cmd = "sync-create", + .sc_cmd_func = cmd_sync_create, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &sync_create_help, +#endif + }, + { + .sc_cmd = "sync-terminate", + .sc_cmd_func = cmd_sync_terminate, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &sync_terminate_help, +#endif + }, + { + .sc_cmd = "sync-stats", + .sc_cmd_func = cmd_sync_stats, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &sync_stats_help, +#endif + }, +#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_TRANSFER) + { + .sc_cmd = "sync-transfer", + .sc_cmd_func = cmd_sync_transfer, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &sync_transfer_help, +#endif + }, + { + .sc_cmd = "sync-transfer-set-info", + .sc_cmd_func = cmd_sync_transfer_set_info, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &sync_transfer_set_info_help, +#endif + }, + { + .sc_cmd = "sync-transfer-receive", + .sc_cmd_func = cmd_sync_transfer_receive, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &sync_transfer_receive_help, +#endif + }, + { + .sc_cmd = "sync-reporting", + .sc_cmd_func = cmd_sync_reporting, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &sync_reporting_help, +#endif + }, +#endif +#endif + { 0 }, +}; + + +void +cmd_init(void) +{ + shell_register(BTSHELL_MODULE, btshell_commands); + shell_register_default_module(BTSHELL_MODULE); +} diff --git a/src/libs/mynewt-nimble/apps/btshell/src/cmd.h b/src/libs/mynewt-nimble/apps/btshell/src/cmd.h new file mode 100644 index 00000000..63bd50d1 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/btshell/src/cmd.h @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef CMD_H +#define CMD_H + +#include +#include "host/ble_uuid.h" + +struct kv_pair { + char *key; + int val; +}; + +uint32_t parse_arg_time_dflt(char *name, int step, uint32_t dflt, int *out_status); +const struct kv_pair *parse_kv_find(const struct kv_pair *kvs, char *name); +int parse_arg_find_idx(const char *key); +char *parse_arg_extract(const char *key); +long parse_arg_long_bounds(char *name, long min, long max, int *out_status); +long parse_arg_long_bounds_dflt(char *name, long min, long max, + long dflt, int *out_status); +uint64_t parse_arg_uint64_bounds(char *name, uint64_t min, + uint64_t max, int *out_status); +long parse_arg_long(char *name, int *staus); +uint8_t parse_arg_bool(char *name, int *status); +uint8_t parse_arg_bool_dflt(char *name, uint8_t dflt, int *out_status); +uint8_t parse_arg_uint8(char *name, int *status); +uint8_t parse_arg_uint8_dflt(char *name, uint8_t dflt, int *out_status); +uint16_t parse_arg_uint16(char *name, int *status); +uint16_t parse_arg_uint16_dflt(char *name, uint16_t dflt, int *out_status); +uint32_t parse_arg_uint32(char *name, int *out_status); +uint32_t parse_arg_uint32_dflt(char *name, uint32_t dflt, int *out_status); +uint64_t parse_arg_uint64(char *name, int *out_status); +int parse_arg_kv(char *name, const struct kv_pair *kvs, int *out_status); +int parse_arg_kv_dflt(char *name, const struct kv_pair *kvs, int def_val, + int *out_status); +int parse_arg_byte_stream(char *name, int max_len, uint8_t *dst, int *out_len); +int parse_arg_uint8_list_with_separator(char *name, char *separator, int max_len, + uint8_t *dst, int *out_len); +int parse_arg_byte_stream_exact_length(char *name, uint8_t *dst, int len); +int parse_arg_mac(char *name, uint8_t *dst); +int parse_arg_addr(char *name, ble_addr_t *addr); +int parse_arg_uuid(char *name, ble_uuid_any_t *uuid); +int parse_arg_all(int argc, char **argv); +int parse_eddystone_url(char *full_url, uint8_t *out_scheme, char *out_body, + uint8_t *out_body_len, uint8_t *out_suffix); +int cmd_parse_conn_start_end(uint16_t *out_conn, uint16_t *out_start, + uint16_t *out_end); + +void cmd_init(void); + +#endif diff --git a/src/libs/mynewt-nimble/apps/btshell/src/cmd_gatt.c b/src/libs/mynewt-nimble/apps/btshell/src/cmd_gatt.c new file mode 100644 index 00000000..ba3799e5 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/btshell/src/cmd_gatt.c @@ -0,0 +1,587 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include + +#include "bsp/bsp.h" +#include "host/ble_hs_mbuf.h" +#include "host/ble_gap.h" +#include "services/gatt/ble_svc_gatt.h" +#include "console/console.h" +#include "btshell.h" +#include "cmd.h" +#include "cmd_gatt.h" + +#define CMD_BUF_SZ 256 +static bssnz_t uint8_t cmd_buf[CMD_BUF_SZ]; + +/***************************************************************************** + * $gatt-discover * + *****************************************************************************/ + +int +cmd_gatt_discover_characteristic(int argc, char **argv) +{ + uint16_t start_handle; + uint16_t conn_handle; + uint16_t end_handle; + ble_uuid_any_t uuid; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle); + if (rc != 0) { + console_printf("invalid 'conn start end' parameter\n"); + return rc; + } + + rc = parse_arg_uuid("uuid", &uuid); + if (rc == 0) { + rc = btshell_disc_chrs_by_uuid(conn_handle, start_handle, end_handle, + &uuid.u); + } else if (rc == ENOENT) { + rc = btshell_disc_all_chrs(conn_handle, start_handle, end_handle); + } else { + console_printf("invalid 'uuid' parameter\n"); + return rc; + } + if (rc != 0) { + console_printf("error discovering characteristics; rc=%d\n", rc); + return rc; + } + + return 0; +} + +int +cmd_gatt_discover_descriptor(int argc, char **argv) +{ + uint16_t start_handle; + uint16_t conn_handle; + uint16_t end_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle); + if (rc != 0) { + console_printf("invalid 'conn start end' parameter\n"); + return rc; + } + + rc = btshell_disc_all_dscs(conn_handle, start_handle, end_handle); + if (rc != 0) { + console_printf("error discovering descriptors; rc=%d\n", rc); + return rc; + } + + return 0; +} + +int +cmd_gatt_discover_service(int argc, char **argv) +{ + ble_uuid_any_t uuid; + int conn_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + rc = parse_arg_uuid("uuid", &uuid); + if (rc == 0) { + rc = btshell_disc_svc_by_uuid(conn_handle, &uuid.u); + } else if (rc == ENOENT) { + rc = btshell_disc_svcs(conn_handle); + } else { + console_printf("invalid 'uuid' parameter\n"); + return rc; + } + + if (rc != 0) { + console_printf("error discovering services; rc=%d\n", rc); + return rc; + } + + return 0; +} + +int +cmd_gatt_discover_full(int argc, char **argv) +{ + int conn_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + rc = btshell_disc_full(conn_handle); + if (rc != 0) { + console_printf("error discovering all; rc=%d\n", rc); + return rc; + } + + return 0; +} + +/***************************************************************************** + * $gatt-exchange-mtu * + *****************************************************************************/ + +int +cmd_gatt_exchange_mtu(int argc, char **argv) +{ + uint16_t conn_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + rc = btshell_exchange_mtu(conn_handle); + if (rc != 0) { + console_printf("error exchanging mtu; rc=%d\n", rc); + return rc; + } + + return 0; +} + +/***************************************************************************** + * $gatt-notify * + *****************************************************************************/ + +int +cmd_gatt_notify(int argc, char **argv) +{ + uint16_t attr_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + attr_handle = parse_arg_uint16("attr", &rc); + if (rc != 0) { + console_printf("invalid 'attr' parameter\n"); + return rc; + } + + btshell_notify(attr_handle); + + return 0; +} + +/***************************************************************************** + * $gatt-read * + *****************************************************************************/ + +#define CMD_READ_MAX_ATTRS 8 + +int +cmd_gatt_read(int argc, char **argv) +{ + static uint16_t attr_handles[CMD_READ_MAX_ATTRS]; + uint16_t conn_handle; + uint16_t start; + uint16_t end; + uint16_t offset; + ble_uuid_any_t uuid; + uint8_t num_attr_handles; + int is_uuid; + int is_long; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + is_long = parse_arg_long("long", &rc); + if (rc == ENOENT) { + is_long = 0; + } else if (rc != 0) { + console_printf("invalid 'long' parameter\n"); + return rc; + } + + for (num_attr_handles = 0; + num_attr_handles < CMD_READ_MAX_ATTRS; + num_attr_handles++) { + + attr_handles[num_attr_handles] = parse_arg_uint16("attr", &rc); + if (rc == ENOENT) { + break; + } else if (rc != 0) { + console_printf("invalid 'attr' parameter\n"); + return rc; + } + } + + rc = parse_arg_uuid("uuid", &uuid); + if (rc == ENOENT) { + is_uuid = 0; + } else if (rc == 0) { + is_uuid = 1; + } else { + console_printf("invalid 'uuid' parameter\n"); + return rc; + } + + start = parse_arg_uint16("start", &rc); + if (rc == ENOENT) { + start = 0; + } else if (rc != 0) { + console_printf("invalid 'start' parameter\n"); + return rc; + } + + end = parse_arg_uint16("end", &rc); + if (rc == ENOENT) { + end = 0; + } else if (rc != 0) { + console_printf("invalid 'end' parameter\n"); + return rc; + } + + offset = parse_arg_uint16("offset", &rc); + if (rc == ENOENT) { + offset = 0; + } else if (rc != 0) { + console_printf("invalid 'offset' parameter\n"); + return rc; + } + + if (num_attr_handles == 1) { + if (is_long) { + rc = btshell_read_long(conn_handle, attr_handles[0], offset); + } else { + rc = btshell_read(conn_handle, attr_handles[0]); + } + } else if (num_attr_handles > 1) { + rc = btshell_read_mult(conn_handle, attr_handles, num_attr_handles); + } else if (is_uuid) { + if (start == 0 || end == 0) { + rc = EINVAL; + } else { + rc = btshell_read_by_uuid(conn_handle, start, end, &uuid.u); + } + } else { + rc = EINVAL; + } + + if (rc != 0) { + console_printf("error reading characteristic; rc=%d\n", rc); + return rc; + } + + return 0; +} + + +/***************************************************************************** + * $gatt-service-changed * + *****************************************************************************/ + +int +cmd_gatt_service_changed(int argc, char **argv) +{ + uint16_t start; + uint16_t end; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + start = parse_arg_uint16("start", &rc); + if (rc != 0) { + console_printf("invalid 'start' parameter\n"); + return rc; + } + + end = parse_arg_uint16("end", &rc); + if (rc != 0) { + console_printf("invalid 'end' parameter\n"); + return rc; + } + + ble_svc_gatt_changed(start, end); + + return 0; +} + +/***************************************************************************** + * $gatt-service-visibility * + *****************************************************************************/ + +int +cmd_gatt_service_visibility(int argc, char **argv) +{ + uint16_t handle; + bool vis; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + handle = parse_arg_uint16("handle", &rc); + if (rc != 0) { + console_printf("invalid 'handle' parameter\n"); + return rc; + } + + vis = parse_arg_bool("visibility", &rc); + if (rc != 0) { + console_printf("invalid 'visibility' parameter\n"); + return rc; + } + + ble_gatts_svc_set_visibility(handle, vis); + + return 0; +} + +/***************************************************************************** + * $gatt-find-included-services * + *****************************************************************************/ + +int +cmd_gatt_find_included_services(int argc, char **argv) +{ + uint16_t start_handle; + uint16_t conn_handle; + uint16_t end_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle); + if (rc != 0) { + console_printf("invalid 'conn start end' parameter\n"); + return rc; + } + + rc = btshell_find_inc_svcs(conn_handle, start_handle, end_handle); + if (rc != 0) { + console_printf("error finding included services; rc=%d\n", rc); + return rc; + } + + return 0; +} + +/***************************************************************************** + * $gatt-show * + *****************************************************************************/ + +int +cmd_gatt_show(int argc, char **argv) +{ + struct btshell_conn *conn; + struct btshell_svc *svc; + int i; + + for (i = 0; i < btshell_num_conns; i++) { + conn = btshell_conns + i; + + console_printf("CONNECTION: handle=%d\n", conn->handle); + + SLIST_FOREACH(svc, &conn->svcs, next) { + print_svc(svc); + } + } + + return 0; +} + +int +cmd_gatt_show_local(int argc, char **argv) +{ + gatt_svr_print_svcs(); + return 0; +} + +/***************************************************************************** + * $gatt-write * + *****************************************************************************/ + +int +cmd_gatt_write(int argc, char **argv) +{ + struct ble_gatt_attr attrs[MYNEWT_VAL(BLE_GATT_WRITE_MAX_ATTRS)] = { { 0 } }; + uint16_t attr_handle; + uint16_t conn_handle; + uint16_t offset; + int total_attr_len; + int num_attrs; + int attr_len; + int is_long; + int no_rsp; + int rc; + int i; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + no_rsp = parse_arg_bool_dflt("no_rsp", 0, &rc); + if (rc != 0) { + console_printf("invalid 'no_rsp' parameter\n"); + return rc; + } + + is_long = parse_arg_bool_dflt("long", 0, &rc); + if (rc != 0) { + console_printf("invalid 'long' parameter\n"); + return rc; + } + + total_attr_len = 0; + num_attrs = 0; + while (1) { + attr_handle = parse_arg_uint16("attr", &rc); + if (rc == ENOENT) { + break; + } else if (rc != 0) { + rc = -rc; + console_printf("invalid 'attr' parameter\n"); + goto done; + } + + rc = parse_arg_byte_stream("value", sizeof cmd_buf - total_attr_len, + cmd_buf + total_attr_len, &attr_len); + if (rc == ENOENT) { + break; + } else if (rc != 0) { + console_printf("invalid 'value' parameter\n"); + goto done; + } + + offset = parse_arg_uint16("offset", &rc); + if (rc == ENOENT) { + offset = 0; + } else if (rc != 0) { + console_printf("invalid 'offset' parameter\n"); + return rc; + } + + if (num_attrs >= sizeof attrs / sizeof attrs[0]) { + rc = -EINVAL; + goto done; + } + + attrs[num_attrs].handle = attr_handle; + attrs[num_attrs].offset = offset; + attrs[num_attrs].om = ble_hs_mbuf_from_flat(cmd_buf + total_attr_len, + attr_len); + if (attrs[num_attrs].om == NULL) { + goto done; + } + + total_attr_len += attr_len; + num_attrs++; + } + + if (no_rsp) { + if (num_attrs != 1) { + rc = -EINVAL; + goto done; + } + rc = btshell_write_no_rsp(conn_handle, attrs[0].handle, attrs[0].om); + attrs[0].om = NULL; + } else if (is_long) { + if (num_attrs != 1) { + rc = -EINVAL; + goto done; + } + rc = btshell_write_long(conn_handle, attrs[0].handle, + attrs[0].offset, attrs[0].om); + attrs[0].om = NULL; + } else if (num_attrs > 1) { + rc = btshell_write_reliable(conn_handle, attrs, num_attrs); + } else if (num_attrs == 1) { + rc = btshell_write(conn_handle, attrs[0].handle, attrs[0].om); + attrs[0].om = NULL; + } else { + rc = -EINVAL; + } + +done: + for (i = 0; i < sizeof attrs / sizeof attrs[0]; i++) { + os_mbuf_free_chain(attrs[i].om); + } + + if (rc != 0) { + console_printf("error writing characteristic; rc=%d\n", rc); + } + + return rc; +} diff --git a/src/libs/mynewt-nimble/apps/btshell/src/cmd_gatt.h b/src/libs/mynewt-nimble/apps/btshell/src/cmd_gatt.h new file mode 100644 index 00000000..70536d03 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/btshell/src/cmd_gatt.h @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef CMD_GATT_H +#define CMD_GATT_H + +#include "cmd.h" + +int cmd_gatt_discover_characteristic(int argc, char **argv); +int cmd_gatt_discover_descriptor(int argc, char **argv); +int cmd_gatt_discover_service(int argc, char **argv); +int cmd_gatt_discover_full(int argc, char **argv); +int cmd_gatt_find_included_services(int argc, char **argv); +int cmd_gatt_exchange_mtu(int argc, char **argv); +int cmd_gatt_notify(int argc, char **argv); +int cmd_gatt_read(int argc, char **argv); +int cmd_gatt_service_changed(int argc, char **argv); +int cmd_gatt_service_visibility(int argc, char **argv); +int cmd_gatt_show(int argc, char **argv); +int cmd_gatt_show_local(int argc, char **argv); +int cmd_gatt_write(int argc, char **argv); + +#endif diff --git a/src/libs/mynewt-nimble/apps/btshell/src/cmd_l2cap.c b/src/libs/mynewt-nimble/apps/btshell/src/cmd_l2cap.c new file mode 100644 index 00000000..e74e3bf3 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/btshell/src/cmd_l2cap.c @@ -0,0 +1,325 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include + +#include "syscfg/syscfg.h" +#include "host/ble_gap.h" +#include "host/ble_l2cap.h" +#include "console/console.h" +#include "btshell.h" +#include "cmd.h" +#include "cmd_l2cap.h" + + +/***************************************************************************** + * $l2cap-update * + *****************************************************************************/ + +int +cmd_l2cap_update(int argc, char **argv) +{ + struct ble_l2cap_sig_update_params params; + uint16_t conn_handle; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn_handle = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + params.itvl_min = parse_arg_uint16_dflt("interval_min", + BLE_GAP_INITIAL_CONN_ITVL_MIN, + &rc); + if (rc != 0) { + console_printf("invalid 'interval_min' parameter\n"); + return rc; + } + + params.itvl_max = parse_arg_uint16_dflt("interval_max", + BLE_GAP_INITIAL_CONN_ITVL_MAX, + &rc); + if (rc != 0) { + console_printf("invalid 'interval_max' parameter\n"); + return rc; + } + + params.slave_latency = parse_arg_uint16_dflt("latency", 0, &rc); + if (rc != 0) { + console_printf("invalid 'latency' parameter\n"); + return rc; + } + + params.timeout_multiplier = parse_arg_uint16_dflt("timeout", 0x0100, &rc); + if (rc != 0) { + console_printf("invalid 'timeout' parameter\n"); + return rc; + } + + rc = btshell_l2cap_update(conn_handle, ¶ms); + if (rc != 0) { + console_printf("error txing l2cap update; rc=%d\n", rc); + return rc; + } + + return 0; +} + +/***************************************************************************** + * $l2cap-create-server * + *****************************************************************************/ + +int +cmd_l2cap_create_server(int argc, char **argv) +{ + uint16_t psm = 0; + uint16_t mtu; + int error; + int accept_response = 0; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + psm = parse_arg_uint16("psm", &rc); + if (rc != 0) { + console_printf("invalid 'psm' parameter\n"); + return rc; + } + + error = parse_arg_uint32_dflt("error", 0, &rc); + if (rc != 0) { + console_printf("invalid 'error' parameter\n"); + return rc; + } + + mtu = parse_arg_uint16_dflt("mtu", 0, &rc); + if (rc != 0) { + console_printf("invalid 'mtu' parameter\n"); + return rc; + } + + switch (error) { + case 1: + accept_response = BLE_HS_EAUTHEN; + break; + case 2: + accept_response = BLE_HS_EAUTHOR; + break; + case 3: + accept_response = BLE_HS_EENCRYPT_KEY_SZ; + break; + } + + rc = btshell_l2cap_create_srv(psm, mtu, accept_response); + if (rc) { + console_printf("Server create error: 0x%02x\n", rc); + return rc; + } + + console_printf("Server created successfully\n"); + return 0; +} + +/***************************************************************************** + * $l2cap-connect * + *****************************************************************************/ + +int +cmd_l2cap_connect(int argc, char **argv) +{ + uint16_t conn = 0; + uint16_t psm = 0; + uint16_t mtu; + uint8_t num; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + psm = parse_arg_uint16("psm", &rc); + if (rc != 0) { + console_printf("invalid 'psm' parameter\n"); + return rc; + } + + mtu = parse_arg_uint16_dflt("mtu", 0, &rc); + if (rc != 0) { + console_printf("invalid 'mtu' parameter\n"); + return rc; + } + + num = parse_arg_uint8_dflt("num", 1, &rc); + if (rc != 0) { + console_printf("invalid 'num' parameter\n"); + return rc; + } + + return btshell_l2cap_connect(conn, psm, mtu, num); +} + +/***************************************************************************** + * $l2cap-disconnect * + *****************************************************************************/ + +int +cmd_l2cap_disconnect(int argc, char **argv) +{ + uint16_t conn; + uint16_t idx; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + idx = parse_arg_uint16("idx", &rc); + if (rc != 0) { + console_printf("invalid 'idx' parameter\n"); + return rc; + } + + return btshell_l2cap_disconnect(conn, idx); +} + +/***************************************************************************** + * $l2cap-send * + *****************************************************************************/ + +int +cmd_l2cap_send(int argc, char **argv) +{ + uint16_t conn; + uint16_t idx; + uint16_t bytes; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + idx = parse_arg_uint16("idx", &rc); + if (rc != 0) { + console_printf("invalid 'idx' parameter\n"); + return rc; + } + + bytes = parse_arg_uint16("bytes", &rc); + if (rc != 0) { + console_printf("invalid 'bytes' parameter\n"); + return rc; + } + + return btshell_l2cap_send(conn, idx, bytes); +} + +int +cmd_l2cap_show_coc(int argc, char **argv) +{ + struct btshell_conn *conn = NULL; + struct btshell_l2cap_coc *coc; + int i, j; + + for (i = 0; i < btshell_num_conns; i++) { + conn = btshell_conns + i; + + if (SLIST_EMPTY(&conn->coc_list)) { + continue; + } + + console_printf("conn_handle: 0x%04x\n", conn->handle); + j = 0; + SLIST_FOREACH(coc, &conn->coc_list, next) { + console_printf(" idx: %i, chan pointer = %p\n", j++, coc->chan); + } + } + + return 0; +} + +int +cmd_l2cap_reconfig(int argc, char **argv) +{ +#if MYNEWT_VAL(BLE_L2CAP_ENHANCED_COC) + uint16_t conn; + uint16_t mtu; + uint8_t idxs[5]; + int num; + int rc; + + rc = parse_arg_all(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + conn = parse_arg_uint16("conn", &rc); + if (rc != 0) { + console_printf("invalid 'conn' parameter\n"); + return rc; + } + + mtu = parse_arg_uint16_dflt("mtu", 0,&rc); + if (rc != 0) { + console_printf("invalid 'mtu' parameter\n"); + return rc; + } + + rc = parse_arg_uint8_list_with_separator("idxs", ",", 5, idxs, &num); + if (rc != 0) { + console_printf("invalid 'idxs' parameter\n"); + return rc; + } + + return btshell_l2cap_reconfig(conn, mtu, num, idxs); +#else + console_printf("To enable this features set BLE_L2CAP_ENHANCED_COC\n"); + return ENOTSUP; +#endif +} diff --git a/src/libs/mynewt-nimble/apps/btshell/src/cmd_l2cap.h b/src/libs/mynewt-nimble/apps/btshell/src/cmd_l2cap.h new file mode 100644 index 00000000..d366fe26 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/btshell/src/cmd_l2cap.h @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef CMD_L2CAP_H +#define CMD_L2CAP_H + +#include "cmd.h" + +int cmd_l2cap_update(int argc, char **argv); +int cmd_l2cap_create_server(int argc, char **argv); +int cmd_l2cap_connect(int argc, char **argv); +int cmd_l2cap_disconnect(int argc, char **argv); +int cmd_l2cap_send(int argc, char **argv); +int cmd_l2cap_show_coc(int argc, char **argv); +int cmd_l2cap_reconfig(int argc, char **argv); + +#endif diff --git a/src/libs/mynewt-nimble/apps/btshell/src/gatt_svr.c b/src/libs/mynewt-nimble/apps/btshell/src/gatt_svr.c new file mode 100644 index 00000000..99d44d05 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/btshell/src/gatt_svr.c @@ -0,0 +1,638 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include "bsp/bsp.h" +#include "console/console.h" +#include "host/ble_hs.h" +#include "host/ble_uuid.h" +#include "host/ble_gatt.h" +#include "btshell.h" + +/* 0000xxxx-8c26-476f-89a7-a108033a69c7 */ +#define PTS_UUID_DECLARE(uuid16) \ + ((const ble_uuid_t *) (&(ble_uuid128_t) BLE_UUID128_INIT( \ + 0xc7, 0x69, 0x3a, 0x03, 0x08, 0xa1, 0xa7, 0x89, \ + 0x6f, 0x47, 0x26, 0x8c, uuid16, uuid16 >> 8, 0x00, 0x00 \ + ))) + +#define PTS_SVC 0x0001 +#define PTS_CHR_READ 0x0002 +#define PTS_CHR_WRITE 0x0003 +#define PTS_CHR_RELIABLE_WRITE 0x0004 +#define PTS_CHR_WRITE_NO_RSP 0x0005 +#define PTS_CHR_READ_WRITE 0x0006 +#define PTS_CHR_READ_WRITE_ENC 0x0007 +#define PTS_CHR_READ_WRITE_AUTHEN 0x0008 +#define PTS_DSC_READ 0x0009 +#define PTS_DSC_WRITE 0x000a +#define PTS_DSC_READ_WRITE 0x000b +#define PTS_DSC_READ_WRITE_ENC 0x000c +#define PTS_DSC_READ_WRITE_AUTHEN 0x000d + +#define PTS_LONG_SVC 0x0011 +#define PTS_LONG_CHR_READ 0x0012 +#define PTS_LONG_CHR_WRITE 0x0013 +#define PTS_LONG_CHR_RELIABLE_WRITE 0x0014 +#define PTS_LONG_CHR_READ_WRITE 0x0015 +#define PTS_LONG_CHR_READ_WRITE_ALT 0x0016 +#define PTS_LONG_CHR_READ_WRITE_ENC 0x0017 +#define PTS_LONG_CHR_READ_WRITE_AUTHEN 0x0018 +#define PTS_LONG_DSC_READ 0x0019 +#define PTS_LONG_DSC_WRITE 0x001a +#define PTS_LONG_DSC_READ_WRITE 0x001b +#define PTS_LONG_DSC_READ_WRITE_ENC 0x001c +#define PTS_LONG_DSC_READ_WRITE_AUTHEN 0x001d + +#define PTS_INC_SVC 0x001e +#define PTS_CHR_READ_WRITE_ALT 0x001f + +/** + * The vendor specific security test service consists of two characteristics: + * o random-number-generator: generates a random 32-bit number each time + * it is read. This characteristic can only be read over an encrypted + * connection. + * o static-value: a single-byte characteristic that can always be read, + * but can only be written over an encrypted connection. + */ + +/* 59462f12-9543-9999-12c8-58b459a2712d */ +static const ble_uuid128_t gatt_svr_svc_sec_test_uuid = + BLE_UUID128_INIT(0x2d, 0x71, 0xa2, 0x59, 0xb4, 0x58, 0xc8, 0x12, + 0x99, 0x99, 0x43, 0x95, 0x12, 0x2f, 0x46, 0x59); + +/* 5c3a659e-897e-45e1-b016-007107c96df6 */ +static const ble_uuid128_t gatt_svr_chr_sec_test_rand_uuid = + BLE_UUID128_INIT(0xf6, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0, + 0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c); + +/* 5c3a659e-897e-45e1-b016-007107c96df7 */ +static const ble_uuid128_t gatt_svr_chr_sec_test_static_uuid = + BLE_UUID128_INIT(0xf7, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0, + 0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c); + +/* 5c3a659e-897e-45e1-b016-007107c96df8 */ +static const ble_uuid128_t gatt_svr_chr_sec_test_static_auth_uuid = + BLE_UUID128_INIT(0xf8, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0, + 0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c); + +static uint8_t gatt_svr_sec_test_static_val; + +static uint8_t gatt_svr_pts_static_val; +static uint8_t gatt_svr_pts_static_long_val[30]; +static uint8_t gatt_svr_pts_static_long_val_alt[30]; + +static int +gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_access_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_long_access_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static const struct ble_gatt_svc_def gatt_svr_svcs[] = { + { + /*** Service: PTS test. */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = PTS_UUID_DECLARE(PTS_SVC), + .characteristics = (struct ble_gatt_chr_def[]) { { + .uuid = PTS_UUID_DECLARE(PTS_CHR_READ), + .access_cb = gatt_svr_access_test, + .flags = BLE_GATT_CHR_F_READ, + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_WRITE), + .access_cb = gatt_svr_access_test, + .flags = BLE_GATT_CHR_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_RELIABLE_WRITE), + .access_cb = gatt_svr_access_test, + .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_RELIABLE_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_WRITE_NO_RSP), + .access_cb = gatt_svr_access_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE_NO_RSP, + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE), + .access_cb = gatt_svr_access_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE_ENC), + .access_cb = gatt_svr_access_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC | + BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC, + .min_key_size = 16, + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE_AUTHEN), + .access_cb = gatt_svr_access_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_AUTHEN | + BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN, + + .descriptors = (struct ble_gatt_dsc_def[]){ { + .uuid = PTS_UUID_DECLARE(PTS_DSC_READ), + .access_cb = gatt_svr_access_test, + .att_flags = BLE_ATT_F_READ, + }, { + .uuid = PTS_UUID_DECLARE(PTS_DSC_WRITE), + .access_cb = gatt_svr_access_test, + .att_flags = BLE_ATT_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_DSC_READ_WRITE), + .access_cb = gatt_svr_access_test, + .att_flags = BLE_ATT_F_READ | BLE_ATT_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_DSC_READ_WRITE_ENC), + .access_cb = gatt_svr_access_test, + .att_flags = BLE_ATT_F_READ | BLE_ATT_F_READ_ENC | + BLE_ATT_F_WRITE | BLE_ATT_F_WRITE_ENC, + .min_key_size = 16, + }, { + .uuid = PTS_UUID_DECLARE(PTS_DSC_READ_WRITE_AUTHEN), + .access_cb = gatt_svr_access_test, + .att_flags = BLE_ATT_F_READ | BLE_ATT_F_READ_AUTHEN | + BLE_ATT_F_WRITE | BLE_ATT_F_WRITE_AUTHEN, + }, { + 0, /* No more descriptors in this characteristic. */ + } } + }, { + 0, /* No more characteristics in this service. */ + } }, + }, + + { + /*** Service: PTS long test. */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = PTS_UUID_DECLARE(PTS_LONG_SVC), + .characteristics = (struct ble_gatt_chr_def[]) { { + .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ), + .access_cb = gatt_svr_long_access_test, + .flags = BLE_GATT_CHR_F_READ, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_WRITE), + .access_cb = gatt_svr_long_access_test, + .flags = BLE_GATT_CHR_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_RELIABLE_WRITE), + .access_cb = gatt_svr_long_access_test, + .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_RELIABLE_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE), + .access_cb = gatt_svr_long_access_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE_ALT), + .access_cb = gatt_svr_long_access_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE_ENC), + .access_cb = gatt_svr_long_access_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC | + BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC, + .min_key_size = 16, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE_AUTHEN), + .access_cb = gatt_svr_long_access_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_AUTHEN | + BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN, + + .descriptors = (struct ble_gatt_dsc_def[]){ { + .uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_READ), + .access_cb = gatt_svr_long_access_test, + .att_flags = BLE_ATT_F_READ, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_WRITE), + .access_cb = gatt_svr_long_access_test, + .att_flags = BLE_ATT_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_READ_WRITE), + .access_cb = gatt_svr_long_access_test, + .att_flags = BLE_ATT_F_READ | BLE_ATT_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_READ_WRITE_ENC), + .access_cb = gatt_svr_long_access_test, + .att_flags = BLE_ATT_F_READ | BLE_ATT_F_READ_ENC | + BLE_ATT_F_WRITE | BLE_ATT_F_WRITE_ENC, + .min_key_size = 16, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_READ_WRITE_AUTHEN), + .access_cb = gatt_svr_long_access_test, + .att_flags = BLE_ATT_F_READ | BLE_ATT_F_READ_AUTHEN | + BLE_ATT_F_WRITE | BLE_ATT_F_WRITE_AUTHEN, + }, { + 0, /* No more descriptors in this characteristic. */ + } } + }, { + 0, /* No more characteristics in this service. */ + } }, + }, + + { + /*** Service: Security test. */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = &gatt_svr_svc_sec_test_uuid.u, + .characteristics = (struct ble_gatt_chr_def[]) { { + /*** Characteristic: Random number generator. */ + .uuid = &gatt_svr_chr_sec_test_rand_uuid.u, + .access_cb = gatt_svr_chr_access_sec_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC, + }, { + /*** Characteristic: Static value. */ + .uuid = &gatt_svr_chr_sec_test_static_uuid.u, + .access_cb = gatt_svr_chr_access_sec_test, + .flags = BLE_GATT_CHR_F_READ | + BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC, + }, { + /*** Characteristic: Static value. */ + .uuid = &gatt_svr_chr_sec_test_static_auth_uuid.u, + .access_cb = gatt_svr_chr_access_sec_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_AUTHEN, + }, { + 0, /* No more characteristics in this service. */ + } }, + }, + + { + 0, /* No more services. */ + }, +}; + +static const struct ble_gatt_svc_def *inc_svcs[] = { + &gatt_svr_svcs[0], + NULL, +}; + +static const struct ble_gatt_svc_def gatt_svr_inc_svcs[] = { + { + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = PTS_UUID_DECLARE(PTS_INC_SVC), + .includes = inc_svcs, + .characteristics = (struct ble_gatt_chr_def[]) {{ + .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE_ALT), + .access_cb = gatt_svr_access_test, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + }, { + 0, /* No more characteristics */ + }, }, + }, + + { + 0, /* No more services. */ + }, +}; + +static int +gatt_svr_chr_write(struct os_mbuf *om, uint16_t min_len, uint16_t max_len, + void *dst, uint16_t *len) +{ + uint16_t om_len; + int rc; + + om_len = OS_MBUF_PKTLEN(om); + if (om_len < min_len || om_len > max_len) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + rc = ble_hs_mbuf_to_flat(om, dst, max_len, len); + if (rc != 0) { + return BLE_ATT_ERR_UNLIKELY; + } + + return 0; +} + +static int +gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + const ble_uuid_t *uuid; + int rand_num; + int rc; + + uuid = ctxt->chr->uuid; + + /* Determine which characteristic is being accessed by examining its + * 128-bit UUID. + */ + + if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_rand_uuid.u) == 0) { + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + + /* Respond with a 32-bit random number. */ + rand_num = rand(); + rc = os_mbuf_append(ctxt->om, &rand_num, sizeof rand_num); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_static_uuid.u) == 0 || + ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_static_auth_uuid.u) == 0) { + switch (ctxt->op) { + case BLE_GATT_ACCESS_OP_READ_CHR: + rc = os_mbuf_append(ctxt->om, &gatt_svr_sec_test_static_val, + sizeof gatt_svr_sec_test_static_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + + case BLE_GATT_ACCESS_OP_WRITE_CHR: + rc = gatt_svr_chr_write(ctxt->om, + sizeof gatt_svr_sec_test_static_val, + sizeof gatt_svr_sec_test_static_val, + &gatt_svr_sec_test_static_val, NULL); + return rc; + + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } + } + + /* Unknown characteristic; the nimble stack should not have called this + * function. + */ + assert(0); + return BLE_ATT_ERR_UNLIKELY; +} + +/* This method is used for PTS testing only, to extract 16 bit value + * from 128 bit vendor specific UUID. + */ +static uint16_t +extract_uuid16_from_pts_uuid128(const ble_uuid_t *uuid) +{ + const uint8_t *u8ptr; + uint16_t uuid16; + + u8ptr = BLE_UUID128(uuid)->value; + uuid16 = u8ptr[12]; + uuid16 |= (uint16_t)u8ptr[13] << 8; + return uuid16; +} + +static int +gatt_svr_access_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_CHR_READ: + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val, + sizeof gatt_svr_pts_static_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + + case PTS_CHR_WRITE: + case PTS_CHR_RELIABLE_WRITE: + case PTS_CHR_WRITE_NO_RSP: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(ctxt->om,0, + sizeof gatt_svr_pts_static_val, + &gatt_svr_pts_static_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val, + sizeof gatt_svr_pts_static_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + assert(0); + break; + case PTS_CHR_READ_WRITE: + case PTS_CHR_READ_WRITE_ENC: + case PTS_CHR_READ_WRITE_AUTHEN: + case PTS_CHR_READ_WRITE_ALT: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(ctxt->om,0, + sizeof gatt_svr_pts_static_val, + &gatt_svr_pts_static_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val, + sizeof gatt_svr_pts_static_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + assert(0); + break; + case PTS_DSC_READ: + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC); + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val, + sizeof gatt_svr_pts_static_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + + case PTS_DSC_WRITE: + assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC); + rc = gatt_svr_chr_write(ctxt->om,0, + sizeof gatt_svr_pts_static_val, + &gatt_svr_pts_static_val, NULL); + return rc; + + case PTS_DSC_READ_WRITE: + case PTS_DSC_READ_WRITE_ENC: + case PTS_DSC_READ_WRITE_AUTHEN: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC) { + rc = gatt_svr_chr_write(ctxt->om,0, + sizeof gatt_svr_pts_static_val, + &gatt_svr_pts_static_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val, + sizeof gatt_svr_pts_static_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + assert(0); + break; + default: + assert(0); + break; + } + + return BLE_ATT_ERR_UNLIKELY; +} + +static int +gatt_svr_long_access_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_LONG_CHR_READ: + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val, + sizeof gatt_svr_pts_static_long_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + + case PTS_LONG_CHR_WRITE: + case PTS_LONG_CHR_RELIABLE_WRITE: + assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR); + rc = gatt_svr_chr_write(ctxt->om,0, + sizeof gatt_svr_pts_static_long_val, + &gatt_svr_pts_static_long_val, NULL); + return rc; + + case PTS_LONG_CHR_READ_WRITE: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(ctxt->om,0, + sizeof gatt_svr_pts_static_long_val, + &gatt_svr_pts_static_long_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val, + sizeof gatt_svr_pts_static_long_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + case PTS_LONG_CHR_READ_WRITE_ALT: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(ctxt->om,0, + sizeof gatt_svr_pts_static_long_val_alt, + &gatt_svr_pts_static_long_val_alt, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val_alt, + sizeof gatt_svr_pts_static_long_val_alt); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + case PTS_LONG_CHR_READ_WRITE_ENC: + case PTS_LONG_CHR_READ_WRITE_AUTHEN: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(ctxt->om,0, + sizeof gatt_svr_pts_static_long_val, + &gatt_svr_pts_static_long_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val, + sizeof gatt_svr_pts_static_long_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + case PTS_LONG_DSC_READ: + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC); + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val, + sizeof gatt_svr_pts_static_long_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + + case PTS_LONG_DSC_WRITE: + assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC); + rc = gatt_svr_chr_write(ctxt->om,0, + sizeof gatt_svr_pts_static_long_val, + &gatt_svr_pts_static_long_val, NULL); + return rc; + + case PTS_LONG_DSC_READ_WRITE: + case PTS_LONG_DSC_READ_WRITE_ENC: + case PTS_LONG_DSC_READ_WRITE_AUTHEN: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC) { + rc = gatt_svr_chr_write(ctxt->om,0, + sizeof gatt_svr_pts_static_long_val, + &gatt_svr_pts_static_long_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val, + sizeof gatt_svr_pts_static_long_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +void +gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) +{ + char buf[BLE_UUID_STR_LEN]; + + switch (ctxt->op) { + case BLE_GATT_REGISTER_OP_SVC: + MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n", + ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf), + ctxt->svc.handle); + break; + + case BLE_GATT_REGISTER_OP_CHR: + MODLOG_DFLT(DEBUG, "registering characteristic %s with " + "def_handle=%d val_handle=%d\n", + ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf), + ctxt->chr.def_handle, + ctxt->chr.val_handle); + break; + + case BLE_GATT_REGISTER_OP_DSC: + MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n", + ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf), + ctxt->dsc.handle); + break; + + default: + assert(0); + break; + } +} + +void +gatt_svr_print_svcs(void) +{ + ble_gatts_show_local(); +} + +int +gatt_svr_init(void) +{ + int rc; + + rc = ble_gatts_count_cfg(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_add_svcs(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_count_cfg(gatt_svr_inc_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_add_svcs(gatt_svr_inc_svcs); + if (rc != 0) { + return rc; + } + + return 0; +} diff --git a/src/libs/mynewt-nimble/apps/btshell/src/main.c b/src/libs/mynewt-nimble/apps/btshell/src/main.c new file mode 100644 index 00000000..b7031836 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/btshell/src/main.c @@ -0,0 +1,2630 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include "os/mynewt.h" +#include "bsp/bsp.h" +#include "log/log.h" +#include "stats/stats.h" +#include "bsp/bsp.h" +#include "hal/hal_gpio.h" +#include "console/console.h" +#include "btshell.h" +#include "cmd.h" + +/* BLE */ +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "nimble/ble_hci_trans.h" +#include "host/ble_hs.h" +#include "host/ble_hs_adv.h" +#include "host/ble_uuid.h" +#include "host/ble_att.h" +#include "host/ble_gap.h" +#include "host/ble_gatt.h" +#include "host/ble_store.h" +#include "host/ble_sm.h" + +/* Mandatory services. */ +#include "services/gap/ble_svc_gap.h" +#include "services/gatt/ble_svc_gatt.h" + +/* XXX: An app should not include private headers from a library. The btshell + * app uses some of nimble's internal details for logging. + */ +#include "../src/ble_hs_conn_priv.h" +#include "../src/ble_hs_atomic_priv.h" +#include "../src/ble_hs_priv.h" + +#if MYNEWT_VAL(BLE_ROLE_CENTRAL) +#define BTSHELL_MAX_SVCS 32 +#define BTSHELL_MAX_CHRS 64 +#define BTSHELL_MAX_DSCS 64 +#else +#define BTSHELL_MAX_SVCS 1 +#define BTSHELL_MAX_CHRS 1 +#define BTSHELL_MAX_DSCS 1 +#endif + +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) +#define BTSHELL_COC_MTU (256) +/* We use same pool for incoming and outgoing sdu */ +#define BTSHELL_COC_BUF_COUNT (3 * MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)) + +#define INT_TO_PTR(x) (void *)((intptr_t)(x)) +#define PTR_TO_INT(x) (int) ((intptr_t)(x)) +#endif + +bssnz_t struct btshell_conn btshell_conns[MYNEWT_VAL(BLE_MAX_CONNECTIONS)]; +int btshell_num_conns; + +static os_membuf_t btshell_svc_mem[ + OS_MEMPOOL_SIZE(BTSHELL_MAX_SVCS, sizeof(struct btshell_svc)) +]; +static struct os_mempool btshell_svc_pool; + +static os_membuf_t btshell_chr_mem[ + OS_MEMPOOL_SIZE(BTSHELL_MAX_CHRS, sizeof(struct btshell_chr)) +]; +static struct os_mempool btshell_chr_pool; + +static os_membuf_t btshell_dsc_mem[ + OS_MEMPOOL_SIZE(BTSHELL_MAX_DSCS, sizeof(struct btshell_dsc)) +]; +static struct os_mempool btshell_dsc_pool; + +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) +static os_membuf_t btshell_coc_conn_mem[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM), + sizeof(struct btshell_l2cap_coc)) +]; +static struct os_mempool btshell_coc_conn_pool; + +static os_membuf_t btshell_sdu_coc_mem[ + OS_MEMPOOL_SIZE(BTSHELL_COC_BUF_COUNT, BTSHELL_COC_MTU) +]; +struct os_mbuf_pool sdu_os_mbuf_pool; +static struct os_mempool sdu_coc_mbuf_mempool; +#endif + +static struct os_callout btshell_tx_timer; +struct btshell_tx_data_s +{ + uint16_t tx_num; + uint16_t tx_num_requested; + uint16_t tx_rate; + uint16_t tx_conn_handle; + uint16_t tx_len; + struct ble_hs_conn *conn; +}; +static struct btshell_tx_data_s btshell_tx_data; +int btshell_full_disc_prev_chr_val; + +struct ble_sm_sc_oob_data oob_data_local; +struct ble_sm_sc_oob_data oob_data_remote; + +#define XSTR(s) STR(s) +#ifndef STR +#define STR(s) #s +#endif + + +#ifdef DEVICE_NAME +#define BTSHELL_AUTO_DEVICE_NAME XSTR(DEVICE_NAME) +#else +#define BTSHELL_AUTO_DEVICE_NAME "" +#endif + +#if MYNEWT_VAL(BLE_EXT_ADV) +struct { + bool restart; + uint16_t conn_handle; +} ext_adv_restart[BLE_ADV_INSTANCES]; +#endif + +static struct { + bool restart; + uint8_t own_addr_type; + ble_addr_t direct_addr; + int32_t duration_ms; + struct ble_gap_adv_params params; +} adv_params; + +static void +btshell_print_error(char *msg, uint16_t conn_handle, + const struct ble_gatt_error *error) +{ + if (msg == NULL) { + msg = "ERROR"; + } + + console_printf("%s: conn_handle=%d status=%d att_handle=%d\n", + msg, conn_handle, error->status, error->att_handle); +} + +static void +btshell_print_adv_fields(const struct ble_hs_adv_fields *fields) +{ + const uint8_t *u8p; + int i; + + if (fields->flags != 0) { + console_printf(" flags=0x%02x:\n", fields->flags); + + if (!(fields->flags & BLE_HS_ADV_F_DISC_LTD) && + !(fields->flags & BLE_HS_ADV_F_DISC_GEN)) { + console_printf(" Non-discoverable mode\n"); + } + + if (fields->flags & BLE_HS_ADV_F_DISC_LTD) { + console_printf(" Limited discoverable mode\n"); + } + + if (fields->flags & BLE_HS_ADV_F_DISC_GEN) { + console_printf(" General discoverable mode\n"); + } + + if (fields->flags & BLE_HS_ADV_F_BREDR_UNSUP) { + console_printf(" BR/EDR not supported\n"); + } + } + + if (fields->uuids16 != NULL) { + console_printf(" uuids16(%scomplete)=", + fields->uuids16_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids16; i++) { + print_uuid(&fields->uuids16[i].u); + console_printf(" "); + } + console_printf("\n"); + } + + if (fields->uuids32 != NULL) { + console_printf(" uuids32(%scomplete)=", + fields->uuids32_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids32; i++) { + print_uuid(&fields->uuids32[i].u); + console_printf(" "); + } + console_printf("\n"); + } + + if (fields->uuids128 != NULL) { + console_printf(" uuids128(%scomplete)=", + fields->uuids128_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids128; i++) { + print_uuid(&fields->uuids128[i].u); + console_printf(" "); + } + console_printf("\n"); + } + + if (fields->name != NULL) { + console_printf(" name(%scomplete)=", + fields->name_is_complete ? "" : "in"); + console_write((char *)fields->name, fields->name_len); + console_printf("\n"); + } + + if (fields->tx_pwr_lvl_is_present) { + console_printf(" tx_pwr_lvl=%d\n", fields->tx_pwr_lvl); + } + + if (fields->slave_itvl_range != NULL) { + console_printf(" slave_itvl_range="); + print_bytes(fields->slave_itvl_range, + BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN); + console_printf("\n"); + } + + if (fields->svc_data_uuid16 != NULL) { + console_printf(" svc_data_uuid16="); + print_bytes(fields->svc_data_uuid16, + fields->svc_data_uuid16_len); + console_printf("\n"); + } + + if (fields->public_tgt_addr != NULL) { + console_printf(" public_tgt_addr="); + u8p = fields->public_tgt_addr; + for (i = 0; i < fields->num_public_tgt_addrs; i++) { + print_addr(u8p); + u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN; + } + console_printf("\n"); + } + + if (fields->appearance_is_present) { + console_printf(" appearance=0x%04x\n", fields->appearance); + } + + if (fields->adv_itvl_is_present) { + console_printf(" adv_itvl=0x%04x\n", fields->adv_itvl); + } + + if (fields->svc_data_uuid32 != NULL) { + console_printf(" svc_data_uuid32="); + print_bytes(fields->svc_data_uuid32, + fields->svc_data_uuid32_len); + console_printf("\n"); + } + + if (fields->svc_data_uuid128 != NULL) { + console_printf(" svc_data_uuid128="); + print_bytes(fields->svc_data_uuid128, + fields->svc_data_uuid128_len); + console_printf("\n"); + } + + if (fields->uri != NULL) { + console_printf(" uri="); + print_bytes(fields->uri, fields->uri_len); + console_printf("\n"); + } + + if (fields->mfg_data != NULL) { + console_printf(" mfg_data="); + print_bytes(fields->mfg_data, fields->mfg_data_len); + console_printf("\n"); + } +} + +static int +btshell_conn_find_idx(uint16_t handle) +{ + int i; + + for (i = 0; i < btshell_num_conns; i++) { + if (btshell_conns[i].handle == handle) { + return i; + } + } + + return -1; +} + +static struct btshell_conn * +btshell_conn_find(uint16_t handle) +{ + int idx; + + idx = btshell_conn_find_idx(handle); + if (idx == -1) { + return NULL; + } else { + return btshell_conns + idx; + } +} + +static struct btshell_svc * +btshell_svc_find_prev(struct btshell_conn *conn, uint16_t svc_start_handle) +{ + struct btshell_svc *prev; + struct btshell_svc *svc; + + prev = NULL; + SLIST_FOREACH(svc, &conn->svcs, next) { + if (svc->svc.start_handle >= svc_start_handle) { + break; + } + + prev = svc; + } + + return prev; +} + +static struct btshell_svc * +btshell_svc_find(struct btshell_conn *conn, uint16_t svc_start_handle, + struct btshell_svc **out_prev) +{ + struct btshell_svc *prev; + struct btshell_svc *svc; + + prev = btshell_svc_find_prev(conn, svc_start_handle); + if (prev == NULL) { + svc = SLIST_FIRST(&conn->svcs); + } else { + svc = SLIST_NEXT(prev, next); + } + + if (svc != NULL && svc->svc.start_handle != svc_start_handle) { + svc = NULL; + } + + if (out_prev != NULL) { + *out_prev = prev; + } + return svc; +} + +static struct btshell_svc * +btshell_svc_find_range(struct btshell_conn *conn, uint16_t attr_handle) +{ + struct btshell_svc *svc; + + SLIST_FOREACH(svc, &conn->svcs, next) { + if (svc->svc.start_handle <= attr_handle && + svc->svc.end_handle >= attr_handle) { + + return svc; + } + } + + return NULL; +} + +static void +btshell_chr_delete(struct btshell_chr *chr) +{ + struct btshell_dsc *dsc; + + while ((dsc = SLIST_FIRST(&chr->dscs)) != NULL) { + SLIST_REMOVE_HEAD(&chr->dscs, next); + os_memblock_put(&btshell_dsc_pool, dsc); + } + + os_memblock_put(&btshell_chr_pool, chr); +} + +static void +btshell_svc_delete(struct btshell_svc *svc) +{ + struct btshell_chr *chr; + + while ((chr = SLIST_FIRST(&svc->chrs)) != NULL) { + SLIST_REMOVE_HEAD(&svc->chrs, next); + btshell_chr_delete(chr); + } + + os_memblock_put(&btshell_svc_pool, svc); +} + +static struct btshell_svc * +btshell_svc_add(uint16_t conn_handle, const struct ble_gatt_svc *gatt_svc) +{ + struct btshell_conn *conn; + struct btshell_svc *prev; + struct btshell_svc *svc; + + conn = btshell_conn_find(conn_handle); + if (conn == NULL) { + MODLOG_DFLT(DEBUG, "RECEIVED SERVICE FOR UNKNOWN CONNECTION; " + "HANDLE=%d\n", + conn_handle); + return NULL; + } + + svc = btshell_svc_find(conn, gatt_svc->start_handle, &prev); + if (svc != NULL) { + /* Service already discovered. */ + return svc; + } + + svc = os_memblock_get(&btshell_svc_pool); + if (svc == NULL) { + MODLOG_DFLT(DEBUG, "OOM WHILE DISCOVERING SERVICE\n"); + return NULL; + } + memset(svc, 0, sizeof *svc); + + svc->svc = *gatt_svc; + SLIST_INIT(&svc->chrs); + + if (prev == NULL) { + SLIST_INSERT_HEAD(&conn->svcs, svc, next); + } else { + SLIST_INSERT_AFTER(prev, svc, next); + } + + return svc; +} + +static struct btshell_chr * +btshell_chr_find_prev(const struct btshell_svc *svc, uint16_t chr_val_handle) +{ + struct btshell_chr *prev; + struct btshell_chr *chr; + + prev = NULL; + SLIST_FOREACH(chr, &svc->chrs, next) { + if (chr->chr.val_handle >= chr_val_handle) { + break; + } + + prev = chr; + } + + return prev; +} + +static struct btshell_chr * +btshell_chr_find(const struct btshell_svc *svc, uint16_t chr_val_handle, + struct btshell_chr **out_prev) +{ + struct btshell_chr *prev; + struct btshell_chr *chr; + + prev = btshell_chr_find_prev(svc, chr_val_handle); + if (prev == NULL) { + chr = SLIST_FIRST(&svc->chrs); + } else { + chr = SLIST_NEXT(prev, next); + } + + if (chr != NULL && chr->chr.val_handle != chr_val_handle) { + chr = NULL; + } + + if (out_prev != NULL) { + *out_prev = prev; + } + return chr; +} + +static struct btshell_chr * +btshell_chr_add(uint16_t conn_handle, uint16_t svc_start_handle, + const struct ble_gatt_chr *gatt_chr) +{ + struct btshell_conn *conn; + struct btshell_chr *prev; + struct btshell_chr *chr; + struct btshell_svc *svc; + + conn = btshell_conn_find(conn_handle); + if (conn == NULL) { + MODLOG_DFLT(DEBUG, "RECEIVED SERVICE FOR UNKNOWN CONNECTION; " + "HANDLE=%d\n", + conn_handle); + return NULL; + } + + svc = btshell_svc_find(conn, svc_start_handle, NULL); + if (svc == NULL) { + MODLOG_DFLT(DEBUG, "CAN'T FIND SERVICE FOR DISCOVERED CHR; HANDLE=%d\n", + conn_handle); + return NULL; + } + + chr = btshell_chr_find(svc, gatt_chr->val_handle, &prev); + if (chr != NULL) { + /* Characteristic already discovered. */ + return chr; + } + + chr = os_memblock_get(&btshell_chr_pool); + if (chr == NULL) { + MODLOG_DFLT(DEBUG, "OOM WHILE DISCOVERING CHARACTERISTIC\n"); + return NULL; + } + memset(chr, 0, sizeof *chr); + + chr->chr = *gatt_chr; + + if (prev == NULL) { + SLIST_INSERT_HEAD(&svc->chrs, chr, next); + } else { + SLIST_NEXT(prev, next) = chr; + } + + return chr; +} + +static struct btshell_dsc * +btshell_dsc_find_prev(const struct btshell_chr *chr, uint16_t dsc_handle) +{ + struct btshell_dsc *prev; + struct btshell_dsc *dsc; + + prev = NULL; + SLIST_FOREACH(dsc, &chr->dscs, next) { + if (dsc->dsc.handle >= dsc_handle) { + break; + } + + prev = dsc; + } + + return prev; +} + +static struct btshell_dsc * +btshell_dsc_find(const struct btshell_chr *chr, uint16_t dsc_handle, + struct btshell_dsc **out_prev) +{ + struct btshell_dsc *prev; + struct btshell_dsc *dsc; + + prev = btshell_dsc_find_prev(chr, dsc_handle); + if (prev == NULL) { + dsc = SLIST_FIRST(&chr->dscs); + } else { + dsc = SLIST_NEXT(prev, next); + } + + if (dsc != NULL && dsc->dsc.handle != dsc_handle) { + dsc = NULL; + } + + if (out_prev != NULL) { + *out_prev = prev; + } + return dsc; +} + +static struct btshell_dsc * +btshell_dsc_add(uint16_t conn_handle, uint16_t chr_val_handle, + const struct ble_gatt_dsc *gatt_dsc) +{ + struct btshell_conn *conn; + struct btshell_dsc *prev; + struct btshell_dsc *dsc; + struct btshell_svc *svc; + struct btshell_chr *chr; + + conn = btshell_conn_find(conn_handle); + if (conn == NULL) { + MODLOG_DFLT(DEBUG, "RECEIVED SERVICE FOR UNKNOWN CONNECTION; " + "HANDLE=%d\n", + conn_handle); + return NULL; + } + + svc = btshell_svc_find_range(conn, chr_val_handle); + if (svc == NULL) { + MODLOG_DFLT(DEBUG, "CAN'T FIND SERVICE FOR DISCOVERED DSC; HANDLE=%d\n", + conn_handle); + return NULL; + } + + chr = btshell_chr_find(svc, chr_val_handle, NULL); + if (chr == NULL) { + MODLOG_DFLT(DEBUG, "CAN'T FIND CHARACTERISTIC FOR DISCOVERED DSC; " + "HANDLE=%d\n", + conn_handle); + return NULL; + } + + dsc = btshell_dsc_find(chr, gatt_dsc->handle, &prev); + if (dsc != NULL) { + /* Descriptor already discovered. */ + return dsc; + } + + dsc = os_memblock_get(&btshell_dsc_pool); + if (dsc == NULL) { + console_printf("OOM WHILE DISCOVERING DESCRIPTOR\n"); + return NULL; + } + memset(dsc, 0, sizeof *dsc); + + dsc->dsc = *gatt_dsc; + + if (prev == NULL) { + SLIST_INSERT_HEAD(&chr->dscs, dsc, next); + } else { + SLIST_NEXT(prev, next) = dsc; + } + + return dsc; +} + +static struct btshell_conn * +btshell_conn_add(struct ble_gap_conn_desc *desc) +{ + struct btshell_conn *conn; + + assert(btshell_num_conns < MYNEWT_VAL(BLE_MAX_CONNECTIONS)); + + conn = btshell_conns + btshell_num_conns; + btshell_num_conns++; + + conn->handle = desc->conn_handle; + SLIST_INIT(&conn->svcs); + SLIST_INIT(&conn->coc_list); + + return conn; +} + +static void +btshell_conn_delete_idx(int idx) +{ + struct btshell_conn *conn; + struct btshell_svc *svc; + + assert(idx >= 0 && idx < btshell_num_conns); + + conn = btshell_conns + idx; + while ((svc = SLIST_FIRST(&conn->svcs)) != NULL) { + SLIST_REMOVE_HEAD(&conn->svcs, next); + btshell_svc_delete(svc); + } + + /* This '#if' is not strictly necessary. It is here to prevent a spurious + * warning from being reported. + */ +#if MYNEWT_VAL(BLE_MAX_CONNECTIONS) > 1 + int i; + for (i = idx + 1; i < btshell_num_conns; i++) { + btshell_conns[i - 1] = btshell_conns[i]; + } +#endif + + btshell_num_conns--; +} + +static int +btshell_on_mtu(uint16_t conn_handle, const struct ble_gatt_error *error, + uint16_t mtu, void *arg) +{ + switch (error->status) { + case 0: + console_printf("mtu exchange complete: conn_handle=%d mtu=%d\n", + conn_handle, mtu); + break; + + default: + btshell_print_error(NULL, conn_handle, error); + break; + } + + return 0; +} + +static void +btshell_full_disc_complete(int rc) +{ + console_printf("full discovery complete; rc=%d\n", rc); + btshell_full_disc_prev_chr_val = 0; +} + +static void +btshell_disc_full_dscs(uint16_t conn_handle) +{ + struct btshell_conn *conn; + struct btshell_chr *chr; + struct btshell_svc *svc; + int rc; + + conn = btshell_conn_find(conn_handle); + if (conn == NULL) { + MODLOG_DFLT(DEBUG, "Failed to discover descriptors for conn=%d; " + "not connected\n", conn_handle); + btshell_full_disc_complete(BLE_HS_ENOTCONN); + return; + } + + SLIST_FOREACH(svc, &conn->svcs, next) { + SLIST_FOREACH(chr, &svc->chrs, next) { + if (!chr_is_empty(svc, chr) && + SLIST_EMPTY(&chr->dscs) && + btshell_full_disc_prev_chr_val <= chr->chr.def_handle) { + + rc = btshell_disc_all_dscs(conn_handle, + chr->chr.val_handle, + chr_end_handle(svc, chr)); + if (rc != 0) { + btshell_full_disc_complete(rc); + } + + btshell_full_disc_prev_chr_val = chr->chr.val_handle; + return; + } + } + } + + /* All descriptors discovered. */ + btshell_full_disc_complete(0); +} + +static void +btshell_disc_full_chrs(uint16_t conn_handle) +{ + struct btshell_conn *conn; + struct btshell_svc *svc; + int rc; + + conn = btshell_conn_find(conn_handle); + if (conn == NULL) { + MODLOG_DFLT(DEBUG, "Failed to discover characteristics for conn=%d; " + "not connected\n", conn_handle); + btshell_full_disc_complete(BLE_HS_ENOTCONN); + return; + } + + SLIST_FOREACH(svc, &conn->svcs, next) { + if (!svc->discovered) { + rc = btshell_disc_all_chrs_in_svc(conn_handle, svc); + if (rc != 0) { + btshell_full_disc_complete(rc); + } + return; + } + } + + /* All characteristics discovered. */ + btshell_disc_full_dscs(conn_handle); +} + +static int +btshell_on_disc_s(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_svc *service, void *arg) +{ + switch (error->status) { + case 0: + btshell_svc_add(conn_handle, service); + break; + + case BLE_HS_EDONE: + console_printf("service discovery successful\n"); + if (btshell_full_disc_prev_chr_val > 0) { + btshell_disc_full_chrs(conn_handle); + } + break; + + default: + btshell_print_error(NULL, conn_handle, error); + break; + } + + return 0; +} + +static int +btshell_on_disc_c(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_chr *chr, void *arg) +{ + intptr_t svc_start_handle; + + svc_start_handle = (intptr_t)arg; + + switch (error->status) { + case 0: + btshell_chr_add(conn_handle, svc_start_handle, chr); + break; + + case BLE_HS_EDONE: + console_printf("characteristic discovery successful\n"); + if (btshell_full_disc_prev_chr_val > 0) { + btshell_disc_full_chrs(conn_handle); + } + break; + + default: + btshell_print_error(NULL, conn_handle, error); + break; + } + + return 0; +} + +static int +btshell_on_disc_c_in_s(uint16_t conn_handle, const struct ble_gatt_error *error, + const struct ble_gatt_chr *chr, void *arg) +{ + struct btshell_svc *svc = arg; + + switch (error->status) { + case 0: + btshell_chr_add(conn_handle, svc->svc.start_handle, chr); + break; + + case BLE_HS_EDONE: + svc->discovered = true; + console_printf("characteristic discovery successful\n"); + if (btshell_full_disc_prev_chr_val > 0) { + btshell_disc_full_chrs(conn_handle); + } + break; + + default: + btshell_print_error(NULL, conn_handle, error); + break; + } + + return 0; +} + +static int +btshell_on_disc_d(uint16_t conn_handle, const struct ble_gatt_error *error, + uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc, + void *arg) +{ + switch (error->status) { + case 0: + btshell_dsc_add(conn_handle, chr_val_handle, dsc); + break; + + case BLE_HS_EDONE: + console_printf("descriptor discovery successful\n"); + if (btshell_full_disc_prev_chr_val > 0) { + btshell_disc_full_dscs(conn_handle); + } + break; + + default: + btshell_print_error(NULL, conn_handle, error); + break; + } + + return 0; +} + +static int +btshell_on_read(uint16_t conn_handle, const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg) +{ + switch (error->status) { + case 0: + console_printf("characteristic read; conn_handle=%d " + "attr_handle=%d len=%d value=", conn_handle, + attr->handle, OS_MBUF_PKTLEN(attr->om)); + print_mbuf(attr->om); + console_printf("\n"); + break; + + case BLE_HS_EDONE: + console_printf("characteristic read complete\n"); + break; + + default: + btshell_print_error(NULL, conn_handle, error); + break; + } + + return 0; +} + +static int +btshell_on_write(uint16_t conn_handle, const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, void *arg) +{ + switch (error->status) { + case 0: + console_printf("characteristic write complete; conn_handle=%d " + "attr_handle=%d\n", conn_handle, attr->handle); + break; + + default: + btshell_print_error(NULL, conn_handle, error); + break; + } + + return 0; +} + +static int +btshell_on_write_reliable(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attrs, uint8_t num_attrs, + void *arg) +{ + int i; + + switch (error->status) { + case 0: + console_printf("characteristic write reliable complete; " + "conn_handle=%d", conn_handle); + + for (i = 0; i < num_attrs; i++) { + console_printf(" attr_handle=%d len=%d value=", attrs[i].handle, + OS_MBUF_PKTLEN(attrs[i].om)); + print_mbuf(attrs[i].om); + } + console_printf("\n"); + break; + + default: + btshell_print_error(NULL, conn_handle, error); + break; + } + + return 0; +} + +static void +btshell_decode_adv_data(const uint8_t *adv_data, uint8_t adv_data_len, void *arg) +{ + struct btshell_scan_opts *scan_opts = arg; + struct ble_hs_adv_fields fields; + + console_printf(" data_length=%d data=", adv_data_len); + + if (scan_opts) { + adv_data_len = min(adv_data_len, scan_opts->limit); + } + + print_bytes(adv_data, adv_data_len); + + console_printf(" fields:\n"); + ble_hs_adv_parse_fields(&fields, adv_data, adv_data_len); + btshell_print_adv_fields(&fields); +} + +#if MYNEWT_VAL(BLE_EXT_ADV) +static void +btshell_decode_event_type(struct ble_gap_ext_disc_desc *desc, void *arg) +{ + struct btshell_scan_opts *scan_opts = arg; + uint8_t directed = 0; + + if (desc->props & BLE_HCI_ADV_LEGACY_MASK) { + if (scan_opts && scan_opts->ignore_legacy) { + return; + } + + console_printf("Legacy PDU type %d", desc->legacy_event_type); + if (desc->legacy_event_type == BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) { + directed = 1; + } + goto common_data; + } else { + if (scan_opts && scan_opts->periodic_only && desc->periodic_adv_itvl == 0) { + return; + } + } + + console_printf("Extended adv: "); + if (desc->props & BLE_HCI_ADV_CONN_MASK) { + console_printf("'conn' "); + } + if (desc->props & BLE_HCI_ADV_SCAN_MASK) { + console_printf("'scan' "); + } + if (desc->props & BLE_HCI_ADV_DIRECT_MASK) { + console_printf("'dir' "); + directed = 1; + } + + if (desc->props & BLE_HCI_ADV_SCAN_RSP_MASK) { + console_printf("'scan rsp' "); + } + + switch(desc->data_status) { + case BLE_GAP_EXT_ADV_DATA_STATUS_COMPLETE: + console_printf("complete"); + break; + case BLE_GAP_EXT_ADV_DATA_STATUS_INCOMPLETE: + console_printf("incomplete"); + break; + case BLE_GAP_EXT_ADV_DATA_STATUS_TRUNCATED: + console_printf("truncated"); + break; + default: + console_printf("reserved %d", desc->data_status); + break; + } + +common_data: + console_printf(" rssi=%d txpower=%d, pphy=%d, sphy=%d, sid=%d," + " periodic_adv_itvl=%u, addr_type=%d addr=", + desc->rssi, desc->tx_power, desc->prim_phy, desc->sec_phy, + desc->sid, desc->periodic_adv_itvl, desc->addr.type); + print_addr(desc->addr.val); + if (directed) { + console_printf(" init_addr_type=%d inita=", desc->direct_addr.type); + print_addr(desc->direct_addr.val); + } + + console_printf("\n"); + + if(!desc->length_data) { + return; + } + + btshell_decode_adv_data(desc->data, desc->length_data, arg); +} +#endif + +static int +btshell_restart_adv(struct ble_gap_event *event) +{ + int rc = 0; +#if MYNEWT_VAL(BLE_EXT_ADV) + uint8_t i; +#endif + + if (event->type != BLE_GAP_EVENT_DISCONNECT) { + return -1; + } + +#if MYNEWT_VAL(BLE_EXT_ADV) + for (i = 0; i < BLE_ADV_INSTANCES; ++i) { + if (ext_adv_restart[i].restart && + (ext_adv_restart[i].conn_handle == + event->disconnect.conn.conn_handle)) { + rc = ble_gap_ext_adv_start(i, 0, 0); + break; + } + } +#else + if (!adv_params.restart) { + return 0; + } + + rc = ble_gap_adv_start(adv_params.own_addr_type, &adv_params.direct_addr, + adv_params.duration_ms, &adv_params.params, + btshell_gap_event, NULL); +#endif + + return rc; +} + +#if MYNEWT_VAL(BLE_PERIODIC_ADV) +struct psync { + bool established; + unsigned int complete; + unsigned int truncated; + size_t off; + bool changed; + uint8_t data[1650]; /* TODO make this configurable */ +}; + +static struct psync g_periodic_data[MYNEWT_VAL(BLE_MAX_PERIODIC_SYNCS)]; + +void +btshell_sync_stats(uint16_t handle) +{ + struct psync *psync; + + if (handle >= MYNEWT_VAL(BLE_MAX_PERIODIC_SYNCS)) { + return; + } + + psync = &g_periodic_data[handle]; + if (!psync->established) { + console_printf("Sync not established\n"); + return; + } + + console_printf("completed=%u truncated=%u\n", + psync->complete, psync->truncated); +} + +static void +handle_periodic_report(struct ble_gap_event *event) +{ + struct psync *psync; + uint16_t handle = event->periodic_report.sync_handle; + + if (handle >= MYNEWT_VAL(BLE_MAX_PERIODIC_SYNCS)) { + return; + } + + psync = &g_periodic_data[handle]; + + if (psync->changed || + memcmp(psync->data + psync->off, event->periodic_report.data, + event->periodic_report.data_length)) { + /* first fragment with changed data */ + if (!psync->changed) { + console_printf("Sync data changed, completed=%u, truncated=%u\n", + psync->complete, psync->truncated); + } + + psync->changed = true; + + console_printf("Sync report handle=%u status=", handle); + switch(event->periodic_report.data_status) { + case BLE_HCI_PERIODIC_DATA_STATUS_COMPLETE: + console_printf("complete"); + break; + case BLE_HCI_PERIODIC_DATA_STATUS_INCOMPLETE: + console_printf("incomplete"); + break; + case BLE_HCI_PERIODIC_DATA_STATUS_TRUNCATED: + console_printf("truncated"); + break; + default: + console_printf("reserved 0x%x", event->periodic_report.data_status); + break; + } + + btshell_decode_adv_data(event->periodic_report.data, + event->periodic_report.data_length, NULL); + + psync->complete = 0; + psync->truncated = 0; + } + + /* cache data */ + memcpy(psync->data + psync->off, event->periodic_report.data, + event->periodic_report.data_length); + + switch(event->periodic_report.data_status) { + case BLE_HCI_PERIODIC_DATA_STATUS_INCOMPLETE: + psync->off += event->periodic_report.data_length; + break; + case BLE_HCI_PERIODIC_DATA_STATUS_COMPLETE: + psync->complete++; + psync->off = 0; + psync->changed = false; + break; + case BLE_HCI_PERIODIC_DATA_STATUS_TRUNCATED: + psync->truncated++; + psync->off = 0; + psync->changed = false; + break; + default: + break; + } +} +#endif + +int +btshell_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int conn_idx; + int rc; +#if MYNEWT_VAL(BLE_PERIODIC_ADV) + struct psync *psync; +#endif + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + console_printf("connection %s; status=%d ", + event->connect.status == 0 ? "established" : "failed", + event->connect.status); + + if (event->connect.status == 0) { + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + btshell_conn_add(&desc); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + console_printf("disconnect; reason=%d ", event->disconnect.reason); + print_conn_desc(&event->disconnect.conn); + + conn_idx = btshell_conn_find_idx(event->disconnect.conn.conn_handle); + if (conn_idx != -1) { + btshell_conn_delete_idx(conn_idx); + } + + return btshell_restart_adv(event); +#if MYNEWT_VAL(BLE_EXT_ADV) + case BLE_GAP_EVENT_EXT_DISC: + btshell_decode_event_type(&event->ext_disc, arg); + return 0; +#endif + case BLE_GAP_EVENT_DISC: + console_printf("received advertisement; event_type=%d rssi=%d " + "addr_type=%d addr=", event->disc.event_type, + event->disc.rssi, event->disc.addr.type); + print_addr(event->disc.addr.val); + + /* + * There is no adv data to print in case of connectable + * directed advertising + */ + if (event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) { + console_printf("\nConnectable directed advertising event\n"); + return 0; + } + + btshell_decode_adv_data(event->disc.data, event->disc.length_data, arg); + + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE: + console_printf("connection updated; status=%d ", + event->conn_update.status); + rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE_REQ: + console_printf("connection update request\n"); + *event->conn_update_req.self_params = + *event->conn_update_req.peer_params; + return 0; + + case BLE_GAP_EVENT_PASSKEY_ACTION: + console_printf("passkey action event; action=%d", + event->passkey.params.action); + if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) { + console_printf(" numcmp=%lu", + (unsigned long)event->passkey.params.numcmp); + } + console_printf("\n"); + return 0; + + + case BLE_GAP_EVENT_DISC_COMPLETE: + console_printf("discovery complete; reason=%d\n", + event->disc_complete.reason); + return 0; + + case BLE_GAP_EVENT_ADV_COMPLETE: +#if MYNEWT_VAL(BLE_EXT_ADV) + console_printf("advertise complete; reason=%d, instance=%u, handle=%d\n", + event->adv_complete.reason, event->adv_complete.instance, + event->adv_complete.conn_handle); + + ext_adv_restart[event->adv_complete.instance].conn_handle = + event->adv_complete.conn_handle; +#else + console_printf("advertise complete; reason=%d\n", + event->adv_complete.reason); +#endif + return 0; + + case BLE_GAP_EVENT_ENC_CHANGE: + console_printf("encryption change event; status=%d ", + event->enc_change.status); + rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + return 0; + + case BLE_GAP_EVENT_NOTIFY_RX: + console_printf("notification rx event; attr_handle=%d indication=%d " + "len=%d data=", + event->notify_rx.attr_handle, + event->notify_rx.indication, + OS_MBUF_PKTLEN(event->notify_rx.om)); + + print_mbuf(event->notify_rx.om); + console_printf("\n"); + return 0; + + case BLE_GAP_EVENT_NOTIFY_TX: + console_printf("notification tx event; status=%d attr_handle=%d " + "indication=%d\n", + event->notify_tx.status, + event->notify_tx.attr_handle, + event->notify_tx.indication); + return 0; + + case BLE_GAP_EVENT_SUBSCRIBE: + console_printf("subscribe event; conn_handle=%d attr_handle=%d " + "reason=%d prevn=%d curn=%d previ=%d curi=%d\n", + event->subscribe.conn_handle, + event->subscribe.attr_handle, + event->subscribe.reason, + event->subscribe.prev_notify, + event->subscribe.cur_notify, + event->subscribe.prev_indicate, + event->subscribe.cur_indicate); + return 0; + + case BLE_GAP_EVENT_MTU: + console_printf("mtu update event; conn_handle=%d cid=%d mtu=%d\n", + event->mtu.conn_handle, + event->mtu.channel_id, + event->mtu.value); + return 0; + + case BLE_GAP_EVENT_IDENTITY_RESOLVED: + console_printf("identity resolved "); + rc = ble_gap_conn_find(event->identity_resolved.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + return 0; + + case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: + console_printf("PHY update complete; status=%d, conn_handle=%d " + " tx_phy=%d, rx_phy=%d\n", + event->phy_updated.status, + event->phy_updated.conn_handle, + event->phy_updated.tx_phy, + event->phy_updated.rx_phy); + return 0; + + case BLE_GAP_EVENT_REPEAT_PAIRING: + /* We already have a bond with the peer, but it is attempting to + * establish a new secure link. This app sacrifices security for + * convenience: just throw away the old bond and accept the new link. + */ + + /* Delete the old bond. */ + rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc); + assert(rc == 0); + ble_store_util_delete_peer(&desc.peer_id_addr); + + /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should + * continue with the pairing operation. + */ + return BLE_GAP_REPEAT_PAIRING_RETRY; +#if MYNEWT_VAL(BLE_PERIODIC_ADV) + case BLE_GAP_EVENT_PERIODIC_SYNC: + if (event->periodic_sync.status) { + console_printf("Periodic Sync Establishment Failed; status=%u\n", + event->periodic_sync.status); + } else { + console_printf("Periodic Sync Established; sync_handle=%u sid=%u " + "phy=%u adv_interval=%u ca=%u addr_type=%u addr=", + event->periodic_sync.sync_handle, + event->periodic_sync.sid, event->periodic_sync.adv_phy, + event->periodic_sync.per_adv_ival, + event->periodic_sync.adv_clk_accuracy, + event->periodic_sync.adv_addr.type); + print_addr(event->periodic_sync.adv_addr.val); + console_printf("\n"); + + /* TODO non-NimBLE controllers may not start handles from 0 */ + if (event->periodic_sync.sync_handle >= MYNEWT_VAL(BLE_MAX_PERIODIC_SYNCS)) { + console_printf("Unable to prepare cache for sync data\n"); + } else { + psync = &g_periodic_data[event->periodic_sync.sync_handle]; + memset(psync, 0, sizeof(*psync)); + psync->changed = true; + psync->established = true; + } + } + return 0; + case BLE_GAP_EVENT_PERIODIC_SYNC_LOST: + /* TODO non-NimBLE controllers may not start handles from 0 */ + if (event->periodic_sync_lost.sync_handle >= MYNEWT_VAL(BLE_MAX_PERIODIC_SYNCS)) { + console_printf("Periodic Sync Lost; sync_handle=%d reason=%d\n", + event->periodic_sync_lost.sync_handle, + event->periodic_sync_lost.reason); + } else { + psync = &g_periodic_data[event->periodic_sync_lost.sync_handle]; + + console_printf("Periodic Sync Lost; sync_handle=%d reason=%d completed=%u truncated=%u\n", + event->periodic_sync_lost.sync_handle, + event->periodic_sync_lost.reason, + psync->complete, psync->truncated); + + memset(psync, 0, sizeof(*psync)); + } + return 0; + case BLE_GAP_EVENT_PERIODIC_REPORT: + handle_periodic_report(event); + return 0; +#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_TRANSFER) + case BLE_GAP_EVENT_PERIODIC_TRANSFER: + console_printf("Periodic Sync Transfer Received on conn=%u; status=%u," + " sync_handle=%u sid=%u phy=%u adv_interval=%u ca=%u " + "addr_type=%u addr=", + event->periodic_transfer.conn_handle, + event->periodic_transfer.status, + event->periodic_transfer.sync_handle, + event->periodic_transfer.sid, + event->periodic_transfer.adv_phy, + event->periodic_transfer.per_adv_itvl, + event->periodic_transfer.adv_clk_accuracy, + event->periodic_transfer.adv_addr.type); + print_addr(event->periodic_transfer.adv_addr.val); + console_printf("\n"); + + if (!event->periodic_transfer.status) { + /* TODO non-NimBLE controllers may not start handles from 0 */ + if (event->periodic_transfer.sync_handle >= MYNEWT_VAL(BLE_MAX_PERIODIC_SYNCS)) { + console_printf("Unable to prepare cache for sync data\n"); + } else { + psync = &g_periodic_data[event->periodic_transfer.sync_handle]; + memset(psync, 0, sizeof(*psync)); + psync->changed = true; + psync->established = true; + } + } + return 0; +#endif +#endif + default: + return 0; + } +} + +static void +btshell_on_l2cap_update(uint16_t conn_handle, int status, void *arg) +{ + console_printf("l2cap update complete; conn_handle=%d status=%d\n", + conn_handle, status); +} + +static void +btshell_tx_timer_cb(struct os_event *ev) +{ + uint8_t i; + uint8_t len; + int32_t timeout; + struct ble_l2cap_hdr l2cap_hdr; + struct os_mbuf *om; + + if ((btshell_tx_data.tx_num == 0) || (btshell_tx_data.tx_len == 0)) { + return; + } + + console_printf("Sending %d/%d len: %d\n", + btshell_tx_data.tx_num_requested - btshell_tx_data.tx_num + 1, + btshell_tx_data.tx_num_requested, btshell_tx_data.tx_len); + + len = btshell_tx_data.tx_len; + + om = NULL; + if (os_msys_num_free() >= 4) { + om = os_msys_get_pkthdr(len + BLE_L2CAP_HDR_SZ, BLE_HCI_DATA_HDR_SZ); + } + + if (om) { + /* + * NOTE: CID is 0xffff so it is not confused with valid l2cap channel. + * The rest of the data gets filled with incrementing pattern starting + * from 0. + */ + put_le16(&l2cap_hdr.len, len); + put_le16(&l2cap_hdr.cid, 0xffff); + + os_mbuf_append(om, (void *)&l2cap_hdr, BLE_L2CAP_HDR_SZ); + + for (i = 0; i < len; ++i) { + os_mbuf_append(om, (void *)&i, 1); + } + + ble_hs_lock(); + ble_hs_hci_acl_tx_now(btshell_tx_data.conn, &om); + ble_hs_unlock(); + + --btshell_tx_data.tx_num; + } + + if (btshell_tx_data.tx_num) { + timeout = (int32_t)btshell_tx_data.tx_rate; + timeout = (timeout * OS_TICKS_PER_SEC) / 1000; + os_callout_reset(&btshell_tx_timer, timeout); + } +} + +int +btshell_exchange_mtu(uint16_t conn_handle) +{ + int rc; + + rc = ble_gattc_exchange_mtu(conn_handle, btshell_on_mtu, NULL); + return rc; +} + +int +btshell_disc_all_chrs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle) +{ + intptr_t svc_start_handle; + int rc; + + svc_start_handle = start_handle; + rc = ble_gattc_disc_all_chrs(conn_handle, start_handle, end_handle, + btshell_on_disc_c, (void *)svc_start_handle); + return rc; +} + +int +btshell_disc_all_chrs_in_svc(uint16_t conn_handle, struct btshell_svc *svc) +{ + int rc; + + rc = ble_gattc_disc_all_chrs(conn_handle, svc->svc.start_handle, + svc->svc.end_handle, btshell_on_disc_c_in_s, + svc); + return rc; +} + +int +btshell_disc_chrs_by_uuid(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, const ble_uuid_t *uuid) +{ + intptr_t svc_start_handle; + int rc; + + svc_start_handle = start_handle; + rc = ble_gattc_disc_chrs_by_uuid(conn_handle, start_handle, end_handle, + uuid, btshell_on_disc_c, + (void *)svc_start_handle); + return rc; +} + +int +btshell_disc_svcs(uint16_t conn_handle) +{ + int rc; + + rc = ble_gattc_disc_all_svcs(conn_handle, btshell_on_disc_s, NULL); + return rc; +} + +int +btshell_disc_svc_by_uuid(uint16_t conn_handle, const ble_uuid_t *uuid) +{ + int rc; + + rc = ble_gattc_disc_svc_by_uuid(conn_handle, uuid, + btshell_on_disc_s, NULL); + return rc; +} + +int +btshell_disc_all_dscs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle) +{ + int rc; + + rc = ble_gattc_disc_all_dscs(conn_handle, start_handle, end_handle, + btshell_on_disc_d, NULL); + return rc; +} + +int +btshell_disc_full(uint16_t conn_handle) +{ + struct btshell_conn *conn; + struct btshell_svc *svc; + + /* Undiscover everything first. */ + conn = btshell_conn_find(conn_handle); + if (conn == NULL) { + return BLE_HS_ENOTCONN; + } + + while ((svc = SLIST_FIRST(&conn->svcs)) != NULL) { + SLIST_REMOVE_HEAD(&conn->svcs, next); + btshell_svc_delete(svc); + } + + btshell_full_disc_prev_chr_val = 1; + btshell_disc_svcs(conn_handle); + + return 0; +} + +int +btshell_find_inc_svcs(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle) +{ + int rc; + + rc = ble_gattc_find_inc_svcs(conn_handle, start_handle, end_handle, + btshell_on_disc_s, NULL); + return rc; +} + +int +btshell_read(uint16_t conn_handle, uint16_t attr_handle) +{ + struct os_mbuf *om; + int rc; + + if (conn_handle == BLE_HS_CONN_HANDLE_NONE) { + rc = ble_att_svr_read_local(attr_handle, &om); + if (rc == 0) { + console_printf("read local; attr_handle=%d len=%d value=", + attr_handle, OS_MBUF_PKTLEN(om)); + print_mbuf(om); + console_printf("\n"); + + os_mbuf_free_chain(om); + } + } else { + rc = ble_gattc_read(conn_handle, attr_handle, btshell_on_read, NULL); + } + return rc; +} + +int +btshell_read_long(uint16_t conn_handle, uint16_t attr_handle, uint16_t offset) +{ + int rc; + + rc = ble_gattc_read_long(conn_handle, attr_handle, offset, + btshell_on_read, NULL); + return rc; +} + +int +btshell_read_by_uuid(uint16_t conn_handle, uint16_t start_handle, + uint16_t end_handle, const ble_uuid_t *uuid) +{ + int rc; + + rc = ble_gattc_read_by_uuid(conn_handle, start_handle, end_handle, uuid, + btshell_on_read, NULL); + return rc; +} + +int +btshell_read_mult(uint16_t conn_handle, uint16_t *attr_handles, + int num_attr_handles) +{ + int rc; + + rc = ble_gattc_read_mult(conn_handle, attr_handles, num_attr_handles, + btshell_on_read, NULL); + return rc; +} + +int +btshell_write(uint16_t conn_handle, uint16_t attr_handle, struct os_mbuf *om) +{ + int rc; + + if (conn_handle == BLE_HS_CONN_HANDLE_NONE) { + rc = ble_att_svr_write_local(attr_handle, om); + } else { + rc = ble_gattc_write(conn_handle, attr_handle, om, + btshell_on_write, NULL); + } + + return rc; +} + +int +btshell_write_no_rsp(uint16_t conn_handle, uint16_t attr_handle, + struct os_mbuf *om) +{ + int rc; + + rc = ble_gattc_write_no_rsp(conn_handle, attr_handle, om); + + return rc; +} + +int +btshell_write_long(uint16_t conn_handle, uint16_t attr_handle, + uint16_t offset, struct os_mbuf *om) +{ + int rc; + + rc = ble_gattc_write_long(conn_handle, attr_handle, offset, + om, btshell_on_write, NULL); + return rc; +} + +int +btshell_write_reliable(uint16_t conn_handle, + struct ble_gatt_attr *attrs, + int num_attrs) +{ + int rc; + + rc = ble_gattc_write_reliable(conn_handle, attrs, num_attrs, + btshell_on_write_reliable, NULL); + return rc; +} + +#if MYNEWT_VAL(BLE_EXT_ADV) +int +btshell_ext_adv_configure(uint8_t instance, + const struct ble_gap_ext_adv_params *params, + int8_t *selected_tx_power) +{ + return ble_gap_ext_adv_configure(instance, params, selected_tx_power, + btshell_gap_event, NULL); +} + +int +btshell_ext_adv_start(uint8_t instance, int duration, + int max_events, bool restart) +{ + int rc; + + /* Advertising restart doesn't make sense + * with limited duration or events + */ + if (restart && (duration == 0) && (max_events == 0)) { + ext_adv_restart[instance].restart = restart; + } + + rc = ble_gap_ext_adv_start(instance, duration, max_events); + + return rc; +} + +int +btshell_ext_adv_stop(uint8_t instance) +{ + int rc; + + ext_adv_restart[instance].restart = false; + + rc = ble_gap_ext_adv_stop(instance); + + return rc; +} +#endif + +int +btshell_adv_stop(void) +{ + int rc; + + adv_params.restart = false; + + rc = ble_gap_adv_stop(); + return rc; +} + +int +btshell_adv_start(uint8_t own_addr_type, const ble_addr_t *direct_addr, + int32_t duration_ms, const struct ble_gap_adv_params *params, + bool restart) +{ + int rc; + + if (restart) { + adv_params.restart = restart; + adv_params.own_addr_type = own_addr_type; + adv_params.duration_ms = duration_ms; + + if (direct_addr) { + memcpy(&adv_params.direct_addr, direct_addr, sizeof(adv_params.direct_addr)); + } + + if (params) { + memcpy(&adv_params.params, params, sizeof(adv_params.params)); + } + } + + rc = ble_gap_adv_start(own_addr_type, direct_addr, duration_ms, params, + btshell_gap_event, NULL); + return rc; +} + +int +btshell_conn_initiate(uint8_t own_addr_type, const ble_addr_t *peer_addr, + int32_t duration_ms, struct ble_gap_conn_params *params) +{ + int rc; + + rc = ble_gap_connect(own_addr_type, peer_addr, duration_ms, params, + btshell_gap_event, NULL); + + return rc; +} + +int +btshell_ext_conn_initiate(uint8_t own_addr_type, const ble_addr_t *peer_addr, + int32_t duration_ms, + struct ble_gap_conn_params *phy_1m_params, + struct ble_gap_conn_params *phy_2m_params, + struct ble_gap_conn_params *phy_coded_params) +{ +#if !MYNEWT_VAL(BLE_EXT_ADV) + console_printf("BLE extended advertising not supported."); + console_printf(" Configure nimble host to enable it\n"); + return 0; +#else + int rc; + uint8_t phy_mask = 0; + + if (phy_1m_params) { + phy_mask |= BLE_GAP_LE_PHY_1M_MASK; + } + + if (phy_2m_params) { + phy_mask |= BLE_GAP_LE_PHY_2M_MASK; + } + + if (phy_coded_params) { + phy_mask |= BLE_GAP_LE_PHY_CODED_MASK; + } + + rc = ble_gap_ext_connect(own_addr_type, peer_addr, duration_ms, phy_mask, + phy_1m_params, phy_2m_params, phy_coded_params, + btshell_gap_event, NULL); + + return rc; +#endif +} + +int +btshell_conn_cancel(void) +{ + int rc; + + rc = ble_gap_conn_cancel(); + return rc; +} + +int +btshell_term_conn(uint16_t conn_handle, uint8_t reason) +{ + int rc; + + rc = ble_gap_terminate(conn_handle, reason); + return rc; +} + +int +btshell_wl_set(ble_addr_t *addrs, int addrs_count) +{ + int rc; + + rc = ble_gap_wl_set(addrs, addrs_count); + return rc; +} + +int +btshell_scan(uint8_t own_addr_type, int32_t duration_ms, + const struct ble_gap_disc_params *disc_params, void *cb_args) +{ + int rc; + + rc = ble_gap_disc(own_addr_type, duration_ms, disc_params, + btshell_gap_event, cb_args); + return rc; +} + +int +btshell_ext_scan(uint8_t own_addr_type, uint16_t duration, uint16_t period, + uint8_t filter_duplicates, uint8_t filter_policy, + uint8_t limited, + const struct ble_gap_ext_disc_params *uncoded_params, + const struct ble_gap_ext_disc_params *coded_params, + void *cb_args) +{ +#if !MYNEWT_VAL(BLE_EXT_ADV) + console_printf("BLE extended advertising not supported."); + console_printf(" Configure nimble host to enable it\n"); + return 0; +#else + int rc; + + rc = ble_gap_ext_disc(own_addr_type, duration, period, filter_duplicates, + filter_policy, limited, uncoded_params, coded_params, + btshell_gap_event, cb_args); + return rc; +#endif +} + +int +btshell_scan_cancel(void) +{ + int rc; + + rc = ble_gap_disc_cancel(); + return rc; +} + +int +btshell_update_conn(uint16_t conn_handle, struct ble_gap_upd_params *params) +{ + int rc; + + rc = ble_gap_update_params(conn_handle, params); + return rc; +} + +void +btshell_notify(uint16_t attr_handle) +{ + ble_gatts_chr_updated(attr_handle); +} + +int +btshell_datalen(uint16_t conn_handle, uint16_t tx_octets, uint16_t tx_time) +{ + int rc; + + rc = ble_hs_hci_util_set_data_len(conn_handle, tx_octets, tx_time); + return rc; +} + +int +btshell_l2cap_update(uint16_t conn_handle, + struct ble_l2cap_sig_update_params *params) +{ + int rc; + + rc = ble_l2cap_sig_update(conn_handle, params, btshell_on_l2cap_update, + NULL); + return rc; +} + +int +btshell_sec_pair(uint16_t conn_handle) +{ +#if !NIMBLE_BLE_SM + return BLE_HS_ENOTSUP; +#endif + + int rc; + + rc = ble_gap_pair_initiate(conn_handle); + return rc; +} + +int +btshell_sec_unpair(ble_addr_t *peer_addr) +{ +#if !NIMBLE_BLE_SM + return BLE_HS_ENOTSUP; +#endif + + int rc; + + rc = ble_gap_unpair(peer_addr); + return rc; +} + +int +btshell_sec_start(uint16_t conn_handle) +{ +#if !NIMBLE_BLE_SM + return BLE_HS_ENOTSUP; +#endif + + int rc; + + rc = ble_gap_security_initiate(conn_handle); + return rc; +} + +int +btshell_sec_restart(uint16_t conn_handle, + uint8_t key_size, + uint8_t *ltk, + uint16_t ediv, + uint64_t rand_val, + int auth) +{ +#if !NIMBLE_BLE_SM + return BLE_HS_ENOTSUP; +#endif + + struct ble_store_value_sec value_sec; + struct ble_store_key_sec key_sec; + struct ble_gap_conn_desc desc; + ble_hs_conn_flags_t conn_flags; + int rc; + + if (ltk == NULL) { + /* The user is requesting a store lookup. */ + rc = ble_gap_conn_find(conn_handle, &desc); + if (rc != 0) { + return rc; + } + + memset(&key_sec, 0, sizeof key_sec); + key_sec.peer_addr = desc.peer_id_addr; + + rc = ble_hs_atomic_conn_flags(conn_handle, &conn_flags); + if (rc != 0) { + return rc; + } + if (conn_flags & BLE_HS_CONN_F_MASTER) { + rc = ble_store_read_peer_sec(&key_sec, &value_sec); + } else { + rc = ble_store_read_our_sec(&key_sec, &value_sec); + } + if (rc != 0) { + return rc; + } + + ltk = value_sec.ltk; + key_size = value_sec.key_size; + ediv = value_sec.ediv; + rand_val = value_sec.rand_num; + auth = value_sec.authenticated; + } + + rc = ble_gap_encryption_initiate(conn_handle, key_size, ltk, + ediv, rand_val, auth); + return rc; +} + +/** + * Called to start transmitting 'num' packets at rate 'rate' of size 'size' + * to connection handle 'handle' + * + * @param handle + * @param len + * @param rate + * @param num + * + * @return int + */ +int +btshell_tx_start(uint16_t conn_handle, uint16_t len, uint16_t rate, uint16_t num) +{ + /* Cannot be currently in a session */ + if (num == 0) { + return 0; + } + + /* Do not allow start if already in progress */ + if (btshell_tx_data.tx_num != 0) { + return -1; + } + + /* XXX: for now, must have contiguous mbuf space */ + if ((len + 4) > MYNEWT_VAL_MSYS_1_BLOCK_SIZE) { + return -2; + } + + btshell_tx_data.tx_num = num; + btshell_tx_data.tx_num_requested = num; + btshell_tx_data.tx_rate = rate; + btshell_tx_data.tx_len = len; + btshell_tx_data.tx_conn_handle = conn_handle; + + ble_hs_lock(); + btshell_tx_data.conn = ble_hs_conn_find(conn_handle); + ble_hs_unlock(); + + if (!btshell_tx_data.conn) { + console_printf("Could not find ble_hs_conn for handle: %d\n", + conn_handle); + return -1; + } + + os_callout_reset(&btshell_tx_timer, 0); + + return 0; +} + +void +btshell_tx_stop(void) +{ + os_callout_stop(&btshell_tx_timer); + btshell_tx_data.tx_num = 0; +} + +int +btshell_rssi(uint16_t conn_handle, int8_t *out_rssi) +{ + int rc; + + rc = ble_gap_conn_rssi(conn_handle, out_rssi); + if (rc != 0) { + return rc; + } + + return 0; +} + +static void +btshell_on_reset(int reason) +{ + console_printf("Error: Resetting state; reason=%d\n", reason); +} + +static void +btshell_on_sync(void) +{ +#if MYNEWT_VAL(BLE_SM_SC) + int rc; + + rc = ble_sm_sc_oob_generate_data(&oob_data_local); + if (rc) { + console_printf("Error: generating oob data; reason=%d\n", rc); + return; + } +#endif + + console_printf("Host and controller synced\n"); +} + +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0 + +static int +btshell_l2cap_coc_add(uint16_t conn_handle, struct ble_l2cap_chan *chan) +{ + struct btshell_conn *conn; + struct btshell_l2cap_coc *coc; + struct btshell_l2cap_coc *prev, *cur; + + conn = btshell_conn_find(conn_handle); + assert(conn != NULL); + + coc = os_memblock_get(&btshell_coc_conn_pool); + if (!coc) { + return ENOMEM; + } + + coc->chan = chan; + + prev = NULL; + SLIST_FOREACH(cur, &conn->coc_list, next) { + prev = cur; + } + + if (prev == NULL) { + SLIST_INSERT_HEAD(&conn->coc_list, coc, next); + } else { + SLIST_INSERT_AFTER(prev, coc, next); + } + + return 0; +} + +static void +btshell_l2cap_coc_remove(uint16_t conn_handle, struct ble_l2cap_chan *chan) +{ + struct btshell_conn *conn; + struct btshell_l2cap_coc *coc; + struct btshell_l2cap_coc *cur; + + conn = btshell_conn_find(conn_handle); + assert(conn != NULL); + + coc = NULL; + SLIST_FOREACH(cur, &conn->coc_list, next) { + if (cur->chan == chan) { + coc = cur; + break; + } + } + + if (!coc) { + return; + } + + SLIST_REMOVE(&conn->coc_list, coc, btshell_l2cap_coc, next); + os_memblock_put(&btshell_coc_conn_pool, coc); +} + +static void +btshell_l2cap_coc_recv(struct ble_l2cap_chan *chan, struct os_mbuf *sdu) +{ + console_printf("LE CoC SDU received, chan: 0x%08lx, data len %d\n", + (uint32_t) chan, OS_MBUF_PKTLEN(sdu)); + + os_mbuf_free_chain(sdu); + sdu = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0); + assert(sdu != NULL); + + if (ble_l2cap_recv_ready(chan, sdu) != 0) { + assert(0); + } +} + +static int +btshell_l2cap_coc_accept(uint16_t conn_handle, uint16_t peer_mtu, + struct ble_l2cap_chan *chan) +{ + struct os_mbuf *sdu_rx; + + console_printf("LE CoC accepting, chan: 0x%08lx, peer_mtu %d\n", + (uint32_t) chan, peer_mtu); + + sdu_rx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0); + if (!sdu_rx) { + return BLE_HS_ENOMEM; + } + + return ble_l2cap_recv_ready(chan, sdu_rx); +} + +static void +btshell_l2cap_coc_unstalled(uint16_t conn_handle, struct ble_l2cap_chan *chan) +{ + struct btshell_conn *conn; + struct btshell_l2cap_coc *coc; + struct btshell_l2cap_coc *cur; + + conn = btshell_conn_find(conn_handle); + assert(conn != NULL); + + coc = NULL; + SLIST_FOREACH(cur, &conn->coc_list, next) { + if (cur->chan == chan) { + coc = cur; + break; + } + } + + if (!coc) { + return; + } + + coc->stalled = false; +} + +static int +btshell_l2cap_event(struct ble_l2cap_event *event, void *arg) +{ + int accept_response; + struct ble_l2cap_chan_info chan_info; + + switch(event->type) { + case BLE_L2CAP_EVENT_COC_CONNECTED: + if (event->connect.status) { + console_printf("LE COC error: %d\n", event->connect.status); + return 0; + } + + if (ble_l2cap_get_chan_info(event->connect.chan, &chan_info)) { + assert(0); + } + + console_printf("LE COC connected, conn: %d, chan: %p, psm: 0x%02x, scid: 0x%04x, " + "dcid: 0x%04x, our_mps: %d, our_mtu: %d, peer_mps: %d, peer_mtu: %d\n", + event->connect.conn_handle, event->connect.chan, + chan_info.psm, chan_info.scid, chan_info.dcid, + chan_info.our_l2cap_mtu, chan_info.our_coc_mtu, chan_info.peer_l2cap_mtu, chan_info.peer_coc_mtu); + + btshell_l2cap_coc_add(event->connect.conn_handle, + event->connect.chan); + + return 0; + case BLE_L2CAP_EVENT_COC_DISCONNECTED: + console_printf("LE CoC disconnected, chan: %p\n", + event->disconnect.chan); + + btshell_l2cap_coc_remove(event->disconnect.conn_handle, + event->disconnect.chan); + return 0; + case BLE_L2CAP_EVENT_COC_ACCEPT: + accept_response = PTR_TO_INT(arg); + if (accept_response) { + return accept_response; + } + + return btshell_l2cap_coc_accept(event->accept.conn_handle, + event->accept.peer_sdu_size, + event->accept.chan); + + case BLE_L2CAP_EVENT_COC_DATA_RECEIVED: + btshell_l2cap_coc_recv(event->receive.chan, event->receive.sdu_rx); + return 0; + case BLE_L2CAP_EVENT_COC_RECONFIG_COMPLETED: + + if (ble_l2cap_get_chan_info(event->reconfigured.chan, &chan_info)) { + assert(0); + } + + console_printf("LE CoC reconfigure completed status 0x%02x," \ + "chan: %p\n", + event->reconfigured.status, + event->reconfigured.chan); + + if (event->reconfigured.status == 0) { + console_printf("\t our_mps: %d our_mtu %d\n", chan_info.our_l2cap_mtu, chan_info.our_coc_mtu); + } + return 0; + case BLE_L2CAP_EVENT_COC_PEER_RECONFIGURED: + + if (ble_l2cap_get_chan_info(event->reconfigured.chan, &chan_info)) { + assert(0); + } + + console_printf("LE CoC peer reconfigured status 0x%02x," \ + "chan: %p\n", + event->reconfigured.status, + event->reconfigured.chan); + + if (event->reconfigured.status == 0) { + console_printf("\t peer_mps: %d peer_mtu %d\n", chan_info.peer_l2cap_mtu, chan_info.peer_coc_mtu); + } + + return 0; + case BLE_L2CAP_EVENT_COC_TX_UNSTALLED: + console_printf("L2CAP CoC channel %p unstalled, last sdu sent with err=0x%02x\n", + event->tx_unstalled.chan, event->tx_unstalled.status); + btshell_l2cap_coc_unstalled(event->tx_unstalled.conn_handle, event->tx_unstalled.chan); + return 0; + default: + return 0; + } +} +#endif + +int +btshell_l2cap_create_srv(uint16_t psm, uint16_t mtu, int accept_response) +{ +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) == 0 + console_printf("BLE L2CAP LE COC not supported."); + console_printf(" Configure nimble host to enable it\n"); + return 0; +#else + + if (mtu == 0 || mtu > BTSHELL_COC_MTU) { + mtu = BTSHELL_COC_MTU; + } + + return ble_l2cap_create_server(psm, mtu, btshell_l2cap_event, + INT_TO_PTR(accept_response)); +#endif +} + +int +btshell_l2cap_connect(uint16_t conn_handle, uint16_t psm, uint16_t mtu, uint8_t num) +{ +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) == 0 + console_printf("BLE L2CAP LE COC not supported."); + console_printf(" Configure nimble host to enable it\n"); + return 0; +#else + + struct os_mbuf *sdu_rx[num]; + int i; + + if (mtu == 0 || mtu > BTSHELL_COC_MTU) { + mtu = BTSHELL_COC_MTU; + } + + console_printf("L2CAP CoC MTU: %d, max available %d\n", mtu, BTSHELL_COC_MTU); + + for (i = 0; i < num; i++) { + sdu_rx[i] = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0); + assert(sdu_rx != NULL); + } + + if (num == 1) { + return ble_l2cap_connect(conn_handle, psm, mtu, sdu_rx[0], + btshell_l2cap_event, NULL); + } + + return ble_l2cap_enhanced_connect(conn_handle, psm, mtu, + num, sdu_rx,btshell_l2cap_event, NULL); +#endif +} + +int +btshell_l2cap_disconnect(uint16_t conn_handle, uint16_t idx) +{ +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) == 0 + console_printf("BLE L2CAP LE COC not supported."); + console_printf(" Configure nimble host to enable it\n"); + return 0; +#else + + struct btshell_conn *conn; + struct btshell_l2cap_coc *coc; + int i; + int rc = 0; + + conn = btshell_conn_find(conn_handle); + assert(conn != NULL); + + i = 0; + SLIST_FOREACH(coc, &conn->coc_list, next) { + if (i == idx) { + break; + } + i++; + } + assert(coc != NULL); + + rc = ble_l2cap_disconnect(coc->chan); + if (rc) { + console_printf("Could not disconnect channel rc=%d\n", rc); + } + + return rc; +#endif +} + +int +btshell_l2cap_reconfig(uint16_t conn_handle, uint16_t mtu, + uint8_t num, uint8_t idxs[]) +{ + struct btshell_conn *conn; + struct btshell_l2cap_coc *coc; + struct ble_l2cap_chan * chans[5] = {0}; + int i, j; + int cnt; + + conn = btshell_conn_find(conn_handle); + if (conn == NULL) { + console_printf("conn=%d does not exist\n", conn_handle); + return 0; + } + + i = 0; + j = 0; + cnt = 0; + SLIST_FOREACH(coc, &conn->coc_list, next) { + for (i = 0; i < num; i++) { + if (idxs[i] == j) { + chans[cnt] = coc->chan; + cnt++; + break; + } + } + j++; + } + + if (cnt != num) { + console_printf("Missing coc? (%d!=%d)\n", num, cnt); + return BLE_HS_EINVAL; + } + + return ble_l2cap_reconfig(chans, cnt, mtu); +} + +int +btshell_l2cap_send(uint16_t conn_handle, uint16_t idx, uint16_t bytes) +{ +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) == 0 + console_printf("BLE L2CAP LE COC not supported."); + console_printf(" Configure nimble host to enable it\n"); + return 0; +#else + + struct btshell_conn *conn; + struct btshell_l2cap_coc *coc; + struct os_mbuf *sdu_tx; + uint8_t b[] = {0x00, 0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88, 0x99}; + int i; + int rc; + + console_printf("conn=%d, idx=%d, bytes=%d\n", conn_handle, idx, bytes); + + conn = btshell_conn_find(conn_handle); + if (conn == NULL) { + console_printf("conn=%d does not exist\n", conn_handle); + return 0; + } + + i = 0; + SLIST_FOREACH(coc, &conn->coc_list, next) { + if (i == idx) { + break; + } + i++; + } + if (coc == NULL) { + console_printf("Are you sure your channel exist?\n"); + return 0; + } + + if (coc->stalled) { + console_printf("Channel is stalled, wait ...\n"); + return 0; + } + + sdu_tx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0); + if (sdu_tx == NULL) { + console_printf("No memory in the test sdu pool\n"); + return 0; + } + + /* For the testing purpose we fill up buffer with known data, easy + * to validate on other side. In this loop we add as many full chunks as we + * can + */ + for (i = 0; i < bytes / sizeof(b); i++) { + rc = os_mbuf_append(sdu_tx, b, sizeof(b)); + if (rc) { + console_printf("Cannot append data %i !\n", i); + os_mbuf_free_chain(sdu_tx); + return rc; + } + } + + /* Here we add the rest < sizeof(b) */ + rc = os_mbuf_append(sdu_tx, b, bytes - (sizeof(b) * i)); + if (rc) { + console_printf("Cannot append data %i !\n", i); + os_mbuf_free_chain(sdu_tx); + return rc; + } + + rc = ble_l2cap_send(coc->chan, sdu_tx); + if (rc) { + if (rc == BLE_HS_ESTALLED) { + console_printf("CoC module is stalled with data. Wait for unstalled \n"); + coc->stalled = true; + } else { + console_printf("Could not send data rc=%d\n", rc); + } + os_mbuf_free_chain(sdu_tx); + } + + return rc; + +#endif +} + +static void +btshell_init_ext_adv_restart(void) +{ +#if MYNEWT_VAL(BLE_EXT_ADV) + int i; + + for (i = 0; i < BLE_ADV_INSTANCES; ++i) { + ext_adv_restart[i].conn_handle = BLE_HS_CONN_HANDLE_NONE; + } +#endif +} + +/** + * main + * + * The main task for the project. This function initializes the packages, + * then starts serving events from default event queue. + * + * @return int NOTE: this function should never return! + */ +int +main(int argc, char **argv) +{ + int rc; + +#ifdef ARCH_sim + mcu_sim_parse_args(argc, argv); +#endif + + /* Initialize OS */ + sysinit(); + + /* Initialize some application specific memory pools. */ + rc = os_mempool_init(&btshell_svc_pool, BTSHELL_MAX_SVCS, + sizeof (struct btshell_svc), btshell_svc_mem, + "btshell_svc_pool"); + assert(rc == 0); + + rc = os_mempool_init(&btshell_chr_pool, BTSHELL_MAX_CHRS, + sizeof (struct btshell_chr), btshell_chr_mem, + "btshell_chr_pool"); + assert(rc == 0); + + rc = os_mempool_init(&btshell_dsc_pool, BTSHELL_MAX_DSCS, + sizeof (struct btshell_dsc), btshell_dsc_mem, + "btshell_dsc_pool"); + assert(rc == 0); + +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0 + /* For testing we want to support all the available channels */ + rc = os_mempool_init(&sdu_coc_mbuf_mempool, BTSHELL_COC_BUF_COUNT, + BTSHELL_COC_MTU, btshell_sdu_coc_mem, + "btshell_coc_sdu_pool"); + assert(rc == 0); + + rc = os_mbuf_pool_init(&sdu_os_mbuf_pool, &sdu_coc_mbuf_mempool, + BTSHELL_COC_MTU, BTSHELL_COC_BUF_COUNT); + assert(rc == 0); + + rc = os_mempool_init(&btshell_coc_conn_pool, + MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM), + sizeof (struct btshell_l2cap_coc), btshell_coc_conn_mem, + "btshell_coc_conn_pool"); + assert(rc == 0); +#endif + + /* Initialize the NimBLE host configuration. */ + ble_hs_cfg.reset_cb = btshell_on_reset; + ble_hs_cfg.sync_cb = btshell_on_sync; + ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb; + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + + rc = gatt_svr_init(); + assert(rc == 0); + + cmd_init(); + + /* Set the default device name. */ + rc = ble_svc_gap_device_name_set("nimble-btshell"); + assert(rc == 0); + + /* Create a callout (timer). This callout is used by the "tx" btshell + * command to repeatedly send packets of sequential data bytes. + */ + os_callout_init(&btshell_tx_timer, os_eventq_dflt_get(), + btshell_tx_timer_cb, NULL); + + btshell_init_ext_adv_restart(); + + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + /* os start should never return. If it does, this should be an error */ + assert(0); + + return 0; +} diff --git a/src/libs/mynewt-nimble/apps/btshell/src/misc.c b/src/libs/mynewt-nimble/apps/btshell/src/misc.c new file mode 100644 index 00000000..e100eb79 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/btshell/src/misc.c @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "console/console.h" +#include "host/ble_uuid.h" +#include "host/ble_gap.h" + +#include "btshell.h" + +/** + * Utility function to log an array of bytes. + */ +void +print_bytes(const uint8_t *bytes, int len) +{ + int i; + + for (i = 0; i < len; i++) { + console_printf("%s0x%02x", i != 0 ? ":" : "", bytes[i]); + } +} + +void +print_mbuf(const struct os_mbuf *om) +{ + int colon; + + colon = 0; + while (om != NULL) { + if (colon) { + console_printf(":"); + } else { + colon = 1; + } + print_bytes(om->om_data, om->om_len); + om = SLIST_NEXT(om, om_next); + } +} + +void +print_addr(const void *addr) +{ + const uint8_t *u8p; + + u8p = addr; + console_printf("%02x:%02x:%02x:%02x:%02x:%02x", + u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); +} + +void +print_uuid(const ble_uuid_t *uuid) +{ + char buf[BLE_UUID_STR_LEN]; + + ble_uuid_to_str(uuid, buf); + + console_printf("%s", buf); +} + +int +svc_is_empty(const struct btshell_svc *svc) +{ + return svc->svc.end_handle <= svc->svc.start_handle; +} + +uint16_t +chr_end_handle(const struct btshell_svc *svc, const struct btshell_chr *chr) +{ + const struct btshell_chr *next_chr; + + next_chr = SLIST_NEXT(chr, next); + if (next_chr != NULL) { + return next_chr->chr.def_handle - 1; + } else { + return svc->svc.end_handle; + } +} + +int +chr_is_empty(const struct btshell_svc *svc, const struct btshell_chr *chr) +{ + return chr_end_handle(svc, chr) <= chr->chr.val_handle; +} + +void +print_conn_desc(const struct ble_gap_conn_desc *desc) +{ + console_printf("handle=%d our_ota_addr_type=%d our_ota_addr=", + desc->conn_handle, desc->our_ota_addr.type); + print_addr(desc->our_ota_addr.val); + console_printf(" our_id_addr_type=%d our_id_addr=", + desc->our_id_addr.type); + print_addr(desc->our_id_addr.val); + console_printf(" peer_ota_addr_type=%d peer_ota_addr=", + desc->peer_ota_addr.type); + print_addr(desc->peer_ota_addr.val); + console_printf(" peer_id_addr_type=%d peer_id_addr=", + desc->peer_id_addr.type); + print_addr(desc->peer_id_addr.val); + console_printf(" conn_itvl=%d conn_latency=%d supervision_timeout=%d" + " key_size=%d encrypted=%d authenticated=%d bonded=%d\n", + desc->conn_itvl, desc->conn_latency, + desc->supervision_timeout, + desc->sec_state.key_size, + desc->sec_state.encrypted, + desc->sec_state.authenticated, + desc->sec_state.bonded); +} + +static void +print_dsc(struct btshell_dsc *dsc) +{ + console_printf(" dsc_handle=%d uuid=", dsc->dsc.handle); + print_uuid(&dsc->dsc.uuid.u); + console_printf("\n"); +} + +static void +print_chr(struct btshell_chr *chr) +{ + struct btshell_dsc *dsc; + + console_printf(" def_handle=%d val_handle=%d properties=0x%02x " + "uuid=", chr->chr.def_handle, chr->chr.val_handle, + chr->chr.properties); + print_uuid(&chr->chr.uuid.u); + console_printf("\n"); + + SLIST_FOREACH(dsc, &chr->dscs, next) { + print_dsc(dsc); + } +} + +void +print_svc(struct btshell_svc *svc) +{ + struct btshell_chr *chr; + + console_printf(" start=%d end=%d uuid=", svc->svc.start_handle, + svc->svc.end_handle); + print_uuid(&svc->svc.uuid.u); + console_printf("\n"); + + SLIST_FOREACH(chr, &svc->chrs, next) { + print_chr(chr); + } +} diff --git a/src/libs/mynewt-nimble/apps/btshell/src/parse.c b/src/libs/mynewt-nimble/apps/btshell/src/parse.c new file mode 100644 index 00000000..d8018c5c --- /dev/null +++ b/src/libs/mynewt-nimble/apps/btshell/src/parse.c @@ -0,0 +1,734 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "console/console.h" +#include "host/ble_hs.h" +#include "host/ble_uuid.h" +#include "host/ble_eddystone.h" +#include "cmd.h" +#include "btshell.h" + +#define CMD_MAX_ARGS 16 + +static char *cmd_args[CMD_MAX_ARGS][2]; +static int cmd_num_args; + +int +parse_arg_find_idx(const char *key) +{ + int i; + + for (i = 0; i < cmd_num_args; i++) { + if (strcmp(cmd_args[i][0], key) == 0) { + return i; + } + } + + return -1; +} + +char * +parse_arg_peek(const char *key) +{ + int i; + + for (i = 0; i < cmd_num_args; i++) { + if (strcmp(cmd_args[i][0], key) == 0) { + return cmd_args[i][1]; + } + } + + return NULL; +} + +char * +parse_arg_extract(const char *key) +{ + int i; + + for (i = 0; i < cmd_num_args; i++) { + if (strcmp(cmd_args[i][0], key) == 0) { + /* Erase parameter. */ + cmd_args[i][0][0] = '\0'; + + return cmd_args[i][1]; + } + } + + return NULL; +} + +/** + * Determines which number base to use when parsing the specified numeric + * string. This just avoids base '0' so that numbers don't get interpreted as + * octal. + */ +static int +parse_arg_long_base(char *sval) +{ + if (sval[0] == '0' && sval[1] == 'x') { + return 0; + } else { + return 10; + } +} + +long +parse_long_bounds(char *sval, long min, long max, int *out_status) +{ + char *endptr; + long lval; + + lval = strtol(sval, &endptr, parse_arg_long_base(sval)); + if (sval[0] != '\0' && *endptr == '\0' && + lval >= min && lval <= max) { + + *out_status = 0; + return lval; + } + + *out_status = EINVAL; + return 0; +} + +long +parse_arg_long_bounds_peek(char *name, long min, long max, int *out_status) +{ + char *sval; + + sval = parse_arg_peek(name); + if (sval == NULL) { + *out_status = ENOENT; + return 0; + } + return parse_long_bounds(sval, min, max, out_status); +} + +long +parse_arg_long_bounds(char *name, long min, long max, int *out_status) +{ + char *sval; + + sval = parse_arg_extract(name); + if (sval == NULL) { + *out_status = ENOENT; + return 0; + } + return parse_long_bounds(sval, min, max, out_status); +} + +long +parse_arg_long_bounds_dflt(char *name, long min, long max, + long dflt, int *out_status) +{ + long val; + int rc; + + val = parse_arg_long_bounds(name, min, max, &rc); + if (rc == ENOENT) { + rc = 0; + val = dflt; + } + + *out_status = rc; + + return val; +} + +uint64_t +parse_arg_uint64_bounds(char *name, uint64_t min, uint64_t max, int *out_status) +{ + char *endptr; + char *sval; + uint64_t lval; + + sval = parse_arg_extract(name); + if (sval == NULL) { + *out_status = ENOENT; + return 0; + } + + lval = strtoull(sval, &endptr, parse_arg_long_base(sval)); + if (sval[0] != '\0' && *endptr == '\0' && + lval >= min && lval <= max) { + + *out_status = 0; + return lval; + } + + *out_status = EINVAL; + return 0; +} + +long +parse_arg_long(char *name, int *out_status) +{ + return parse_arg_long_bounds(name, LONG_MIN, LONG_MAX, out_status); +} + +uint8_t +parse_arg_bool(char *name, int *out_status) +{ + return parse_arg_long_bounds(name, 0, 1, out_status); +} + +uint8_t +parse_arg_bool_dflt(char *name, uint8_t dflt, int *out_status) +{ + return parse_arg_long_bounds_dflt(name, 0, 1, dflt, out_status); +} + +uint8_t +parse_arg_uint8(char *name, int *out_status) +{ + return parse_arg_long_bounds(name, 0, UINT8_MAX, out_status); +} + +uint16_t +parse_arg_uint16(char *name, int *out_status) +{ + return parse_arg_long_bounds(name, 0, UINT16_MAX, out_status); +} + +uint16_t +parse_arg_uint16_peek(char *name, int *out_status) +{ + return parse_arg_long_bounds_peek(name, 0, UINT16_MAX, out_status); +} + +uint32_t +parse_arg_uint32(char *name, int *out_status) +{ + return parse_arg_uint64_bounds(name, 0, UINT32_MAX, out_status); +} + +uint64_t +parse_arg_uint64(char *name, int *out_status) +{ + return parse_arg_uint64_bounds(name, 0, UINT64_MAX, out_status); +} + +uint8_t +parse_arg_uint8_dflt(char *name, uint8_t dflt, int *out_status) +{ + uint8_t val; + int rc; + + val = parse_arg_uint8(name, &rc); + if (rc == ENOENT) { + val = dflt; + rc = 0; + } + + *out_status = rc; + return val; +} + +uint16_t +parse_arg_uint16_dflt(char *name, uint16_t dflt, int *out_status) +{ + uint16_t val; + int rc; + + val = parse_arg_uint16(name, &rc); + if (rc == ENOENT) { + val = dflt; + rc = 0; + } + + *out_status = rc; + return val; +} + +uint32_t +parse_arg_uint32_dflt(char *name, uint32_t dflt, int *out_status) +{ + uint32_t val; + int rc; + + val = parse_arg_uint32(name, &rc); + if (rc == ENOENT) { + val = dflt; + rc = 0; + } + + *out_status = rc; + return val; +} + +static uint32_t +parse_time_unit_mult(const char *str) +{ + if (!strcasecmp(str, "us")) { + return 1; + } else if (!strcasecmp(str, "ms")) { + return 1000; + } else if (!strcasecmp(str, "s")) { + return 1000000; + } + + return 0; +} + +static uint32_t +parse_time_us(const char *str, int *out_status) +{ + uint32_t val = 0; + uint32_t val_div = 1; + uint32_t val_mult = 1; + uint32_t val_us; + + while (isdigit(*str)) { + val *= 10; + val += *str - '0'; + str++; + } + + if (*str == '.') { + str++; + while (isdigit(*str)) { + val *= 10; + val += *str - '0'; + val_div *= 10; + str++; + } + } + + val_mult = parse_time_unit_mult(str); + if (val_mult == 0) { + *out_status = EINVAL; + return 0; + } + + if (val_mult > val_div) { + val_us = val * (val_mult / val_div); + } else { + val_us = val * (val_div / val_mult); + } + + *out_status = 0; + + return val_us; +} + +uint32_t +parse_arg_time_dflt(char *name, int step_us, uint32_t dflt, int *out_status) +{ + const char *arg; + uint32_t val; + int rc; + + arg = parse_arg_peek(name); + if (!arg) { + *out_status = 0; + return dflt; + } + + val = parse_time_us(arg, &rc); + if (rc) { + val = parse_arg_uint32(name, &rc); + if (rc == ENOENT) { + *out_status = 0; + return dflt; + } + } else { + val /= step_us; + parse_arg_extract(name); + } + + *out_status = rc; + return val; +} + +const struct kv_pair * +parse_kv_find(const struct kv_pair *kvs, char *name) +{ + const struct kv_pair *kv; + int i; + + for (i = 0; kvs[i].key != NULL; i++) { + kv = kvs + i; + if (strcmp(name, kv->key) == 0) { + return kv; + } + } + + return NULL; +} + +int +parse_arg_kv(char *name, const struct kv_pair *kvs, int *out_status) +{ + const struct kv_pair *kv; + char *sval; + + sval = parse_arg_extract(name); + if (sval == NULL) { + *out_status = ENOENT; + return -1; + } + + kv = parse_kv_find(kvs, sval); + if (kv == NULL) { + *out_status = EINVAL; + return -1; + } + + *out_status = 0; + return kv->val; +} + +int +parse_arg_kv_dflt(char *name, const struct kv_pair *kvs, int def_val, + int *out_status) +{ + int val; + int rc; + + val = parse_arg_kv(name, kvs, &rc); + if (rc == ENOENT) { + rc = 0; + val = def_val; + } + + *out_status = rc; + + return val; +} + + +static int +parse_arg_byte_stream_delim(char *sval, char *delims, int max_len, + uint8_t *dst, int *out_len) +{ + unsigned long ul; + char *endptr; + char *token; + int i; + + i = 0; + for (token = strtok(sval, delims); + token != NULL; + token = strtok(NULL, delims)) { + + if (i >= max_len) { + return EINVAL; + } + + ul = strtoul(token, &endptr, 16); + if (sval[0] == '\0' || *endptr != '\0' || ul > UINT8_MAX) { + return -1; + } + + dst[i] = ul; + i++; + } + + *out_len = i; + + return 0; +} + +int +parse_arg_byte_stream(char *name, int max_len, uint8_t *dst, int *out_len) +{ + char *sval; + + sval = parse_arg_extract(name); + if (sval == NULL) { + return ENOENT; + } + + return parse_arg_byte_stream_delim(sval, ":-", max_len, dst, out_len); +} + +int +parse_arg_uint8_list_with_separator(char *name, char *separator, int max_len, + uint8_t *dst, int *out_len) +{ + char *sval; + + sval = parse_arg_extract(name); + if (sval == NULL) { + return ENOENT; + } + + return parse_arg_byte_stream_delim(sval, separator, max_len, dst, out_len); +} + +int +parse_arg_byte_stream_exact_length(char *name, uint8_t *dst, int len) +{ + int actual_len; + int rc; + + rc = parse_arg_byte_stream(name, len, dst, &actual_len); + if (rc != 0) { + return rc; + } + + if (actual_len != len) { + return EINVAL; + } + + return 0; +} + +static void +parse_reverse_bytes(uint8_t *bytes, int len) +{ + uint8_t tmp; + int i; + + for (i = 0; i < len / 2; i++) { + tmp = bytes[i]; + bytes[i] = bytes[len - i - 1]; + bytes[len - i - 1] = tmp; + } +} + +int +parse_arg_mac(char *name, uint8_t *dst) +{ + int rc; + + rc = parse_arg_byte_stream_exact_length(name, dst, 6); + if (rc != 0) { + return rc; + } + + parse_reverse_bytes(dst, 6); + + return 0; +} + +int +parse_arg_addr(char *name, ble_addr_t *addr) +{ + char *arg; + size_t len; + uint8_t addr_type; + bool addr_type_found; + int rc; + + arg = parse_arg_peek(name); + if (!arg) { + return ENOENT; + } + + len = strlen(arg); + if (len < 2) { + return EINVAL; + } + + addr_type_found = false; + if ((arg[len - 2] == ':') || (arg[len - 2] == '-')) { + if (tolower(arg[len - 1]) == 'p') { + addr_type = BLE_ADDR_PUBLIC; + addr_type_found = true; + } else if (tolower(arg[len - 1]) == 'r') { + addr_type = BLE_ADDR_RANDOM; + addr_type_found = true; + } + + if (addr_type_found) { + arg[len - 2] = '\0'; + } +} + + rc = parse_arg_mac(name, addr->val); + if (rc != 0) { + return rc; + } + + if (addr_type_found) { + addr->type = addr_type; + } else { + rc = EAGAIN; + } + + return rc; +} + +int +parse_arg_uuid(char *str, ble_uuid_any_t *uuid) +{ + uint16_t uuid16; + uint8_t val[16]; + int len; + int rc; + + uuid16 = parse_arg_uint16_peek(str, &rc); + switch (rc) { + case ENOENT: + parse_arg_extract(str); + return ENOENT; + + case 0: + len = 2; + val[0] = uuid16; + val[1] = uuid16 >> 8; + parse_arg_extract(str); + break; + + default: + len = 16; + rc = parse_arg_byte_stream_exact_length(str, val, 16); + if (rc != 0) { + return EINVAL; + } + parse_reverse_bytes(val, 16); + break; + } + + rc = ble_uuid_init_from_buf(uuid, val, len); + if (rc != 0) { + return EINVAL; + } else { + return 0; + } +} + +int +parse_arg_all(int argc, char **argv) +{ + char *key; + char *val; + int i; + + cmd_num_args = 0; + + for (i = 0; i < argc; i++) { + key = strtok(argv[i], "="); + val = strtok(NULL, "="); + + if (key != NULL && val != NULL) { + if (strlen(key) == 0) { + console_printf("Error: invalid argument: %s\n", argv[i]); + return -1; + } + + if (cmd_num_args >= CMD_MAX_ARGS) { + console_printf("Error: too many arguments"); + return -1; + } + + cmd_args[cmd_num_args][0] = key; + cmd_args[cmd_num_args][1] = val; + cmd_num_args++; + } + } + + return 0; +} + +int +parse_eddystone_url(char *full_url, uint8_t *out_scheme, char *out_body, + uint8_t *out_body_len, uint8_t *out_suffix) +{ + static const struct { + char *s; + uint8_t scheme; + } schemes[] = { + { "http://www.", BLE_EDDYSTONE_URL_SCHEME_HTTP_WWW }, + { "https://www.", BLE_EDDYSTONE_URL_SCHEME_HTTPS_WWW }, + { "http://", BLE_EDDYSTONE_URL_SCHEME_HTTP }, + { "https://", BLE_EDDYSTONE_URL_SCHEME_HTTPS }, + }; + + static const struct { + char *s; + uint8_t code; + } suffixes[] = { + { ".com/", BLE_EDDYSTONE_URL_SUFFIX_COM_SLASH }, + { ".org/", BLE_EDDYSTONE_URL_SUFFIX_ORG_SLASH }, + { ".edu/", BLE_EDDYSTONE_URL_SUFFIX_EDU_SLASH }, + { ".net/", BLE_EDDYSTONE_URL_SUFFIX_NET_SLASH }, + { ".info/", BLE_EDDYSTONE_URL_SUFFIX_INFO_SLASH }, + { ".biz/", BLE_EDDYSTONE_URL_SUFFIX_BIZ_SLASH }, + { ".gov/", BLE_EDDYSTONE_URL_SUFFIX_GOV_SLASH }, + { ".com", BLE_EDDYSTONE_URL_SUFFIX_COM }, + { ".org", BLE_EDDYSTONE_URL_SUFFIX_ORG }, + { ".edu", BLE_EDDYSTONE_URL_SUFFIX_EDU }, + { ".net", BLE_EDDYSTONE_URL_SUFFIX_NET }, + { ".info", BLE_EDDYSTONE_URL_SUFFIX_INFO }, + { ".biz", BLE_EDDYSTONE_URL_SUFFIX_BIZ }, + { ".gov", BLE_EDDYSTONE_URL_SUFFIX_GOV }, + }; + + char *prefix; + char *suffix; + int full_url_len; + int prefix_len; + int suffix_len; + int suffix_idx; + int rc; + int i; + + full_url_len = strlen(full_url); + + rc = BLE_HS_EINVAL; + for (i = 0; i < sizeof schemes / sizeof schemes[0]; i++) { + prefix = schemes[i].s; + prefix_len = strlen(schemes[i].s); + + if (full_url_len >= prefix_len && + memcmp(full_url, prefix, prefix_len) == 0) { + + *out_scheme = i; + rc = 0; + break; + } + } + if (rc != 0) { + return rc; + } + + rc = BLE_HS_EINVAL; + for (i = 0; i < sizeof suffixes / sizeof suffixes[0]; i++) { + suffix = suffixes[i].s; + suffix_len = strlen(suffixes[i].s); + + suffix_idx = full_url_len - suffix_len; + if (suffix_idx >= prefix_len && + memcmp(full_url + suffix_idx, suffix, suffix_len) == 0) { + + *out_suffix = i; + rc = 0; + break; + } + } + if (rc != 0) { + *out_suffix = BLE_EDDYSTONE_URL_SUFFIX_NONE; + *out_body_len = full_url_len - prefix_len; + } else { + *out_body_len = full_url_len - prefix_len - suffix_len; + } + + memcpy(out_body, full_url + prefix_len, *out_body_len); + + return 0; +} diff --git a/src/libs/mynewt-nimble/apps/btshell/syscfg.yml b/src/libs/mynewt-nimble/apps/btshell/syscfg.yml new file mode 100644 index 00000000..9ebf9d89 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/btshell/syscfg.yml @@ -0,0 +1,42 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.defs: + BTSHELL_ANS: + description: Include support for the alert notification service. + value: 1 + +syscfg.vals: + # Enable the shell task. + SHELL_TASK: 1 + + # Set log level to info (disable debug logging). + LOG_LEVEL: 1 + + # Disable security manager (pairing and bonding). + BLE_SM_LEGACY: 0 + BLE_SM_SC: 0 + + # Default task settings + OS_MAIN_STACK_SIZE: 512 + + # SMP is not supported in this app, so disable smp-over-shell. + SHELL_MGMT: 0 + +syscfg.vals.BLE_MESH: + MSYS_1_BLOCK_COUNT: 16 diff --git a/src/libs/mynewt-nimble/apps/bttester/README b/src/libs/mynewt-nimble/apps/bttester/README new file mode 100644 index 00000000..29db2eba --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bttester/README @@ -0,0 +1,14 @@ +Title: Bluetooth tester application + +Description: + +Tester application uses binary protocol to control Mynewt Nimble stack +and is aimed at automated testing. It uses Bluetooth Testing Protocol (BTP) +to drive Bluetooth stack. BTP commands and events are received and buffered for +further processing. +-------------------------------------------------------------------------------- +Supported Profiles: + +GAP, GATT, SM, L2CAP, MESH +-------------------------------------------------------------------------------- + diff --git a/src/libs/mynewt-nimble/apps/bttester/pkg.yml b/src/libs/mynewt-nimble/apps/bttester/pkg.yml new file mode 100644 index 00000000..00e7a760 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bttester/pkg.yml @@ -0,0 +1,44 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: apps/bttester +pkg.type: app +pkg.description: Bluetooth tester application +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/shell" + - "@apache-mynewt-nimble/nimble/controller" + - "@apache-mynewt-nimble/nimble/host" + - "@apache-mynewt-nimble/nimble/host/util" + - "@apache-mynewt-nimble/nimble/host/services/gap" + - "@apache-mynewt-nimble/nimble/host/services/gatt" + - "@apache-mynewt-nimble/nimble/host/services/dis" + - "@apache-mynewt-nimble/nimble/host/store/ram" + - "@apache-mynewt-nimble/nimble/transport/ram" + - "@apache-mynewt-core/hw/drivers/uart" + - "@apache-mynewt-core/hw/drivers/rtt" + diff --git a/src/libs/mynewt-nimble/apps/bttester/src/atomic.h b/src/libs/mynewt-nimble/apps/bttester/src/atomic.h new file mode 100644 index 00000000..66283e9a --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bttester/src/atomic.h @@ -0,0 +1,405 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* atomic operations */ + +/* + * Copyright (c) 1997-2015, Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ATOMIC_H__ +#define __ATOMIC_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef int atomic_t; +typedef atomic_t atomic_val_t; + +/** + * @defgroup atomic_apis Atomic Services APIs + * @ingroup kernel_apis + * @{ + */ + +/** + * @brief Atomic compare-and-set. + * + * This routine performs an atomic compare-and-set on @a target. If the current + * value of @a target equals @a old_value, @a target is set to @a new_value. + * If the current value of @a target does not equal @a old_value, @a target + * is left unchanged. + * + * @param target Address of atomic variable. + * @param old_value Original value to compare against. + * @param new_value New value to store. + * @return 1 if @a new_value is written, 0 otherwise. + */ +static inline int atomic_cas(atomic_t *target, atomic_val_t old_value, + atomic_val_t new_value) +{ + return __atomic_compare_exchange_n(target, &old_value, new_value, + 0, __ATOMIC_SEQ_CST, + __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic addition. + * + * This routine performs an atomic addition on @a target. + * + * @param target Address of atomic variable. + * @param value Value to add. + * + * @return Previous value of @a target. + */ +static inline atomic_val_t atomic_add(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_add(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic subtraction. + * + * This routine performs an atomic subtraction on @a target. + * + * @param target Address of atomic variable. + * @param value Value to subtract. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_sub(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_sub(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic increment. + * + * This routine performs an atomic increment by 1 on @a target. + * + * @param target Address of atomic variable. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_inc(atomic_t *target) +{ + return atomic_add(target, 1); +} + +/** + * + * @brief Atomic decrement. + * + * This routine performs an atomic decrement by 1 on @a target. + * + * @param target Address of atomic variable. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_dec(atomic_t *target) +{ + return atomic_sub(target, 1); +} + +/** + * + * @brief Atomic get. + * + * This routine performs an atomic read on @a target. + * + * @param target Address of atomic variable. + * + * @return Value of @a target. + */ + +static inline atomic_val_t atomic_get(const atomic_t *target) +{ + return __atomic_load_n(target, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic get-and-set. + * + * This routine atomically sets @a target to @a value and returns + * the previous value of @a target. + * + * @param target Address of atomic variable. + * @param value Value to write to @a target. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_set(atomic_t *target, atomic_val_t value) +{ + /* This builtin, as described by Intel, is not a traditional + * test-and-set operation, but rather an atomic exchange operation. It + * writes value into *ptr, and returns the previous contents of *ptr. + */ + return __atomic_exchange_n(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic clear. + * + * This routine atomically sets @a target to zero and returns its previous + * value. (Hence, it is equivalent to atomic_set(target, 0).) + * + * @param target Address of atomic variable. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_clear(atomic_t *target) +{ + return atomic_set(target, 0); +} + +/** + * + * @brief Atomic bitwise inclusive OR. + * + * This routine atomically sets @a target to the bitwise inclusive OR of + * @a target and @a value. + * + * @param target Address of atomic variable. + * @param value Value to OR. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_or(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_or(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic bitwise exclusive OR (XOR). + * + * This routine atomically sets @a target to the bitwise exclusive OR (XOR) of + * @a target and @a value. + * + * @param target Address of atomic variable. + * @param value Value to XOR + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_xor(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_xor(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic bitwise AND. + * + * This routine atomically sets @a target to the bitwise AND of @a target + * and @a value. + * + * @param target Address of atomic variable. + * @param value Value to AND. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_and(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_and(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic bitwise NAND. + * + * This routine atomically sets @a target to the bitwise NAND of @a target + * and @a value. (This operation is equivalent to target = ~(target & value).) + * + * @param target Address of atomic variable. + * @param value Value to NAND. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_nand(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_nand(target, value, __ATOMIC_SEQ_CST); +} + + /** + * @brief Initialize an atomic variable. + * + * This macro can be used to initialize an atomic variable. For example, + * @code atomic_t my_var = ATOMIC_INIT(75); @endcode + * + * @param i Value to assign to atomic variable. + */ +#define ATOMIC_INIT(i) (i) + + /** + * @cond INTERNAL_HIDDEN + */ + +#define ATOMIC_BITS (sizeof(atomic_val_t) * 8) +#define ATOMIC_MASK(bit) (1 << ((bit) & (ATOMIC_BITS - 1))) +#define ATOMIC_ELEM(addr, bit) ((addr) + ((bit) / ATOMIC_BITS)) + + /** + * INTERNAL_HIDDEN @endcond + */ + + /** + * @brief Define an array of atomic variables. + * + * This macro defines an array of atomic variables containing at least + * @a num_bits bits. + * + * @note + * If used from file scope, the bits of the array are initialized to zero; + * if used from within a function, the bits are left uninitialized. + * + * @param name Name of array of atomic variables. + * @param num_bits Number of bits needed. + */ +#define ATOMIC_DEFINE(name, num_bits) \ + atomic_t name[1 + ((num_bits) - 1) / ATOMIC_BITS] + + /** + * @brief Atomically test a bit. + * + * This routine tests whether bit number @a bit of @a target is set or not. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return 1 if the bit was set, 0 if it wasn't. + */ + static inline int + atomic_test_bit(const atomic_t *target, int bit) + { + atomic_val_t val = atomic_get(ATOMIC_ELEM(target, bit)); + + return (1 & (val >> (bit & (ATOMIC_BITS - 1)))); + } + + /** + * @brief Atomically test and clear a bit. + * + * Atomically clear bit number @a bit of @a target and return its old value. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return 1 if the bit was set, 0 if it wasn't. + */ + static inline int + atomic_test_and_clear_bit(atomic_t *target, int bit) + { + atomic_val_t mask = ATOMIC_MASK(bit); + atomic_val_t old; + + old = atomic_and(ATOMIC_ELEM(target, bit), ~mask); + + return (old & mask) != 0; + } + + /** + * @brief Atomically set a bit. + * + * Atomically set bit number @a bit of @a target and return its old value. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return 1 if the bit was set, 0 if it wasn't. + */ + static inline int + atomic_test_and_set_bit(atomic_t *target, int bit) + { + atomic_val_t mask = ATOMIC_MASK(bit); + atomic_val_t old; + + old = atomic_or(ATOMIC_ELEM(target, bit), mask); + + return (old & mask) != 0; + } + + /** + * @brief Atomically clear a bit. + * + * Atomically clear bit number @a bit of @a target. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return N/A + */ + static inline void + atomic_clear_bit(atomic_t *target, int bit) + { + atomic_val_t mask = ATOMIC_MASK(bit); + + atomic_and(ATOMIC_ELEM(target, bit), ~mask); + } + + /** + * @brief Atomically set a bit. + * + * Atomically set bit number @a bit of @a target. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return N/A + */ + static inline void + atomic_set_bit(atomic_t *target, int bit) + { + atomic_val_t mask = ATOMIC_MASK(bit); + + atomic_or(ATOMIC_ELEM(target, bit), mask); + } + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __ATOMIC_H__ */ diff --git a/src/libs/mynewt-nimble/apps/bttester/src/bttester.c b/src/libs/mynewt-nimble/apps/bttester/src/bttester.c new file mode 100644 index 00000000..54b14daa --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bttester/src/bttester.c @@ -0,0 +1,374 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* bttester.c - Bluetooth Tester */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "syscfg/syscfg.h" +#include "console/console.h" + +#include "bttester_pipe.h" +#include "bttester.h" + +#define CMD_QUEUED 2 + +static struct os_eventq avail_queue; +static struct os_eventq *cmds_queue; +static struct os_event bttester_ev[CMD_QUEUED]; + +struct btp_buf { + struct os_event *ev; + union { + u8_t data[BTP_MTU]; + struct btp_hdr hdr; + }; +}; + +static struct btp_buf cmd_buf[CMD_QUEUED]; + +static void supported_commands(u8_t *data, u16_t len) +{ + u8_t buf[1]; + struct core_read_supported_commands_rp *rp = (void *) buf; + + memset(buf, 0, sizeof(buf)); + + tester_set_bit(buf, CORE_READ_SUPPORTED_COMMANDS); + tester_set_bit(buf, CORE_READ_SUPPORTED_SERVICES); + tester_set_bit(buf, CORE_REGISTER_SERVICE); + tester_set_bit(buf, CORE_UNREGISTER_SERVICE); + + tester_send(BTP_SERVICE_ID_CORE, CORE_READ_SUPPORTED_COMMANDS, + BTP_INDEX_NONE, (u8_t *) rp, sizeof(buf)); +} + +static void supported_services(u8_t *data, u16_t len) +{ + u8_t buf[1]; + struct core_read_supported_services_rp *rp = (void *) buf; + + memset(buf, 0, sizeof(buf)); + + tester_set_bit(buf, BTP_SERVICE_ID_CORE); + tester_set_bit(buf, BTP_SERVICE_ID_GAP); + tester_set_bit(buf, BTP_SERVICE_ID_GATT); +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) + tester_set_bit(buf, BTP_SERVICE_ID_L2CAP); +#endif /* MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) */ +#if MYNEWT_VAL(BLE_MESH) + tester_set_bit(buf, BTP_SERVICE_ID_MESH); +#endif /* MYNEWT_VAL(BLE_MESH) */ + + tester_send(BTP_SERVICE_ID_CORE, CORE_READ_SUPPORTED_SERVICES, + BTP_INDEX_NONE, (u8_t *) rp, sizeof(buf)); +} + +static void register_service(u8_t *data, u16_t len) +{ + struct core_register_service_cmd *cmd = (void *) data; + u8_t status; + + switch (cmd->id) { + case BTP_SERVICE_ID_GAP: + status = tester_init_gap(); + /* Rsp with success status will be handled by bt enable cb */ + if (status == BTP_STATUS_FAILED) { + goto rsp; + } + return; + case BTP_SERVICE_ID_GATT: + status = tester_init_gatt(); + break; +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) + case BTP_SERVICE_ID_L2CAP: + status = tester_init_l2cap(); + break; +#endif /* MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) */ +#if MYNEWT_VAL(BLE_MESH) + case BTP_SERVICE_ID_MESH: + status = tester_init_mesh(); + break; +#endif /* MYNEWT_VAL(BLE_MESH) */ + default: + status = BTP_STATUS_FAILED; + break; + } + +rsp: + tester_rsp(BTP_SERVICE_ID_CORE, CORE_REGISTER_SERVICE, BTP_INDEX_NONE, + status); +} + +static void unregister_service(u8_t *data, u16_t len) +{ + struct core_unregister_service_cmd *cmd = (void *) data; + u8_t status; + + switch (cmd->id) { + case BTP_SERVICE_ID_GAP: + status = tester_unregister_gap(); + break; + case BTP_SERVICE_ID_GATT: + status = tester_unregister_gatt(); + break; +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) + case BTP_SERVICE_ID_L2CAP: + status = tester_unregister_l2cap(); + break; +#endif /* MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) */ +#if MYNEWT_VAL(BLE_MESH) + case BTP_SERVICE_ID_MESH: + status = tester_unregister_mesh(); + break; +#endif /* MYNEWT_VAL(BLE_MESH) */ + default: + status = BTP_STATUS_FAILED; + break; + } + + tester_rsp(BTP_SERVICE_ID_CORE, CORE_UNREGISTER_SERVICE, BTP_INDEX_NONE, + status); +} + +static void handle_core(u8_t opcode, u8_t index, u8_t *data, + u16_t len) +{ + if (index != BTP_INDEX_NONE) { + tester_rsp(BTP_SERVICE_ID_CORE, opcode, index, + BTP_STATUS_FAILED); + return; + } + + switch (opcode) { + case CORE_READ_SUPPORTED_COMMANDS: + supported_commands(data, len); + return; + case CORE_READ_SUPPORTED_SERVICES: + supported_services(data, len); + return; + case CORE_REGISTER_SERVICE: + register_service(data, len); + return; + case CORE_UNREGISTER_SERVICE: + unregister_service(data, len); + return; + default: + tester_rsp(BTP_SERVICE_ID_CORE, opcode, BTP_INDEX_NONE, + BTP_STATUS_UNKNOWN_CMD); + return; + } +} + +static void cmd_handler(struct os_event *ev) +{ + u16_t len; + struct btp_buf *cmd; + + if (!ev || !ev->ev_arg) { + return; + } + + cmd = ev->ev_arg; + + len = sys_le16_to_cpu(cmd->hdr.len); + if (MYNEWT_VAL(BTTESTER_BTP_LOG)) { + console_printf("[DBG] received %d bytes: %s\n", + sizeof(cmd->hdr) + len, + bt_hex(cmd->data, + sizeof(cmd->hdr) + len)); + } + + /* TODO + * verify if service is registered before calling handler + */ + + switch (cmd->hdr.service) { + case BTP_SERVICE_ID_CORE: + handle_core(cmd->hdr.opcode, cmd->hdr.index, + cmd->hdr.data, len); + break; + case BTP_SERVICE_ID_GAP: + tester_handle_gap(cmd->hdr.opcode, cmd->hdr.index, + cmd->hdr.data, len); + break; + case BTP_SERVICE_ID_GATT: + tester_handle_gatt(cmd->hdr.opcode, cmd->hdr.index, + cmd->hdr.data, len); + break; +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) + case BTP_SERVICE_ID_L2CAP: + tester_handle_l2cap(cmd->hdr.opcode, cmd->hdr.index, + cmd->hdr.data, len); + break; +#endif /* MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) */ +#if MYNEWT_VAL(BLE_MESH) + case BTP_SERVICE_ID_MESH: + tester_handle_mesh(cmd->hdr.opcode, cmd->hdr.index, + cmd->hdr.data, len); + break; +#endif /* MYNEWT_VAL(BLE_MESH) */ + default: + tester_rsp(cmd->hdr.service, cmd->hdr.opcode, + cmd->hdr.index, BTP_STATUS_FAILED); + break; + } + + os_eventq_put(&avail_queue, ev); +} + +static u8_t *recv_cb(u8_t *buf, size_t *off) +{ + struct btp_hdr *cmd = (void *) buf; + struct os_event *new_ev; + struct btp_buf *new_buf, *old_buf; + u16_t len; + + if (*off < sizeof(*cmd)) { + return buf; + } + + len = sys_le16_to_cpu(cmd->len); + if (len > BTP_MTU - sizeof(*cmd)) { + *off = 0; + return buf; + } + + if (*off < sizeof(*cmd) + len) { + return buf; + } + + new_ev = os_eventq_get_no_wait(&avail_queue); + if (!new_ev) { + SYS_LOG_ERR("BT tester: RX overflow"); + *off = 0; + return buf; + } + + old_buf = CONTAINER_OF(buf, struct btp_buf, data); + os_eventq_put(cmds_queue, old_buf->ev); + + new_buf = new_ev->ev_arg; + *off = 0; + return new_buf->data; +} + +static void avail_queue_init(void) +{ + int i; + + os_eventq_init(&avail_queue); + + for (i = 0; i < CMD_QUEUED; i++) { + cmd_buf[i].ev = &bttester_ev[i]; + bttester_ev[i].ev_cb = cmd_handler; + bttester_ev[i].ev_arg = &cmd_buf[i]; + + os_eventq_put(&avail_queue, &bttester_ev[i]); + } +} + +void bttester_evq_set(struct os_eventq *evq) +{ + cmds_queue = evq; +} + +void tester_init(void) +{ + struct os_event *ev; + struct btp_buf *buf; + + avail_queue_init(); + bttester_evq_set(os_eventq_dflt_get()); + + ev = os_eventq_get(&avail_queue); + buf = ev->ev_arg; + + if (bttester_pipe_init()) { + SYS_LOG_ERR("Failed to initialize pipe"); + return; + } + + bttester_pipe_register(buf->data, BTP_MTU, recv_cb); + + tester_send(BTP_SERVICE_ID_CORE, CORE_EV_IUT_READY, BTP_INDEX_NONE, + NULL, 0); +} + +void tester_send(u8_t service, u8_t opcode, u8_t index, u8_t *data, + size_t len) +{ + struct btp_hdr msg; + + msg.service = service; + msg.opcode = opcode; + msg.index = index; + msg.len = len; + + bttester_pipe_send((u8_t *)&msg, sizeof(msg)); + if (data && len) { + bttester_pipe_send(data, len); + } + + if (MYNEWT_VAL(BTTESTER_BTP_LOG)) { + console_printf("[DBG] send %d bytes hdr: %s\n", sizeof(msg), + bt_hex((char *) &msg, sizeof(msg))); + if (data && len) { + console_printf("[DBG] send %d bytes data: %s\n", len, + bt_hex((char *) data, len)); + } + } +} + +void tester_send_buf(u8_t service, u8_t opcode, u8_t index, + struct os_mbuf *data) +{ + struct btp_hdr msg; + + msg.service = service; + msg.opcode = opcode; + msg.index = index; + msg.len = os_mbuf_len(data); + + bttester_pipe_send((u8_t *)&msg, sizeof(msg)); + if (data && msg.len) { + bttester_pipe_send_buf(data); + } +} + +void tester_rsp(u8_t service, u8_t opcode, u8_t index, u8_t status) +{ + struct btp_status s; + + if (status == BTP_STATUS_SUCCESS) { + tester_send(service, opcode, index, NULL, 0); + return; + } + + s.code = status; + tester_send(service, BTP_STATUS, index, (u8_t *) &s, sizeof(s)); +} diff --git a/src/libs/mynewt-nimble/apps/bttester/src/bttester.h b/src/libs/mynewt-nimble/apps/bttester/src/bttester.h new file mode 100644 index 00000000..f4e66a6f --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bttester/src/bttester.h @@ -0,0 +1,1010 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* bttester.h - Bluetooth tester headers */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BTTESTER_H__ +#define __BTTESTER_H__ + +#include "syscfg/syscfg.h" +#include "host/ble_gatt.h" + +#if MYNEWT_VAL(BLE_MESH) +#include "mesh/glue.h" +#else +#include "glue.h" +#endif + +#define BTP_MTU MYNEWT_VAL(BTTESTER_BTP_DATA_SIZE_MAX) +#define BTP_DATA_MAX_SIZE (BTP_MTU - sizeof(struct btp_hdr)) + +#define BTP_INDEX_NONE 0xff + +#define BTP_SERVICE_ID_CORE 0 +#define BTP_SERVICE_ID_GAP 1 +#define BTP_SERVICE_ID_GATT 2 +#define BTP_SERVICE_ID_L2CAP 3 +#define BTP_SERVICE_ID_MESH 4 + +#define BTP_STATUS_SUCCESS 0x00 +#define BTP_STATUS_FAILED 0x01 +#define BTP_STATUS_UNKNOWN_CMD 0x02 +#define BTP_STATUS_NOT_READY 0x03 + +#define SYS_LOG_DBG(fmt, ...) \ + if (MYNEWT_VAL(BTTESTER_DEBUG)) { \ + console_printf("[DBG] %s: " fmt "\n", \ + __func__, ## __VA_ARGS__); \ + } +#define SYS_LOG_INF(fmt, ...) console_printf("[INF] %s: " fmt "\n", \ + __func__, ## __VA_ARGS__); +#define SYS_LOG_ERR(fmt, ...) console_printf("[WRN] %s: " fmt "\n", \ + __func__, ## __VA_ARGS__); + +#define SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG +#define SYS_LOG_DOMAIN "bttester" + +#define sys_cpu_to_le32 htole32 +#define sys_le32_to_cpu le32toh +#define sys_cpu_to_le16 htole16 + +struct btp_hdr { + u8_t service; + u8_t opcode; + u8_t index; + u16_t len; + u8_t data[0]; +} __packed; + +#define BTP_STATUS 0x00 +struct btp_status { + u8_t code; +} __packed; + +/* Core Service */ +#define CORE_READ_SUPPORTED_COMMANDS 0x01 +struct core_read_supported_commands_rp { + u8_t data[0]; +} __packed; + +#define CORE_READ_SUPPORTED_SERVICES 0x02 +struct core_read_supported_services_rp { + u8_t data[0]; +} __packed; + +#define CORE_REGISTER_SERVICE 0x03 +struct core_register_service_cmd { + u8_t id; +} __packed; + +#define CORE_UNREGISTER_SERVICE 0x04 +struct core_unregister_service_cmd { + u8_t id; +} __packed; + +/* events */ +#define CORE_EV_IUT_READY 0x80 + +/* GAP Service */ +/* commands */ +#define GAP_READ_SUPPORTED_COMMANDS 0x01 +struct gap_read_supported_commands_rp { + u8_t data[0]; +} __packed; + +#define GAP_READ_CONTROLLER_INDEX_LIST 0x02 +struct gap_read_controller_index_list_rp { + u8_t num; + u8_t index[0]; +} __packed; + +#define GAP_SETTINGS_POWERED 0 +#define GAP_SETTINGS_CONNECTABLE 1 +#define GAP_SETTINGS_FAST_CONNECTABLE 2 +#define GAP_SETTINGS_DISCOVERABLE 3 +#define GAP_SETTINGS_BONDABLE 4 +#define GAP_SETTINGS_LINK_SEC_3 5 +#define GAP_SETTINGS_SSP 6 +#define GAP_SETTINGS_BREDR 7 +#define GAP_SETTINGS_HS 8 +#define GAP_SETTINGS_LE 9 +#define GAP_SETTINGS_ADVERTISING 10 +#define GAP_SETTINGS_SC 11 +#define GAP_SETTINGS_DEBUG_KEYS 12 +#define GAP_SETTINGS_PRIVACY 13 +#define GAP_SETTINGS_CONTROLLER_CONFIG 14 +#define GAP_SETTINGS_STATIC_ADDRESS 15 + +#define GAP_READ_CONTROLLER_INFO 0x03 +struct gap_read_controller_info_rp { + u8_t address[6]; + u32_t supported_settings; + u32_t current_settings; + u8_t cod[3]; + u8_t name[249]; + u8_t short_name[11]; +} __packed; + +#define GAP_RESET 0x04 +struct gap_reset_rp { + u32_t current_settings; +} __packed; + +#define GAP_SET_POWERED 0x05 +struct gap_set_powered_cmd { + u8_t powered; +} __packed; +struct gap_set_powered_rp { + u32_t current_settings; +} __packed; + +#define GAP_SET_CONNECTABLE 0x06 +struct gap_set_connectable_cmd { + u8_t connectable; +} __packed; +struct gap_set_connectable_rp { + u32_t current_settings; +} __packed; + +#define GAP_SET_FAST_CONNECTABLE 0x07 +struct gap_set_fast_connectable_cmd { + u8_t fast_connectable; +} __packed; +struct gap_set_fast_connectable_rp { + u32_t current_settings; +} __packed; + +#define GAP_NON_DISCOVERABLE 0x00 +#define GAP_GENERAL_DISCOVERABLE 0x01 +#define GAP_LIMITED_DISCOVERABLE 0x02 + +#define GAP_SET_DISCOVERABLE 0x08 +struct gap_set_discoverable_cmd { + u8_t discoverable; +} __packed; +struct gap_set_discoverable_rp { + u32_t current_settings; +} __packed; + +#define GAP_SET_BONDABLE 0x09 +struct gap_set_bondable_cmd { + u8_t bondable; +} __packed; +struct gap_set_bondable_rp { + u32_t current_settings; +} __packed; + +#define GAP_START_ADVERTISING 0x0a +struct gap_start_advertising_cmd { + u8_t adv_data_len; + u8_t scan_rsp_len; + u8_t adv_data[0]; + u8_t scan_rsp[0]; +} __packed; +struct gap_start_advertising_rp { + u32_t current_settings; +} __packed; + +#define GAP_STOP_ADVERTISING 0x0b +struct gap_stop_advertising_rp { + u32_t current_settings; +} __packed; + +#define GAP_DISCOVERY_FLAG_LE 0x01 +#define GAP_DISCOVERY_FLAG_BREDR 0x02 +#define GAP_DISCOVERY_FLAG_LIMITED 0x04 +#define GAP_DISCOVERY_FLAG_LE_ACTIVE_SCAN 0x08 +#define GAP_DISCOVERY_FLAG_LE_OBSERVE 0x10 + +#define GAP_START_DISCOVERY 0x0c +struct gap_start_discovery_cmd { + u8_t flags; +} __packed; + +#define GAP_STOP_DISCOVERY 0x0d + +#define GAP_CONNECT 0x0e +struct gap_connect_cmd { + u8_t address_type; + u8_t address[6]; +} __packed; + +#define GAP_DISCONNECT 0x0f +struct gap_disconnect_cmd { + u8_t address_type; + u8_t address[6]; +} __packed; + +#define GAP_IO_CAP_DISPLAY_ONLY 0 +#define GAP_IO_CAP_DISPLAY_YESNO 1 +#define GAP_IO_CAP_KEYBOARD_ONLY 2 +#define GAP_IO_CAP_NO_INPUT_OUTPUT 3 +#define GAP_IO_CAP_KEYBOARD_DISPLAY 4 + +#define GAP_SET_IO_CAP 0x10 +struct gap_set_io_cap_cmd { + u8_t io_cap; +} __packed; + +#define GAP_PAIR 0x11 +struct gap_pair_cmd { + u8_t address_type; + u8_t address[6]; +} __packed; + +#define GAP_UNPAIR 0x12 +struct gap_unpair_cmd { + u8_t address_type; + u8_t address[6]; +} __packed; + +#define GAP_PASSKEY_ENTRY 0x13 +struct gap_passkey_entry_cmd { + u8_t address_type; + u8_t address[6]; + u32_t passkey; +} __packed; + +#define GAP_PASSKEY_CONFIRM 0x14 +struct gap_passkey_confirm_cmd { + u8_t address_type; + u8_t address[6]; + u8_t match; +} __packed; + +#define GAP_START_DIRECT_ADV 0x15 +struct gap_start_direct_adv_cmd { + u8_t address_type; + u8_t address[6]; + u8_t high_duty; +} __packed; + +#define GAP_CONN_PARAM_UPDATE 0x16 +struct gap_conn_param_update_cmd { + u8_t address_type; + u8_t address[6]; + u16_t conn_itvl_min; + u16_t conn_itvl_max; + u16_t conn_latency; + u16_t supervision_timeout; +} __packed; + +#define GAP_PAIRING_CONSENT_RSP 0x17 +struct gap_pairing_consent_rsp_cmd { + u8_t address_type; + u8_t address[6]; + u8_t consent; +} __packed; + +#define GAP_OOB_LEGACY_SET_DATA 0x18 +struct gap_oob_legacy_set_data_cmd { + u8_t oob_data[16]; +} __packed; + +#define GAP_OOB_SC_GET_LOCAL_DATA 0x19 +struct gap_oob_sc_get_local_data_rp { + u8_t r[16]; + u8_t c[16]; +} __packed; + +#define GAP_OOB_SC_SET_REMOTE_DATA 0x1a +struct gap_oob_sc_set_remote_data_cmd { + u8_t r[16]; + u8_t c[16]; +} __packed; + +#define GAP_SET_MITM 0x1b +struct gap_set_mitm_cmd { + u8_t mitm; +} __packed; + +/* events */ +#define GAP_EV_NEW_SETTINGS 0x80 +struct gap_new_settings_ev { + u32_t current_settings; +} __packed; + +#define GAP_DEVICE_FOUND_FLAG_RSSI 0x01 +#define GAP_DEVICE_FOUND_FLAG_AD 0x02 +#define GAP_DEVICE_FOUND_FLAG_SD 0x04 + +#define GAP_EV_DEVICE_FOUND 0x81 +struct gap_device_found_ev { + u8_t address_type; + u8_t address[6]; + s8_t rssi; + u8_t flags; + u16_t eir_data_len; + u8_t eir_data[0]; +} __packed; + +#define GAP_EV_DEVICE_CONNECTED 0x82 +struct gap_device_connected_ev { + u8_t address_type; + u8_t address[6]; + u16_t conn_itvl; + u16_t conn_latency; + u16_t supervision_timeout; +} __packed; + +#define GAP_EV_DEVICE_DISCONNECTED 0x83 +struct gap_device_disconnected_ev { + u8_t address_type; + u8_t address[6]; +} __packed; + +#define GAP_EV_PASSKEY_DISPLAY 0x84 +struct gap_passkey_display_ev { + u8_t address_type; + u8_t address[6]; + u32_t passkey; +} __packed; + +#define GAP_EV_PASSKEY_ENTRY_REQ 0x85 +struct gap_passkey_entry_req_ev { + u8_t address_type; + u8_t address[6]; +} __packed; + +#define GAP_EV_PASSKEY_CONFIRM_REQ 0x86 +struct gap_passkey_confirm_req_ev { + u8_t address_type; + u8_t address[6]; + u32_t passkey; +} __packed; + +#define GAP_EV_IDENTITY_RESOLVED 0x87 +struct gap_identity_resolved_ev { + u8_t address_type; + u8_t address[6]; + u8_t identity_address_type; + u8_t identity_address[6]; +} __packed; + +#define GAP_EV_CONN_PARAM_UPDATE 0x88 +struct gap_conn_param_update_ev { + u8_t address_type; + u8_t address[6]; + u16_t conn_itvl; + u16_t conn_latency; + u16_t supervision_timeout; +} __packed; + +#define GAP_EV_SEC_LEVEL_CHANGED 0x89 +struct gap_sec_level_changed_ev { + u8_t address_type; + u8_t address[6]; + u8_t level; +} __packed; + +#define GAP_EV_PAIRING_CONSENT_REQ 0x8a +struct gap_pairing_consent_req_ev { + u8_t address_type; + u8_t address[6]; +} __packed; + +/* GATT Service */ +/* commands */ +#define GATT_READ_SUPPORTED_COMMANDS 0x01 +struct gatt_read_supported_commands_rp { + u8_t data[0]; +} __packed; + +#define GATT_SERVICE_PRIMARY 0x00 +#define GATT_SERVICE_SECONDARY 0x01 + +#define GATT_ADD_SERVICE 0x02 +struct gatt_add_service_cmd { + u8_t type; + u8_t uuid_length; + u8_t uuid[0]; +} __packed; +struct gatt_add_service_rp { + u16_t svc_id; +} __packed; + +#define GATT_ADD_CHARACTERISTIC 0x03 +struct gatt_add_characteristic_cmd { + u16_t svc_id; + u8_t properties; + u8_t permissions; + u8_t uuid_length; + u8_t uuid[0]; +} __packed; +struct gatt_add_characteristic_rp { + u16_t char_id; +} __packed; + +#define GATT_ADD_DESCRIPTOR 0x04 +struct gatt_add_descriptor_cmd { + u16_t char_id; + u8_t permissions; + u8_t uuid_length; + u8_t uuid[0]; +} __packed; +struct gatt_add_descriptor_rp { + u16_t desc_id; +} __packed; + +#define GATT_ADD_INCLUDED_SERVICE 0x05 +struct gatt_add_included_service_cmd { + u16_t svc_id; +} __packed; +struct gatt_add_included_service_rp { + u16_t included_service_id; +} __packed; + +#define GATT_SET_VALUE 0x06 + struct gatt_set_value_cmd { + u16_t attr_id; + u16_t len; + u8_t value[0]; +} __packed; + +#define GATT_START_SERVER 0x07 +struct gatt_start_server_rp { + u16_t db_attr_off; + u8_t db_attr_cnt; +} __packed; + +#define GATT_SET_ENC_KEY_SIZE 0x09 +struct gatt_set_enc_key_size_cmd { + u16_t attr_id; + u8_t key_size; +} __packed; + +/* Gatt Client */ +struct gatt_service { + u16_t start_handle; + u16_t end_handle; + u8_t uuid_length; + u8_t uuid[0]; +} __packed; + +struct gatt_included { + u16_t included_handle; + struct gatt_service service; +} __packed; + +struct gatt_characteristic { + u16_t characteristic_handle; + u16_t value_handle; + u8_t properties; + u8_t uuid_length; + u8_t uuid[0]; +} __packed; + +struct gatt_descriptor { + u16_t descriptor_handle; + u8_t uuid_length; + u8_t uuid[0]; +} __packed; + +#define GATT_EXCHANGE_MTU 0x0a + +#define GATT_DISC_ALL_PRIM_SVCS 0x0b +struct gatt_disc_all_prim_svcs_cmd { + u8_t address_type; + u8_t address[6]; +} __packed; +struct gatt_disc_all_prim_svcs_rp { + u8_t services_count; + struct gatt_service services[0]; +} __packed; + +#define GATT_DISC_PRIM_UUID 0x0c +struct gatt_disc_prim_uuid_cmd { + u8_t address_type; + u8_t address[6]; + u8_t uuid_length; + u8_t uuid[0]; +} __packed; +struct gatt_disc_prim_uuid_rp { + u8_t services_count; + struct gatt_service services[0]; +} __packed; + +#define GATT_FIND_INCLUDED 0x0d +struct gatt_find_included_cmd { + u8_t address_type; + u8_t address[6]; + u16_t start_handle; + u16_t end_handle; +} __packed; +struct gatt_find_included_rp { + u8_t services_count; + struct gatt_included included[0]; +} __packed; + +#define GATT_DISC_ALL_CHRC 0x0e +struct gatt_disc_all_chrc_cmd { + u8_t address_type; + u8_t address[6]; + u16_t start_handle; + u16_t end_handle; +} __packed; +struct gatt_disc_chrc_rp { + u8_t characteristics_count; + struct gatt_characteristic characteristics[0]; +} __packed; + +#define GATT_DISC_CHRC_UUID 0x0f +struct gatt_disc_chrc_uuid_cmd { + u8_t address_type; + u8_t address[6]; + u16_t start_handle; + u16_t end_handle; + u8_t uuid_length; + u8_t uuid[0]; +} __packed; + +#define GATT_DISC_ALL_DESC 0x10 +struct gatt_disc_all_desc_cmd { + u8_t address_type; + u8_t address[6]; + u16_t start_handle; + u16_t end_handle; +} __packed; +struct gatt_disc_all_desc_rp { + u8_t descriptors_count; + struct gatt_descriptor descriptors[0]; +} __packed; + +#define GATT_READ 0x11 +struct gatt_read_cmd { + u8_t address_type; + u8_t address[6]; + u16_t handle; +} __packed; +struct gatt_read_rp { + u8_t att_response; + u16_t data_length; + u8_t data[0]; +} __packed; + +#define GATT_READ_UUID 0x12 +struct gatt_read_uuid_cmd { + u8_t address_type; + u8_t address[6]; + u16_t start_handle; + u16_t end_handle; + u8_t uuid_length; + u8_t uuid[0]; +} __packed; + +#define GATT_READ_LONG 0x13 +struct gatt_read_long_cmd { + u8_t address_type; + u8_t address[6]; + u16_t handle; + u16_t offset; +} __packed; + +#define GATT_READ_MULTIPLE 0x14 +struct gatt_read_multiple_cmd { + u8_t address_type; + u8_t address[6]; + u8_t handles_count; + u16_t handles[0]; +} __packed; + +#define GATT_WRITE_WITHOUT_RSP 0x15 +struct gatt_write_without_rsp_cmd { + u8_t address_type; + u8_t address[6]; + u16_t handle; + u16_t data_length; + u8_t data[0]; +} __packed; + +#define GATT_SIGNED_WRITE_WITHOUT_RSP 0x16 +struct gatt_signed_write_without_rsp_cmd { + u8_t address_type; + u8_t address[6]; + u16_t handle; + u16_t data_length; + u8_t data[0]; +} __packed; + +#define GATT_WRITE 0x17 +struct gatt_write_cmd { + u8_t address_type; + u8_t address[6]; + u16_t handle; + u16_t data_length; + u8_t data[0]; +} __packed; + +#define GATT_WRITE_LONG 0x18 +struct gatt_write_long_cmd { + u8_t address_type; + u8_t address[6]; + u16_t handle; + u16_t offset; + u16_t data_length; + u8_t data[0]; +} __packed; + +#define GATT_RELIABLE_WRITE 0x19 +struct gatt_reliable_write_cmd { + u8_t address_type; + u8_t address[6]; + u16_t handle; + u16_t offset; + u16_t data_length; + u8_t data[0]; +} __packed; + +#define GATT_CFG_NOTIFY 0x1a +#define GATT_CFG_INDICATE 0x1b +struct gatt_cfg_notify_cmd { + u8_t address_type; + u8_t address[6]; + u8_t enable; + u16_t ccc_handle; +} __packed; + +#define GATT_GET_ATTRIBUTES 0x1c +struct gatt_get_attributes_cmd { + u16_t start_handle; + u16_t end_handle; + u8_t type_length; + u8_t type[0]; +} __packed; +struct gatt_get_attributes_rp { + u8_t attrs_count; + u8_t attrs[0]; +} __packed; +struct gatt_attr { + u16_t handle; + u8_t permission; + u8_t type_length; + u8_t type[0]; +} __packed; + +#define GATT_GET_ATTRIBUTE_VALUE 0x1d +struct gatt_get_attribute_value_cmd { + u8_t address_type; + u8_t address[6]; + u16_t handle; +} __packed; +struct gatt_get_attribute_value_rp { + u8_t att_response; + u16_t value_length; + u8_t value[0]; +} __packed; + +#define GATT_CHANGE_DATABASE 0x1e +struct gatt_change_database { + u16_t start_handle; + u16_t end_handle; + u8_t visibility; +} __packed; + +/* GATT events */ +#define GATT_EV_NOTIFICATION 0x80 +struct gatt_notification_ev { + u8_t address_type; + u8_t address[6]; + u8_t type; + u16_t handle; + u16_t data_length; + u8_t data[0]; +} __packed; + +#define GATT_EV_ATTR_VALUE_CHANGED 0x81 +struct gatt_attr_value_changed_ev { + u16_t handle; + u16_t data_length; + u8_t data[0]; +} __packed; + +static inline void tester_set_bit(u8_t *addr, unsigned int bit) +{ + u8_t *p = addr + (bit / 8); + + *p |= BIT(bit % 8); +} + +static inline u8_t tester_test_bit(const u8_t *addr, unsigned int bit) +{ + const u8_t *p = addr + (bit / 8); + + return *p & BIT(bit % 8); +} + +/* L2CAP Service */ +/* commands */ +#define L2CAP_READ_SUPPORTED_COMMANDS 0x01 +struct l2cap_read_supported_commands_rp { + u8_t data[0]; +} __packed; + +#define L2CAP_CONNECT 0x02 +struct l2cap_connect_cmd { + u8_t address_type; + u8_t address[6]; + u16_t psm; +} __packed; + +struct l2cap_connect_rp { + u8_t chan_id; +} __packed; + +#define L2CAP_DISCONNECT 0x03 +struct l2cap_disconnect_cmd { + u8_t chan_id; +} __packed; + +#define L2CAP_SEND_DATA 0x04 +struct l2cap_send_data_cmd { + u8_t chan_id; + u16_t data_len; + u8_t data[]; +} __packed; + +#define L2CAP_TRANSPORT_BREDR 0x00 +#define L2CAP_TRANSPORT_LE 0x01 + +#define L2CAP_LISTEN 0x05 +struct l2cap_listen_cmd { + u16_t psm; + u8_t transport; +} __packed; + +#define L2CAP_ACCEPT_CONNECTION 0x06 +struct l2cap_accept_connection_cmd { + u8_t chan_id; + u16_t result; +} __packed; + +/* events */ +#define L2CAP_EV_CONNECTION_REQ 0x80 +struct l2cap_connection_req_ev { + u8_t chan_id; + u16_t psm; + u8_t address_type; + u8_t address[6]; +} __packed; + +#define L2CAP_EV_CONNECTED 0x81 +struct l2cap_connected_ev { + u8_t chan_id; + u16_t psm; + u8_t address_type; + u8_t address[6]; +} __packed; + +#define L2CAP_EV_DISCONNECTED 0x82 +struct l2cap_disconnected_ev { + u16_t result; + u8_t chan_id; + u16_t psm; + u8_t address_type; + u8_t address[6]; +} __packed; + +#define L2CAP_EV_DATA_RECEIVED 0x83 +struct l2cap_data_received_ev { + u8_t chan_id; + u16_t data_length; + u8_t data[0]; +} __packed; + +/* MESH Service */ +/* commands */ +#define MESH_READ_SUPPORTED_COMMANDS 0x01 +struct mesh_read_supported_commands_rp { + u8_t data[0]; +} __packed; + +#define MESH_OUT_BLINK BIT(0) +#define MESH_OUT_BEEP BIT(1) +#define MESH_OUT_VIBRATE BIT(2) +#define MESH_OUT_DISPLAY_NUMBER BIT(3) +#define MESH_OUT_DISPLAY_STRING BIT(4) + +#define MESH_IN_PUSH BIT(0) +#define MESH_IN_TWIST BIT(1) +#define MESH_IN_ENTER_NUMBER BIT(2) +#define MESH_IN_ENTER_STRING BIT(3) + +#define MESH_CONFIG_PROVISIONING 0x02 +struct mesh_config_provisioning_cmd { + u8_t uuid[16]; + u8_t static_auth[16]; + u8_t out_size; + u16_t out_actions; + u8_t in_size; + u16_t in_actions; +} __packed; + +#define MESH_PROVISION_NODE 0x03 +struct mesh_provision_node_cmd { + u8_t net_key[16]; + u16_t net_key_idx; + u8_t flags; + u32_t iv_index; + u32_t seq_num; + u16_t addr; + u8_t dev_key[16]; +} __packed; + +#define MESH_INIT 0x04 +#define MESH_RESET 0x05 +#define MESH_INPUT_NUMBER 0x06 +struct mesh_input_number_cmd { + u32_t number; +} __packed; + +#define MESH_INPUT_STRING 0x07 +struct mesh_input_string_cmd { + u8_t string_len; + u8_t string[0]; +} __packed; + +#define MESH_IVU_TEST_MODE 0x08 +struct mesh_ivu_test_mode_cmd { + u8_t enable; +} __packed; + +#define MESH_IVU_TOGGLE_STATE 0x09 + +#define MESH_NET_SEND 0x0a +struct mesh_net_send_cmd { + u8_t ttl; + u16_t src; + u16_t dst; + u8_t payload_len; + u8_t payload[0]; +} __packed; + +#define MESH_HEALTH_GENERATE_FAULTS 0x0b +struct mesh_health_generate_faults_rp { + u8_t test_id; + u8_t cur_faults_count; + u8_t reg_faults_count; + u8_t current_faults[0]; + u8_t registered_faults[0]; +} __packed; + +#define MESH_HEALTH_CLEAR_FAULTS 0x0c + +#define MESH_LPN 0x0d +struct mesh_lpn_set_cmd { + u8_t enable; +} __packed; + +#define MESH_LPN_POLL 0x0e + +#define MESH_MODEL_SEND 0x0f +struct mesh_model_send_cmd { + u16_t src; + u16_t dst; + u8_t payload_len; + u8_t payload[0]; +} __packed; + +#define MESH_LPN_SUBSCRIBE 0x10 +struct mesh_lpn_subscribe_cmd { + u16_t address; +} __packed; + +#define MESH_LPN_UNSUBSCRIBE 0x11 +struct mesh_lpn_unsubscribe_cmd { + u16_t address; +} __packed; + +#define MESH_RPL_CLEAR 0x12 +#define MESH_PROXY_IDENTITY 0x13 + +/* events */ +#define MESH_EV_OUT_NUMBER_ACTION 0x80 +struct mesh_out_number_action_ev { + u16_t action; + u32_t number; +} __packed; + +#define MESH_EV_OUT_STRING_ACTION 0x81 +struct mesh_out_string_action_ev { + u8_t string_len; + u8_t string[0]; +} __packed; + +#define MESH_EV_IN_ACTION 0x82 +struct mesh_in_action_ev { + u16_t action; + u8_t size; +} __packed; + +#define MESH_EV_PROVISIONED 0x83 + +#define MESH_PROV_BEARER_PB_ADV 0x00 +#define MESH_PROV_BEARER_PB_GATT 0x01 +#define MESH_EV_PROV_LINK_OPEN 0x84 +struct mesh_prov_link_open_ev { + u8_t bearer; +} __packed; + +#define MESH_EV_PROV_LINK_CLOSED 0x85 +struct mesh_prov_link_closed_ev { + u8_t bearer; +} __packed; + +#define MESH_EV_NET_RECV 0x86 +struct mesh_net_recv_ev { + u8_t ttl; + u8_t ctl; + u16_t src; + u16_t dst; + u8_t payload_len; + u8_t payload[0]; +} __packed; + +#define MESH_EV_INVALID_BEARER 0x87 +struct mesh_invalid_bearer_ev { + u8_t opcode; +} __packed; + +#define MESH_EV_INCOMP_TIMER_EXP 0x88 + +void tester_init(void); +void tester_rsp(u8_t service, u8_t opcode, u8_t index, u8_t status); +void tester_send(u8_t service, u8_t opcode, u8_t index, u8_t *data, + size_t len); +void tester_send_buf(u8_t service, u8_t opcode, u8_t index, + struct os_mbuf *buf); + +u8_t tester_init_gap(void); +u8_t tester_unregister_gap(void); +void tester_handle_gap(u8_t opcode, u8_t index, u8_t *data, + u16_t len); +u8_t tester_init_gatt(void); +u8_t tester_unregister_gatt(void); +void tester_handle_gatt(u8_t opcode, u8_t index, u8_t *data, + u16_t len); +int tester_gatt_notify_rx_ev(u16_t conn_handle, u16_t attr_handle, + u8_t indication, struct os_mbuf *om); +int tester_gatt_subscribe_ev(u16_t conn_handle, u16_t attr_handle, u8_t reason, + u8_t prev_notify, u8_t cur_notify, + u8_t prev_indicate, u8_t cur_indicate); + +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) +u8_t tester_init_l2cap(void); +u8_t tester_unregister_l2cap(void); +void tester_handle_l2cap(u8_t opcode, u8_t index, u8_t *data, + u16_t len); +#endif + +#if MYNEWT_VAL(BLE_MESH) +u8_t tester_init_mesh(void); +u8_t tester_unregister_mesh(void); +void tester_handle_mesh(u8_t opcode, u8_t index, u8_t *data, u16_t len); +#endif /* MYNEWT_VAL(BLE_MESH) */ + +void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg); +int gatt_svr_init(void); + +#endif /* __BTTESTER_H__ */ diff --git a/src/libs/mynewt-nimble/apps/bttester/src/bttester_pipe.h b/src/libs/mynewt-nimble/apps/bttester/src/bttester_pipe.h new file mode 100644 index 00000000..c54d42de --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bttester/src/bttester_pipe.h @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef __BTTESTER_PIPE_H__ +#define __BTTESTER_PIPE_H__ + +#include +#include "bttester.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef u8_t *(*bttester_pipe_recv_cb)(u8_t *buf, size_t *off); +void bttester_pipe_register(u8_t *buf, size_t len, bttester_pipe_recv_cb cb); +int bttester_pipe_send(const u8_t *data, int len); +int bttester_pipe_send_buf(struct os_mbuf *buf); +int bttester_pipe_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __BTTESTER_PIPE_H__ */ diff --git a/src/libs/mynewt-nimble/apps/bttester/src/gap.c b/src/libs/mynewt-nimble/apps/bttester/src/gap.c new file mode 100644 index 00000000..9d6de043 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bttester/src/gap.c @@ -0,0 +1,1688 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* gap.c - Bluetooth GAP Tester */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "host/ble_gap.h" +#include "host/util/util.h" +#include "console/console.h" + +#include "../../../nimble/host/src/ble_hs_pvcy_priv.h" +#include "../../../nimble/host/src/ble_hs_hci_priv.h" +#include "../../../nimble/host/src/ble_sm_priv.h" + +#include "bttester.h" + +#define CONTROLLER_INDEX 0 +#define CONTROLLER_NAME "btp_tester" + +#define BLE_AD_DISCOV_MASK (BLE_HS_ADV_F_DISC_LTD | BLE_HS_ADV_F_DISC_GEN) +#define ADV_BUF_LEN (sizeof(struct gap_device_found_ev) + 2 * 31) + +const uint8_t irk[16] = { + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, +}; + +static uint8_t oob[16]; +static struct ble_sm_sc_oob_data oob_data_local; +static struct ble_sm_sc_oob_data oob_data_remote; + +static uint16_t current_settings; +u8_t own_addr_type; +static ble_addr_t peer_id_addr; +static ble_addr_t peer_ota_addr; +static bool encrypted = false; + +static struct os_callout update_params_co; +static struct gap_conn_param_update_cmd update_params; + +static struct os_callout connected_ev_co; +static struct gap_device_connected_ev connected_ev; +#define CONNECTED_EV_DELAY_MS(itvl) 8 * BLE_HCI_CONN_ITVL * itvl / 1000 +static int connection_attempts; + +static const struct ble_gap_conn_params dflt_conn_params = { + .scan_itvl = 0x0010, + .scan_window = 0x0010, + .itvl_min = BLE_GAP_INITIAL_CONN_ITVL_MIN, + .itvl_max = BLE_GAP_INITIAL_CONN_ITVL_MAX, + .latency = 0, + .supervision_timeout = 0x0100, + .min_ce_len = 0x0010, + .max_ce_len = 0x0300, +}; + +static void conn_param_update(struct os_event *ev); + + +static int gap_conn_find_by_addr(const ble_addr_t *dev_addr, + struct ble_gap_conn_desc *out_desc) +{ + ble_addr_t addr = *dev_addr; + + if (memcmp(BLE_ADDR_ANY, &peer_id_addr, 6) == 0) { + return ble_gap_conn_find_by_addr(&addr, out_desc); + } + + if (BLE_ADDR_IS_RPA(&addr)) { + if(ble_addr_cmp(&peer_ota_addr, &addr) != 0) { + return -1; + } + + return ble_gap_conn_find_by_addr(&addr, out_desc); + } else { + if(ble_addr_cmp(&peer_id_addr, &addr) != 0) { + return -1; + } + + if (BLE_ADDR_IS_RPA(&peer_ota_addr)) { + /* Change addr type to ID addr */ + addr.type |= 2; + } + + return ble_gap_conn_find_by_addr(&addr, out_desc); + } +} + +static int gap_event_cb(struct ble_gap_event *event, void *arg); + +static void supported_commands(u8_t *data, u16_t len) +{ + u8_t cmds[3]; + struct gap_read_supported_commands_rp *rp = (void *) &cmds; + + SYS_LOG_DBG(""); + + memset(cmds, 0, sizeof(cmds)); + + tester_set_bit(cmds, GAP_READ_SUPPORTED_COMMANDS); + tester_set_bit(cmds, GAP_READ_CONTROLLER_INDEX_LIST); + tester_set_bit(cmds, GAP_READ_CONTROLLER_INFO); + tester_set_bit(cmds, GAP_SET_CONNECTABLE); + tester_set_bit(cmds, GAP_SET_DISCOVERABLE); + tester_set_bit(cmds, GAP_SET_BONDABLE); + tester_set_bit(cmds, GAP_START_ADVERTISING); + tester_set_bit(cmds, GAP_STOP_ADVERTISING); + tester_set_bit(cmds, GAP_START_DISCOVERY); + tester_set_bit(cmds, GAP_STOP_DISCOVERY); + tester_set_bit(cmds, GAP_CONNECT); + tester_set_bit(cmds, GAP_DISCONNECT); + tester_set_bit(cmds, GAP_SET_IO_CAP); + tester_set_bit(cmds, GAP_PAIR); + tester_set_bit(cmds, GAP_UNPAIR); + tester_set_bit(cmds, GAP_PASSKEY_ENTRY); + tester_set_bit(cmds, GAP_PASSKEY_CONFIRM); + tester_set_bit(cmds, GAP_START_DIRECT_ADV); + tester_set_bit(cmds, GAP_CONN_PARAM_UPDATE); + tester_set_bit(cmds, GAP_OOB_LEGACY_SET_DATA); + tester_set_bit(cmds, GAP_OOB_SC_GET_LOCAL_DATA); + tester_set_bit(cmds, GAP_OOB_SC_SET_REMOTE_DATA); + tester_set_bit(cmds, GAP_SET_MITM); + + tester_send(BTP_SERVICE_ID_GAP, GAP_READ_SUPPORTED_COMMANDS, + CONTROLLER_INDEX, (u8_t *) rp, sizeof(cmds)); +} + +static void controller_index_list(u8_t *data, u16_t len) +{ + struct gap_read_controller_index_list_rp *rp; + u8_t buf[sizeof(*rp) + 1]; + + SYS_LOG_DBG(""); + + rp = (void *) buf; + + rp->num = 1; + rp->index[0] = CONTROLLER_INDEX; + + tester_send(BTP_SERVICE_ID_GAP, GAP_READ_CONTROLLER_INDEX_LIST, + BTP_INDEX_NONE, (u8_t *) rp, sizeof(buf)); +} + +static int check_pub_addr_unassigned(void) +{ +#ifdef ARCH_sim + return 0; +#else + uint8_t zero_addr[BLE_DEV_ADDR_LEN] = { 0 }; + + return memcmp(MYNEWT_VAL(BLE_PUBLIC_DEV_ADDR), + zero_addr, BLE_DEV_ADDR_LEN) == 0; +#endif +} + +static void controller_info(u8_t *data, u16_t len) +{ + struct gap_read_controller_info_rp rp; + u32_t supported_settings = 0; + ble_addr_t addr; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_hs_pvcy_set_our_irk(irk); + assert(rc == 0); + + memset(&rp, 0, sizeof(rp)); + + /* Make sure we have proper identity address set (public preferred) */ + rc = ble_hs_util_ensure_addr(1); + assert(rc == 0); + rc = ble_hs_id_copy_addr(BLE_ADDR_RANDOM, addr.val, NULL); + assert(rc == 0); + + if (MYNEWT_VAL(BTTESTER_PRIVACY_MODE)) { + if (MYNEWT_VAL(BTTESTER_USE_NRPA)) { + own_addr_type = BLE_OWN_ADDR_RANDOM; + rc = ble_hs_id_gen_rnd(1, &addr); + assert(rc == 0); + rc = ble_hs_id_set_rnd(addr.val); + assert(rc == 0); + } else { + own_addr_type = BLE_OWN_ADDR_RPA_RANDOM_DEFAULT; + } + current_settings |= BIT(GAP_SETTINGS_PRIVACY); + supported_settings |= BIT(GAP_SETTINGS_PRIVACY); + memcpy(rp.address, addr.val, sizeof(rp.address)); + } else { + if (check_pub_addr_unassigned()) { + own_addr_type = BLE_OWN_ADDR_RANDOM; + memcpy(rp.address, addr.val, sizeof(rp.address)); + supported_settings |= BIT(GAP_SETTINGS_STATIC_ADDRESS); + current_settings |= BIT(GAP_SETTINGS_STATIC_ADDRESS); + } else { + own_addr_type = BLE_OWN_ADDR_PUBLIC; + memcpy(rp.address, MYNEWT_VAL(BLE_PUBLIC_DEV_ADDR), + sizeof(rp.address)); + } + } + + supported_settings |= BIT(GAP_SETTINGS_POWERED); + supported_settings |= BIT(GAP_SETTINGS_CONNECTABLE); + supported_settings |= BIT(GAP_SETTINGS_BONDABLE); + supported_settings |= BIT(GAP_SETTINGS_LE); + supported_settings |= BIT(GAP_SETTINGS_ADVERTISING); + supported_settings |= BIT(GAP_SETTINGS_SC); + + if (ble_hs_cfg.sm_bonding) { + current_settings |= BIT(GAP_SETTINGS_BONDABLE); + } + if (ble_hs_cfg.sm_sc) { + current_settings |= BIT(GAP_SETTINGS_SC); + } + + rp.supported_settings = sys_cpu_to_le32(supported_settings); + rp.current_settings = sys_cpu_to_le32(current_settings); + + memcpy(rp.name, CONTROLLER_NAME, sizeof(CONTROLLER_NAME)); + + tester_send(BTP_SERVICE_ID_GAP, GAP_READ_CONTROLLER_INFO, + CONTROLLER_INDEX, (u8_t *) &rp, sizeof(rp)); +} + +static struct ble_gap_adv_params adv_params = { + .conn_mode = BLE_GAP_CONN_MODE_NON, + .disc_mode = BLE_GAP_DISC_MODE_NON, +}; + +static void set_connectable(u8_t *data, u16_t len) +{ + const struct gap_set_connectable_cmd *cmd = (void *) data; + struct gap_set_connectable_rp rp; + + SYS_LOG_DBG(""); + + if (cmd->connectable) { + current_settings |= BIT(GAP_SETTINGS_CONNECTABLE); + adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; + } else { + current_settings &= ~BIT(GAP_SETTINGS_CONNECTABLE); + adv_params.conn_mode = BLE_GAP_CONN_MODE_NON; + } + + rp.current_settings = sys_cpu_to_le32(current_settings); + + tester_send(BTP_SERVICE_ID_GAP, GAP_SET_CONNECTABLE, CONTROLLER_INDEX, + (u8_t *) &rp, sizeof(rp)); +} + +static u8_t ad_flags = BLE_HS_ADV_F_BREDR_UNSUP; + +static void set_discoverable(u8_t *data, u16_t len) +{ + const struct gap_set_discoverable_cmd *cmd = (void *) data; + struct gap_set_discoverable_rp rp; + + SYS_LOG_DBG(""); + + switch (cmd->discoverable) { + case GAP_NON_DISCOVERABLE: + ad_flags &= ~(BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_DISC_LTD); + adv_params.disc_mode = BLE_GAP_DISC_MODE_NON; + current_settings &= ~BIT(GAP_SETTINGS_DISCOVERABLE); + break; + case GAP_GENERAL_DISCOVERABLE: + ad_flags &= ~BLE_HS_ADV_F_DISC_LTD; + ad_flags |= BLE_HS_ADV_F_DISC_GEN; + adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + current_settings |= BIT(GAP_SETTINGS_DISCOVERABLE); + break; + case GAP_LIMITED_DISCOVERABLE: + ad_flags &= ~BLE_HS_ADV_F_DISC_GEN; + ad_flags |= BLE_HS_ADV_F_DISC_LTD; + adv_params.disc_mode = BLE_GAP_DISC_MODE_LTD; + current_settings |= BIT(GAP_SETTINGS_DISCOVERABLE); + break; + default: + tester_rsp(BTP_SERVICE_ID_GAP, GAP_SET_DISCOVERABLE, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + return; + } + + rp.current_settings = sys_cpu_to_le32(current_settings); + + tester_send(BTP_SERVICE_ID_GAP, GAP_SET_DISCOVERABLE, CONTROLLER_INDEX, + (u8_t *) &rp, sizeof(rp)); +} + +static void set_bondable(const u8_t *data, u16_t len) +{ + const struct gap_set_bondable_cmd *cmd = (void *) data; + struct gap_set_bondable_rp rp; + + SYS_LOG_DBG(""); + + ble_hs_cfg.sm_bonding = cmd->bondable; + if (ble_hs_cfg.sm_bonding) { + current_settings |= BIT(GAP_SETTINGS_BONDABLE); + } else { + current_settings &= ~BIT(GAP_SETTINGS_BONDABLE); + } + + rp.current_settings = sys_cpu_to_le32(current_settings); + + tester_send(BTP_SERVICE_ID_GAP, GAP_SET_BONDABLE, CONTROLLER_INDEX, + (u8_t *) &rp, sizeof(rp)); +} + +static struct bt_data ad[10] = { + BT_DATA(BLE_HS_ADV_TYPE_FLAGS, &ad_flags, sizeof(ad_flags)), +}; +static struct bt_data sd[10]; + +static int set_ad(const struct bt_data *ad, size_t ad_len, + u8_t *buf, u8_t *buf_len) +{ + int i; + + for (i = 0; i < ad_len; i++) { + buf[(*buf_len)++] = ad[i].data_len + 1; + buf[(*buf_len)++] = ad[i].type; + + memcpy(&buf[*buf_len], ad[i].data, + ad[i].data_len); + *buf_len += ad[i].data_len; + } + + return 0; +} + +static void start_advertising(const u8_t *data, u16_t len) +{ + const struct gap_start_advertising_cmd *cmd = (void *) data; + struct gap_start_advertising_rp rp; + int32_t duration_ms = BLE_HS_FOREVER; + uint8_t buf[BLE_HS_ADV_MAX_SZ]; + uint8_t buf_len = 0; + u8_t adv_len, sd_len; + int err; + + int i; + + SYS_LOG_DBG(""); + + for (i = 0, adv_len = 1; i < cmd->adv_data_len; adv_len++) { + if (adv_len >= ARRAY_SIZE(ad)) { + SYS_LOG_ERR("ad[] Out of memory"); + goto fail; + } + + ad[adv_len].type = cmd->adv_data[i++]; + ad[adv_len].data_len = cmd->adv_data[i++]; + ad[adv_len].data = &cmd->adv_data[i]; + i += ad[adv_len].data_len; + } + + for (i = 0, sd_len = 0; i < cmd->scan_rsp_len; sd_len++) { + if (sd_len >= ARRAY_SIZE(sd)) { + SYS_LOG_ERR("sd[] Out of memory"); + goto fail; + } + + sd[sd_len].type = cmd->scan_rsp[i++]; + sd[sd_len].data_len = cmd->scan_rsp[i++]; + sd[sd_len].data = &cmd->scan_rsp[i]; + i += sd[sd_len].data_len; + } + + err = set_ad(ad, adv_len, buf, &buf_len); + if (err) { + goto fail; + } + + err = ble_gap_adv_set_data(buf, buf_len); + if (err != 0) { + goto fail; + } + + if (sd_len) { + buf_len = 0; + + err = set_ad(sd, sd_len, buf, &buf_len); + if (err) { + SYS_LOG_ERR("Advertising failed: err %d", err); + goto fail; + } + + err = ble_gap_adv_rsp_set_data(buf, buf_len); + if (err != 0) { + SYS_LOG_ERR("Advertising failed: err %d", err); + goto fail; + } + } + + if (adv_params.disc_mode == BLE_GAP_DISC_MODE_LTD) { + duration_ms = MYNEWT_VAL(BTTESTER_LTD_ADV_TIMEOUT); + } + + err = ble_gap_adv_start(own_addr_type, NULL, duration_ms, + &adv_params, gap_event_cb, NULL); + if (err) { + SYS_LOG_ERR("Advertising failed: err %d", err); + goto fail; + } + + current_settings |= BIT(GAP_SETTINGS_ADVERTISING); + rp.current_settings = sys_cpu_to_le32(current_settings); + + tester_send(BTP_SERVICE_ID_GAP, GAP_START_ADVERTISING, CONTROLLER_INDEX, + (u8_t *) &rp, sizeof(rp)); + return; +fail: + tester_rsp(BTP_SERVICE_ID_GAP, GAP_START_ADVERTISING, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void stop_advertising(const u8_t *data, u16_t len) +{ + struct gap_stop_advertising_rp rp; + + SYS_LOG_DBG(""); + + if (ble_gap_adv_stop() != 0) { + tester_rsp(BTP_SERVICE_ID_GAP, GAP_STOP_ADVERTISING, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + return; + } + + current_settings &= ~BIT(GAP_SETTINGS_ADVERTISING); + rp.current_settings = sys_cpu_to_le32(current_settings); + + tester_send(BTP_SERVICE_ID_GAP, GAP_STOP_ADVERTISING, CONTROLLER_INDEX, + (u8_t *) &rp, sizeof(rp)); +} + +static u8_t get_ad_flags(const u8_t *data, u8_t data_len) +{ + u8_t len, i; + + /* Parse advertisement to get flags */ + for (i = 0; i < data_len; i += len - 1) { + len = data[i++]; + if (!len) { + break; + } + + /* Check if field length is correct */ + if (len > (data_len - i) || (data_len - i) < 1) { + break; + } + + switch (data[i++]) { + case BLE_HS_ADV_TYPE_FLAGS: + return data[i]; + default: + break; + } + } + + return 0; +} + +static u8_t discovery_flags; +static struct os_mbuf *adv_buf; + +static void store_adv(const ble_addr_t *addr, s8_t rssi, + const u8_t *data, u8_t len) +{ + struct gap_device_found_ev *ev; + + /* cleanup */ + net_buf_simple_init(adv_buf, 0); + + ev = net_buf_simple_add(adv_buf, sizeof(*ev)); + + memcpy(ev->address, addr->val, sizeof(ev->address)); + ev->address_type = addr->type; + ev->rssi = rssi; + ev->flags = GAP_DEVICE_FOUND_FLAG_AD | GAP_DEVICE_FOUND_FLAG_RSSI; + ev->eir_data_len = len; + memcpy(net_buf_simple_add(adv_buf, len), data, len); +} + +static void device_found(ble_addr_t *addr, s8_t rssi, u8_t evtype, + const u8_t *data, u8_t len) +{ + struct gap_device_found_ev *ev; + ble_addr_t a; + + /* if General/Limited Discovery - parse Advertising data to get flags */ + if (!(discovery_flags & GAP_DISCOVERY_FLAG_LE_OBSERVE) && + (evtype != BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP)) { + u8_t flags = get_ad_flags(data, len); + + /* ignore non-discoverable devices */ + if (!(flags & BLE_AD_DISCOV_MASK)) { + SYS_LOG_DBG("Non discoverable, skipping"); + return; + } + + /* if Limited Discovery - ignore general discoverable devices */ + if ((discovery_flags & GAP_DISCOVERY_FLAG_LIMITED) && + !(flags & BLE_HS_ADV_F_DISC_LTD)) { + SYS_LOG_DBG("General discoverable, skipping"); + return; + } + } + + /* attach Scan Response data */ + if (evtype == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) { + /* skip if there is no pending advertisement */ + if (!adv_buf->om_len) { + SYS_LOG_INF("No pending advertisement, skipping"); + return; + } + + ev = (void *) adv_buf->om_data; + a.type = ev->address_type; + memcpy(a.val, ev->address, sizeof(a.val)); + + /* + * in general, the Scan Response comes right after the + * Advertisement, but if not if send stored event and ignore + * this one + */ + if (ble_addr_cmp(addr, &a)) { + SYS_LOG_INF("Address does not match, skipping"); + goto done; + } + + ev->eir_data_len += len; + ev->flags |= GAP_DEVICE_FOUND_FLAG_SD; + + memcpy(net_buf_simple_add(adv_buf, len), data, len); + + goto done; + } + + /* + * if there is another pending advertisement, send it and store the + * current one + */ + if (adv_buf->om_len) { + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_FOUND, + CONTROLLER_INDEX, adv_buf->om_data, + adv_buf->om_len); + } + + store_adv(addr, rssi, data, len); + + /* if Active Scan and scannable event - wait for Scan Response */ + if ((discovery_flags & GAP_DISCOVERY_FLAG_LE_ACTIVE_SCAN) && + (evtype == BLE_HCI_ADV_RPT_EVTYPE_ADV_IND || + evtype == BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND)) { + SYS_LOG_DBG("Waiting for scan response"); + return; + } +done: + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_FOUND, + CONTROLLER_INDEX, adv_buf->om_data, adv_buf->om_len); +} + +static int discovery_cb(struct ble_gap_event *event, void *arg) +{ + if (event->type == BLE_GAP_EVENT_DISC) { + device_found(&event->disc.addr, event->disc.rssi, + event->disc.event_type, event->disc.data, + event->disc.length_data); + } + + return 0; +} + +static void start_discovery(const u8_t *data, u16_t len) +{ + const struct gap_start_discovery_cmd *cmd = (void *) data; + struct ble_gap_disc_params params = {0}; + u8_t status; + + SYS_LOG_DBG(""); + + /* only LE scan is supported */ + if (cmd->flags & GAP_DISCOVERY_FLAG_BREDR) { + status = BTP_STATUS_FAILED; + goto reply; + } + + params.passive = (cmd->flags & GAP_DISCOVERY_FLAG_LE_ACTIVE_SCAN) == 0; + params.limited = (cmd->flags & GAP_DISCOVERY_FLAG_LIMITED) > 0; + params.filter_duplicates = 1; + + if (ble_gap_disc(own_addr_type, BLE_HS_FOREVER, + ¶ms, discovery_cb, NULL) != 0) { + status = BTP_STATUS_FAILED; + goto reply; + } + + net_buf_simple_init(adv_buf, 0); + discovery_flags = cmd->flags; + + status = BTP_STATUS_SUCCESS; +reply: + tester_rsp(BTP_SERVICE_ID_GAP, GAP_START_DISCOVERY, CONTROLLER_INDEX, + status); +} + +static void stop_discovery(const u8_t *data, u16_t len) +{ + u8_t status = BTP_STATUS_SUCCESS; + + SYS_LOG_DBG(""); + + if (ble_gap_disc_cancel() != 0) { + status = BTP_STATUS_FAILED; + } + + tester_rsp(BTP_SERVICE_ID_GAP, GAP_STOP_DISCOVERY, CONTROLLER_INDEX, + status); +} + + +/* Bluetooth Core Spec v5.1 | Section 10.7.1 + * If a privacy-enabled Peripheral, that has a stored bond, + * receives a resolvable private address, the Host may resolve + * the resolvable private address [...] + * If the resolution is successful, the Host may accept the connection. + * If the resolution procedure fails, then the Host shall disconnect + * with the error code "Authentication failure" [...] + */ +static void periph_privacy(struct ble_gap_conn_desc desc) +{ +#if !MYNEWT_VAL(BTTESTER_PRIVACY_MODE) + return; +#endif + int count; + + SYS_LOG_DBG(""); + + ble_store_util_count(BLE_STORE_OBJ_TYPE_PEER_SEC, &count); + if (count > 0 && BLE_ADDR_IS_RPA(&desc.peer_id_addr)) { + SYS_LOG_DBG("Authentication failure, disconnecting"); + ble_gap_terminate(desc.conn_handle, BLE_ERR_AUTH_FAIL); + } +} + +static void device_connected_ev_send(struct os_event *ev) +{ + struct ble_gap_conn_desc desc; + int rc; + + SYS_LOG_DBG(""); + + rc = gap_conn_find_by_addr((ble_addr_t *)&connected_ev, &desc); + if (rc) { + tester_rsp(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_CONNECTED, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + return; + } + + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_CONNECTED, + CONTROLLER_INDEX, (u8_t *) &connected_ev, + sizeof(connected_ev)); + + periph_privacy(desc); +} + +static void le_connected(u16_t conn_handle, int status) +{ + struct ble_gap_conn_desc desc; + ble_addr_t *addr; + int rc; + + SYS_LOG_DBG(""); + + if (status != 0) { + return; + } + + rc = ble_gap_conn_find(conn_handle, &desc); + if (rc) { + return; + } + + peer_id_addr = desc.peer_id_addr; + peer_ota_addr = desc.peer_ota_addr; + + addr = &desc.peer_id_addr; + + memcpy(connected_ev.address, addr->val, sizeof(connected_ev.address)); + connected_ev.address_type = addr->type; + connected_ev.conn_itvl = desc.conn_itvl; + connected_ev.conn_latency = desc.conn_latency; + connected_ev.supervision_timeout = desc.supervision_timeout; + +#if MYNEWT_VAL(BTTESTER_CONN_RETRY) + os_callout_reset(&connected_ev_co, + os_time_ms_to_ticks32( + CONNECTED_EV_DELAY_MS(desc.conn_itvl))); +#else + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_CONNECTED, + CONTROLLER_INDEX, (u8_t *) &connected_ev, + sizeof(connected_ev)); +#endif +} + +static void le_disconnected(struct ble_gap_conn_desc *conn, int reason) +{ + struct gap_device_disconnected_ev ev; + ble_addr_t *addr = &conn->peer_ota_addr; + + SYS_LOG_DBG(""); + +#if MYNEWT_VAL(BTTESTER_CONN_RETRY) + int rc; + + if ((reason == BLE_HS_HCI_ERR(BLE_ERR_CONN_ESTABLISHMENT)) && + os_callout_queued(&connected_ev_co)) { + if (connection_attempts < MYNEWT_VAL(BTTESTER_CONN_RETRY)) { + os_callout_stop(&connected_ev_co); + + /* try connecting again */ + rc = ble_gap_connect(own_addr_type, addr, 0, + &dflt_conn_params, gap_event_cb, + NULL); + + if (rc == 0) { + connection_attempts++; + return; + } + } + } else if (os_callout_queued(&connected_ev_co)) { + os_callout_stop(&connected_ev_co); + return; + } +#endif + + connection_attempts = 0; + memset(&connected_ev, 0, sizeof(connected_ev)); + + memcpy(ev.address, addr->val, sizeof(ev.address)); + ev.address_type = addr->type; + + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_DISCONNECTED, + CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev)); +} + +static void auth_passkey_oob(u16_t conn_handle) +{ + struct ble_gap_conn_desc desc; + struct ble_sm_io pk; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find(conn_handle, &desc); + if (rc) { + return; + } + + memcpy(pk.oob, oob, sizeof(oob)); + pk.action = BLE_SM_IOACT_OOB; + + rc = ble_sm_inject_io(conn_handle, &pk); + assert(rc == 0); +} + +static void auth_passkey_display(u16_t conn_handle, unsigned int passkey) +{ + struct ble_gap_conn_desc desc; + struct gap_passkey_display_ev ev; + ble_addr_t *addr; + struct ble_sm_io pk; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find(conn_handle, &desc); + if (rc) { + return; + } + + rc = ble_hs_hci_util_rand(&pk.passkey, sizeof(pk.passkey)); + assert(rc == 0); + /* Max value is 999999 */ + pk.passkey %= 1000000; + pk.action = BLE_SM_IOACT_DISP; + + rc = ble_sm_inject_io(conn_handle, &pk); + assert(rc == 0); + + addr = &desc.peer_ota_addr; + + memcpy(ev.address, addr->val, sizeof(ev.address)); + ev.address_type = addr->type; + ev.passkey = sys_cpu_to_le32(pk.passkey); + + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_PASSKEY_DISPLAY, + CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev)); +} + +static void auth_passkey_entry(u16_t conn_handle) +{ + struct ble_gap_conn_desc desc; + struct gap_passkey_entry_req_ev ev; + ble_addr_t *addr; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find(conn_handle, &desc); + if (rc) { + return; + } + + addr = &desc.peer_ota_addr; + + memcpy(ev.address, addr->val, sizeof(ev.address)); + ev.address_type = addr->type; + + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_PASSKEY_ENTRY_REQ, + CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev)); +} + +static void auth_passkey_numcmp(u16_t conn_handle, unsigned int passkey) +{ + struct ble_gap_conn_desc desc; + struct gap_passkey_confirm_req_ev ev; + ble_addr_t *addr; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find(conn_handle, &desc); + if (rc) { + return; + } + + addr = &desc.peer_ota_addr; + + memcpy(ev.address, addr->val, sizeof(ev.address)); + ev.address_type = addr->type; + ev.passkey = sys_cpu_to_le32(passkey); + + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_PASSKEY_CONFIRM_REQ, + CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev)); +} + +static void auth_passkey_oob_sc(u16_t conn_handle) +{ + int rc; + struct ble_sm_io pk; + + SYS_LOG_DBG(""); + + memset(&pk, 0, sizeof(pk)); + + pk.oob_sc_data.local = &oob_data_local; + + if (ble_hs_cfg.sm_oob_data_flag) { + pk.oob_sc_data.remote = &oob_data_remote; + } + + pk.action = BLE_SM_IOACT_OOB_SC; + rc = ble_sm_inject_io(conn_handle, &pk); + if (rc != 0) { + console_printf("error providing oob; rc=%d\n", rc); + } +} + +static void le_passkey_action(u16_t conn_handle, + struct ble_gap_passkey_params *params) +{ + SYS_LOG_DBG(""); + + switch (params->action) { + case BLE_SM_IOACT_NONE: + break; + case BLE_SM_IOACT_OOB: + auth_passkey_oob(conn_handle); + break; + case BLE_SM_IOACT_INPUT: + auth_passkey_entry(conn_handle); + break; + case BLE_SM_IOACT_DISP: + auth_passkey_display(conn_handle, params->numcmp); + break; + case BLE_SM_IOACT_NUMCMP: + auth_passkey_numcmp(conn_handle, params->numcmp); + break; + case BLE_SM_IOACT_OOB_SC: + auth_passkey_oob_sc(conn_handle); + break; + default: + assert(0); + } +} + +static void le_identity_resolved(u16_t conn_handle) +{ + struct ble_gap_conn_desc desc; + struct gap_identity_resolved_ev ev; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find(conn_handle, &desc); + if (rc) { + return; + } + + peer_id_addr = desc.peer_id_addr; + peer_ota_addr = desc.peer_ota_addr; + + ev.address_type = desc.peer_ota_addr.type; + memcpy(ev.address, desc.peer_ota_addr.val, sizeof(ev.address)); + + ev.identity_address_type = desc.peer_id_addr.type; + memcpy(ev.identity_address, desc.peer_id_addr.val, + sizeof(ev.identity_address)); + + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_IDENTITY_RESOLVED, + CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev)); +} + +static void le_conn_param_update(struct ble_gap_conn_desc *desc) +{ + struct gap_conn_param_update_ev ev; + + SYS_LOG_DBG(""); + + ev.address_type = desc->peer_ota_addr.type; + memcpy(ev.address, desc->peer_ota_addr.val, sizeof(ev.address)); + + ev.conn_itvl = desc->conn_itvl; + ev.conn_latency = desc->conn_latency; + ev.supervision_timeout = desc->supervision_timeout; + + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_CONN_PARAM_UPDATE, + CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev)); +} + +static void le_encryption_changed(struct ble_gap_conn_desc *desc) +{ + struct gap_sec_level_changed_ev ev; + + SYS_LOG_DBG(""); + + encrypted = (bool) desc->sec_state.encrypted; + + ev.address_type = desc->peer_ota_addr.type; + memcpy(ev.address, desc->peer_ota_addr.val, sizeof(ev.address)); + ev.level = 0; + + if (desc->sec_state.encrypted) { + if (desc->sec_state.authenticated) { + if (desc->sec_state.key_size == 16) { + ev.level = 3; + } else { + ev.level = 2; + } + } else { + ev.level = 1; + } + } + + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_SEC_LEVEL_CHANGED, + CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev)); +} + +static void print_bytes(const uint8_t *bytes, int len) +{ + int i; + + for (i = 0; i < len; i++) { + console_printf("%s0x%02x", i != 0 ? ":" : "", bytes[i]); + } +} + +static void print_mbuf(const struct os_mbuf *om) +{ + int colon; + + colon = 0; + while (om != NULL) { + if (colon) { + console_printf(":"); + } else { + colon = 1; + } + print_bytes(om->om_data, om->om_len); + om = SLIST_NEXT(om, om_next); + } +} + +static void print_addr(const void *addr) +{ + const uint8_t *u8p; + + u8p = addr; + console_printf("%02x:%02x:%02x:%02x:%02x:%02x", + u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); +} + +static void print_conn_desc(const struct ble_gap_conn_desc *desc) +{ + console_printf("handle=%d our_ota_addr_type=%d our_ota_addr=", + desc->conn_handle, desc->our_ota_addr.type); + print_addr(desc->our_ota_addr.val); + console_printf(" our_id_addr_type=%d our_id_addr=", + desc->our_id_addr.type); + print_addr(desc->our_id_addr.val); + console_printf(" peer_ota_addr_type=%d peer_ota_addr=", + desc->peer_ota_addr.type); + print_addr(desc->peer_ota_addr.val); + console_printf(" peer_id_addr_type=%d peer_id_addr=", + desc->peer_id_addr.type); + print_addr(desc->peer_id_addr.val); + console_printf(" conn_itvl=%d conn_latency=%d supervision_timeout=%d " + "key_sz=%d encrypted=%d authenticated=%d bonded=%d\n", + desc->conn_itvl, desc->conn_latency, + desc->supervision_timeout, + desc->sec_state.key_size, + desc->sec_state.encrypted, + desc->sec_state.authenticated, + desc->sec_state.bonded); +} + +static void adv_complete(void) +{ + struct gap_new_settings_ev ev; + + current_settings &= ~BIT(GAP_SETTINGS_ADVERTISING); + ev.current_settings = sys_cpu_to_le32(current_settings); + + tester_send(BTP_SERVICE_ID_GAP, GAP_EV_NEW_SETTINGS, CONTROLLER_INDEX, + (u8_t *) &ev, sizeof(ev)); +} + +static int gap_event_cb(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_ADV_COMPLETE: + console_printf("advertising complete; reason=%d\n", + event->adv_complete.reason); + break; + case BLE_GAP_EVENT_CONNECT: + console_printf("connection %s; status=%d ", + event->connect.status == 0 ? "established" : "failed", + event->connect.status); + if (event->connect.status == 0) { + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + } + + if (desc.role == BLE_GAP_ROLE_SLAVE) { + adv_complete(); + } + + le_connected(event->connect.conn_handle, + event->connect.status); + break; + case BLE_GAP_EVENT_DISCONNECT: + console_printf("disconnect; reason=%d ", event->disconnect.reason); + print_conn_desc(&event->disconnect.conn); + le_disconnected(&event->disconnect.conn, + event->disconnect.reason); + break; + case BLE_GAP_EVENT_ENC_CHANGE: + console_printf("encryption change event; status=%d ", event->enc_change.status); + rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + le_encryption_changed(&desc); + break; + case BLE_GAP_EVENT_PASSKEY_ACTION: + console_printf("passkey action event; action=%d", + event->passkey.params.action); + if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) { + console_printf(" numcmp=%lu", + (unsigned long)event->passkey.params.numcmp); + } + console_printf("\n"); + le_passkey_action(event->passkey.conn_handle, + &event->passkey.params); + break; + case BLE_GAP_EVENT_IDENTITY_RESOLVED: + console_printf("identity resolved "); + rc = ble_gap_conn_find(event->identity_resolved.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + le_identity_resolved(event->identity_resolved.conn_handle); + break; + case BLE_GAP_EVENT_NOTIFY_RX: + console_printf("notification rx event; attr_handle=%d indication=%d " + "len=%d data=", + event->notify_rx.attr_handle, + event->notify_rx.indication, + OS_MBUF_PKTLEN(event->notify_rx.om)); + + print_mbuf(event->notify_rx.om); + console_printf("\n"); + tester_gatt_notify_rx_ev(event->notify_rx.conn_handle, + event->notify_rx.attr_handle, + event->notify_rx.indication, + event->notify_rx.om); + break; + case BLE_GAP_EVENT_SUBSCRIBE: + console_printf("subscribe event; conn_handle=%d attr_handle=%d " + "reason=%d prevn=%d curn=%d previ=%d curi=%d\n", + event->subscribe.conn_handle, + event->subscribe.attr_handle, + event->subscribe.reason, + event->subscribe.prev_notify, + event->subscribe.cur_notify, + event->subscribe.prev_indicate, + event->subscribe.cur_indicate); + tester_gatt_subscribe_ev(event->subscribe.conn_handle, + event->subscribe.attr_handle, + event->subscribe.reason, + event->subscribe.prev_notify, + event->subscribe.cur_notify, + event->subscribe.prev_indicate, + event->subscribe.cur_indicate); + break; + case BLE_GAP_EVENT_REPEAT_PAIRING: + console_printf("repeat pairing event; conn_handle=%d " + "cur_key_sz=%d cur_auth=%d cur_sc=%d " + "new_key_sz=%d new_auth=%d new_sc=%d " + "new_bonding=%d\n", + event->repeat_pairing.conn_handle, + event->repeat_pairing.cur_key_size, + event->repeat_pairing.cur_authenticated, + event->repeat_pairing.cur_sc, + event->repeat_pairing.new_key_size, + event->repeat_pairing.new_authenticated, + event->repeat_pairing.new_sc, + event->repeat_pairing.new_bonding); + rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc); + assert(rc == 0); + rc = ble_store_util_delete_peer(&desc.peer_id_addr); + assert(rc == 0); + return BLE_GAP_REPEAT_PAIRING_RETRY; + case BLE_GAP_EVENT_CONN_UPDATE: + console_printf("connection update event; status=%d ", + event->conn_update.status); + rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc); + assert(rc == 0); + print_conn_desc(&desc); + le_conn_param_update(&desc); + break; + case BLE_GAP_EVENT_CONN_UPDATE_REQ: + console_printf("connection update request event; " + "conn_handle=%d itvl_min=%d itvl_max=%d " + "latency=%d supervision_timoeut=%d " + "min_ce_len=%d max_ce_len=%d\n", + event->conn_update_req.conn_handle, + event->conn_update_req.peer_params->itvl_min, + event->conn_update_req.peer_params->itvl_max, + event->conn_update_req.peer_params->latency, + event->conn_update_req.peer_params->supervision_timeout, + event->conn_update_req.peer_params->min_ce_len, + event->conn_update_req.peer_params->max_ce_len); + + *event->conn_update_req.self_params = + *event->conn_update_req.peer_params; + break; + default: + break; + } + + return 0; +} + +static void connect(const u8_t *data, u16_t len) +{ + u8_t status = BTP_STATUS_SUCCESS; + + SYS_LOG_DBG(""); + + if (ble_gap_connect(own_addr_type, (ble_addr_t *) data, 0, + &dflt_conn_params, gap_event_cb, NULL)) { + status = BTP_STATUS_FAILED; + } + + tester_rsp(BTP_SERVICE_ID_GAP, GAP_CONNECT, CONTROLLER_INDEX, status); +} + +static void disconnect(const u8_t *data, u16_t len) +{ + struct ble_gap_conn_desc desc; + u8_t status; + int rc; + + SYS_LOG_DBG(""); + + rc = gap_conn_find_by_addr((ble_addr_t *)data, &desc); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + if (ble_gap_terminate(desc.conn_handle, BLE_ERR_REM_USER_CONN_TERM)) { + status = BTP_STATUS_FAILED; + } else { + status = BTP_STATUS_SUCCESS; + } + +rsp: + tester_rsp(BTP_SERVICE_ID_GAP, GAP_DISCONNECT, CONTROLLER_INDEX, + status); +} + +static void set_io_cap(const u8_t *data, u16_t len) +{ + const struct gap_set_io_cap_cmd *cmd = (void *) data; + u8_t status; + + SYS_LOG_DBG(""); + + switch (cmd->io_cap) { + case GAP_IO_CAP_DISPLAY_ONLY: + ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_DISP_ONLY; + ble_hs_cfg.sm_mitm = 1; + break; + case GAP_IO_CAP_KEYBOARD_DISPLAY: + ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_KEYBOARD_DISP; + ble_hs_cfg.sm_mitm = 1; + break; + case GAP_IO_CAP_NO_INPUT_OUTPUT: + ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_NO_IO; + ble_hs_cfg.sm_mitm = 0; + break; + case GAP_IO_CAP_KEYBOARD_ONLY: + ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_KEYBOARD_ONLY; + ble_hs_cfg.sm_mitm = 1; + break; + case GAP_IO_CAP_DISPLAY_YESNO: + ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_DISP_YES_NO; + ble_hs_cfg.sm_mitm = 1; + break; + default: + status = BTP_STATUS_FAILED; + goto rsp; + } + + status = BTP_STATUS_SUCCESS; + +rsp: + tester_rsp(BTP_SERVICE_ID_GAP, GAP_SET_IO_CAP, CONTROLLER_INDEX, + status); +} + +static void pair(const u8_t *data, u16_t len) +{ + struct ble_gap_conn_desc desc; + u8_t status; + int rc; + + SYS_LOG_DBG(""); + + rc = gap_conn_find_by_addr((ble_addr_t *)data, &desc); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + if (ble_gap_security_initiate(desc.conn_handle)) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + status = BTP_STATUS_SUCCESS; + +rsp: + tester_rsp(BTP_SERVICE_ID_GAP, GAP_PAIR, CONTROLLER_INDEX, status); +} + +static void unpair(const u8_t *data, u16_t len) +{ + u8_t status; + int err; + + SYS_LOG_DBG(""); + + err = ble_gap_unpair((ble_addr_t *) data); + status = (uint8_t) (err != 0 ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS); + tester_rsp(BTP_SERVICE_ID_GAP, GAP_UNPAIR, CONTROLLER_INDEX, status); +} + +static void passkey_entry(const u8_t *data, u16_t len) +{ + const struct gap_passkey_entry_cmd *cmd = (void *) data; + struct ble_gap_conn_desc desc; + struct ble_sm_io pk; + u8_t status; + int rc; + + SYS_LOG_DBG(""); + + rc = gap_conn_find_by_addr((ble_addr_t *)data, &desc); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + pk.action = BLE_SM_IOACT_INPUT; + pk.passkey = sys_le32_to_cpu(cmd->passkey); + + rc = ble_sm_inject_io(desc.conn_handle, &pk); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + status = BTP_STATUS_SUCCESS; + +rsp: + tester_rsp(BTP_SERVICE_ID_GAP, GAP_PASSKEY_ENTRY, CONTROLLER_INDEX, + status); +} + +static void passkey_confirm(const u8_t *data, u16_t len) +{ + const struct gap_passkey_confirm_cmd *cmd = (void *) data; + struct ble_gap_conn_desc desc; + struct ble_sm_io pk; + u8_t status; + int rc; + + SYS_LOG_DBG(""); + + rc = gap_conn_find_by_addr((ble_addr_t *)data, &desc); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + pk.action = BLE_SM_IOACT_NUMCMP; + pk.numcmp_accept = cmd->match; + + rc = ble_sm_inject_io(desc.conn_handle, &pk); + if (rc) { + console_printf("sm inject io failed"); + status = BTP_STATUS_FAILED; + goto rsp; + } + + status = BTP_STATUS_SUCCESS; + +rsp: + tester_rsp(BTP_SERVICE_ID_GAP, GAP_PASSKEY_CONFIRM, CONTROLLER_INDEX, + status); +} + +static void start_direct_adv(const u8_t *data, u16_t len) +{ + const struct gap_start_direct_adv_cmd *cmd = (void *) data; + struct gap_start_advertising_rp rp; + static struct ble_gap_adv_params adv_params = { + .conn_mode = BLE_GAP_CONN_MODE_DIR, + }; + int err; + + SYS_LOG_DBG(""); + + adv_params.high_duty_cycle = cmd->high_duty; + + err = ble_gap_adv_start(own_addr_type, (ble_addr_t *)data, + BLE_HS_FOREVER, &adv_params, + gap_event_cb, NULL); + if (err) { + SYS_LOG_ERR("Advertising failed: err %d", err); + goto fail; + } + + current_settings |= BIT(GAP_SETTINGS_ADVERTISING); + rp.current_settings = sys_cpu_to_le32(current_settings); + + tester_send(BTP_SERVICE_ID_GAP, GAP_START_DIRECT_ADV, CONTROLLER_INDEX, + (u8_t *) &rp, sizeof(rp)); + return; +fail: + tester_rsp(BTP_SERVICE_ID_GAP, GAP_START_DIRECT_ADV, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void conn_param_update_cb(uint16_t conn_handle, int status, void *arg) +{ + console_printf("conn param update complete; conn_handle=%d status=%d\n", + conn_handle, status); +} + +static int conn_param_update_slave(u16_t conn_handle, + const struct gap_conn_param_update_cmd *cmd) +{ + int rc; + struct ble_l2cap_sig_update_params params; + + params.itvl_min = cmd->conn_itvl_min; + params.itvl_max = cmd->conn_itvl_max; + params.slave_latency = cmd->conn_latency; + params.timeout_multiplier = cmd->supervision_timeout; + + rc = ble_l2cap_sig_update(conn_handle, ¶ms, + conn_param_update_cb, NULL); + if (rc) { + SYS_LOG_ERR("Failed to send update params: rc=%d", rc); + } + + return 0; +} + +static int conn_param_update_master(u16_t conn_handle, + const struct gap_conn_param_update_cmd *cmd) +{ + int rc; + struct ble_gap_upd_params params; + + params.itvl_min = cmd->conn_itvl_min; + params.itvl_max = cmd->conn_itvl_max; + params.latency = cmd->conn_latency; + params.supervision_timeout = cmd->supervision_timeout; + params.min_ce_len = 0; + params.max_ce_len = 0; + rc = ble_gap_update_params(conn_handle, ¶ms); + if (rc) { + SYS_LOG_ERR("Failed to send update params: rc=%d", rc); + } + + return rc; +} + +static void conn_param_update(struct os_event *ev) +{ + struct ble_gap_conn_desc desc; + int rc; + + SYS_LOG_DBG(""); + + rc = gap_conn_find_by_addr((ble_addr_t *)&update_params, &desc); + if (rc) { + goto rsp; + } + + if ((desc.conn_itvl >= update_params.conn_itvl_min) && + (desc.conn_itvl <= update_params.conn_itvl_max) && + (desc.conn_latency == update_params.conn_latency) && + (desc.supervision_timeout == update_params.supervision_timeout)) { + goto rsp; + } + + if (desc.role == BLE_GAP_ROLE_MASTER) { + rc = conn_param_update_master(desc.conn_handle, &update_params); + } else { + rc = conn_param_update_slave(desc.conn_handle, &update_params); + } + + if (rc == 0) { + return; + } + +rsp: + SYS_LOG_ERR("Conn param update fail; rc=%d", rc); +} + +static void conn_param_update_async(const u8_t *data, u16_t len) +{ + const struct gap_conn_param_update_cmd *cmd = (void *) data; + update_params = *cmd; + + os_callout_reset(&update_params_co, 0); + + tester_rsp(BTP_SERVICE_ID_GAP, GAP_CONN_PARAM_UPDATE, CONTROLLER_INDEX, + BTP_STATUS_SUCCESS); +} + +static void oob_legacy_set_data(const u8_t *data, u16_t len) +{ + const struct gap_oob_legacy_set_data_cmd *cmd = (void *) data; + + ble_hs_cfg.sm_oob_data_flag = 1; + memcpy(oob, cmd->oob_data, sizeof(oob)); + + tester_rsp(BTP_SERVICE_ID_GAP, GAP_OOB_LEGACY_SET_DATA, + CONTROLLER_INDEX, BTP_STATUS_SUCCESS); +} + +static void oob_sc_get_local_data(const u8_t *data, u16_t len) +{ + struct gap_oob_sc_get_local_data_rp rp; + + memcpy(rp.r, oob_data_local.r, 16); + memcpy(rp.c, oob_data_local.c, 16); + + tester_send(BTP_SERVICE_ID_GAP, GAP_OOB_SC_GET_LOCAL_DATA, + CONTROLLER_INDEX, (u8_t *) &rp, sizeof(rp)); +} + +static void oob_sc_set_remote_data(const u8_t *data, u16_t len) +{ + const struct gap_oob_sc_set_remote_data_cmd *cmd = (void *) data; + + ble_hs_cfg.sm_oob_data_flag = 1; + memcpy(oob_data_remote.r, cmd->r, 16); + memcpy(oob_data_remote.c, cmd->c, 16); + + tester_rsp(BTP_SERVICE_ID_GAP, GAP_OOB_SC_SET_REMOTE_DATA, + CONTROLLER_INDEX, BTP_STATUS_SUCCESS); +} + +static void set_mitm(const u8_t *data, u16_t len) +{ + const struct gap_set_mitm_cmd *cmd = (void *) data; + + ble_hs_cfg.sm_mitm = cmd->mitm; + + tester_rsp(BTP_SERVICE_ID_GAP, GAP_SET_MITM, + CONTROLLER_INDEX, BTP_STATUS_SUCCESS); +} + +void tester_handle_gap(u8_t opcode, u8_t index, u8_t *data, + u16_t len) +{ + switch (opcode) { + case GAP_READ_SUPPORTED_COMMANDS: + case GAP_READ_CONTROLLER_INDEX_LIST: + if (index != BTP_INDEX_NONE){ + tester_rsp(BTP_SERVICE_ID_GAP, opcode, index, + BTP_STATUS_FAILED); + return; + } + break; + default: + if (index != CONTROLLER_INDEX){ + tester_rsp(BTP_SERVICE_ID_GAP, opcode, index, + BTP_STATUS_FAILED); + return; + } + break; + } + + switch (opcode) { + case GAP_READ_SUPPORTED_COMMANDS: + supported_commands(data, len); + return; + case GAP_READ_CONTROLLER_INDEX_LIST: + controller_index_list(data, len); + return; + case GAP_READ_CONTROLLER_INFO: + controller_info(data, len); + return; + case GAP_SET_CONNECTABLE: + set_connectable(data, len); + return; + case GAP_SET_DISCOVERABLE: + set_discoverable(data, len); + return; + case GAP_SET_BONDABLE: + set_bondable(data, len); + return; + case GAP_START_ADVERTISING: + start_advertising(data, len); + return; + case GAP_STOP_ADVERTISING: + stop_advertising(data, len); + return; + case GAP_START_DISCOVERY: + start_discovery(data, len); + return; + case GAP_STOP_DISCOVERY: + stop_discovery(data, len); + return; + case GAP_CONNECT: + connect(data, len); + return; + case GAP_DISCONNECT: + disconnect(data, len); + return; + case GAP_SET_IO_CAP: + set_io_cap(data, len); + return; + case GAP_PAIR: + pair(data, len); + return; + case GAP_UNPAIR: + unpair(data, len); + return; + case GAP_PASSKEY_ENTRY: + passkey_entry(data, len); + return; + case GAP_PASSKEY_CONFIRM: + passkey_confirm(data, len); + return; + case GAP_START_DIRECT_ADV: + start_direct_adv(data, len); + return; + case GAP_CONN_PARAM_UPDATE: + conn_param_update_async(data, len); + return; + case GAP_OOB_LEGACY_SET_DATA: + oob_legacy_set_data(data, len); + return; + case GAP_OOB_SC_GET_LOCAL_DATA: + oob_sc_get_local_data(data, len); + return; + case GAP_OOB_SC_SET_REMOTE_DATA: + oob_sc_set_remote_data(data, len); + return; + case GAP_SET_MITM: + set_mitm(data, len); + return; + default: + tester_rsp(BTP_SERVICE_ID_GAP, opcode, index, + BTP_STATUS_UNKNOWN_CMD); + return; + } +} + +static void tester_init_gap_cb(int err) +{ + if (err) { + tester_rsp(BTP_SERVICE_ID_CORE, CORE_REGISTER_SERVICE, + BTP_INDEX_NONE, BTP_STATUS_FAILED); + return; + } + + current_settings = 0; + current_settings |= BIT(GAP_SETTINGS_POWERED); + current_settings |= BIT(GAP_SETTINGS_LE); + + os_callout_init(&update_params_co, os_eventq_dflt_get(), + conn_param_update, NULL); + + os_callout_init(&connected_ev_co, os_eventq_dflt_get(), + device_connected_ev_send, NULL); + + tester_rsp(BTP_SERVICE_ID_CORE, CORE_REGISTER_SERVICE, BTP_INDEX_NONE, + BTP_STATUS_SUCCESS); +} + +u8_t tester_init_gap(void) +{ +#if MYNEWT_VAL(BLE_SM_SC) + int rc; + + rc = ble_sm_sc_oob_generate_data(&oob_data_local); + if (rc) { + console_printf("Error: generating oob data; reason=%d\n", rc); + return BTP_STATUS_FAILED; + } +#endif + + adv_buf = NET_BUF_SIMPLE(ADV_BUF_LEN); + + tester_init_gap_cb(BTP_STATUS_SUCCESS); + return BTP_STATUS_SUCCESS; +} + +u8_t tester_unregister_gap(void) +{ + return BTP_STATUS_SUCCESS; +} diff --git a/src/libs/mynewt-nimble/apps/bttester/src/gatt.c b/src/libs/mynewt-nimble/apps/bttester/src/gatt.c new file mode 100644 index 00000000..7e7d1d3b --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bttester/src/gatt.c @@ -0,0 +1,2098 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* gatt.c - Bluetooth GATT Server Tester */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "host/ble_gap.h" +#include "host/ble_gatt.h" +#include "console/console.h" +#include "services/gatt/ble_svc_gatt.h" +#include "../../../nimble/host/src/ble_att_priv.h" +#include "../../../nimble/host/src/ble_gatt_priv.h" + +#include "bttester.h" + +#define CONTROLLER_INDEX 0 +#define MAX_BUFFER_SIZE 2048 + +/* 0000xxxx-8c26-476f-89a7-a108033a69c7 */ +#define PTS_UUID_DECLARE(uuid16) \ + ((const ble_uuid_t *) (&(ble_uuid128_t) BLE_UUID128_INIT( \ + 0xc7, 0x69, 0x3a, 0x03, 0x08, 0xa1, 0xa7, 0x89, \ + 0x6f, 0x47, 0x26, 0x8c, uuid16, uuid16 >> 8, 0x00, 0x00 \ + ))) + +/* 0000xxxx-8c26-476f-89a7-a108033a69c6 */ +#define PTS_UUID_DECLARE_ALT(uuid16) \ + ((const ble_uuid_t *) (&(ble_uuid128_t) BLE_UUID128_INIT( \ + 0xc6, 0x69, 0x3a, 0x03, 0x08, 0xa1, 0xa7, 0x89, \ + 0x6f, 0x47, 0x26, 0x8c, uuid16, uuid16 >> 8, 0x00, 0x00 \ + ))) + +#define PTS_SVC 0x0001 +#define PTS_CHR_READ 0x0002 +#define PTS_CHR_WRITE 0x0003 +#define PTS_CHR_RELIABLE_WRITE 0x0004 +#define PTS_CHR_WRITE_NO_RSP 0x0005 +#define PTS_CHR_READ_WRITE 0x0006 +#define PTS_CHR_READ_WRITE_ENC 0x0007 +#define PTS_CHR_READ_WRITE_AUTHEN 0x0008 +#define PTS_DSC_READ 0x0009 +#define PTS_DSC_WRITE 0x000a +#define PTS_DSC_READ_WRITE 0x000b +#define PTS_CHR_NOTIFY 0x0025 +#define PTS_LONG_CHR_READ_WRITE 0x0015 +#define PTS_LONG_CHR_READ_WRITE_ALT 0x0016 +#define PTS_LONG_DSC_READ_WRITE 0x001b +#define PTS_INC_SVC 0x001e +#define PTS_CHR_READ_WRITE_ALT 0x001f + +static uint8_t gatt_svr_pts_static_long_val[300]; +static uint8_t gatt_svr_pts_static_val[30]; +static uint8_t gatt_svr_pts_static_short_val; +static u8_t notify_state; +static u8_t indicate_state; +static uint16_t myconn_handle; +static struct os_callout notify_tx_timer; +uint16_t notify_handle; +uint8_t notify_value = 90; + +struct find_attr_data { + ble_uuid_any_t *uuid; + int attr_type; + void *ptr; + uint16_t handle; +}; + +static int +gatt_svr_read_write_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_read_write_auth_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_read_write_enc_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_dsc_read_write_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_write_no_rsp_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_rel_write_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_read_write_long_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_dsc_read_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static int +gatt_svr_dsc_read_write_long_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); + +static const struct ble_gatt_svc_def gatt_svr_inc_svcs[] = { + { + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = PTS_UUID_DECLARE(PTS_INC_SVC), + .characteristics = (struct ble_gatt_chr_def[]) {{ + .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE_ALT), + .access_cb = gatt_svr_read_write_test, + .flags = BLE_GATT_CHR_F_WRITE | + BLE_GATT_CHR_F_READ, + }, { + 0, + } }, + }, + + { + 0, /* No more services. */ + }, +}; + +static const struct ble_gatt_svc_def *inc_svcs[] = { + &gatt_svr_inc_svcs[0], + NULL, +}; + +static const struct ble_gatt_svc_def gatt_svr_svcs[] = { + { + /*** Service: PTS test. */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = PTS_UUID_DECLARE(PTS_SVC), + .includes = inc_svcs, + .characteristics = (struct ble_gatt_chr_def[]) { { + .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE), + .access_cb = gatt_svr_read_write_test, + .flags = BLE_GATT_CHR_F_READ | + BLE_GATT_CHR_F_WRITE, + .descriptors = (struct ble_gatt_dsc_def[]) { { + .uuid = PTS_UUID_DECLARE(PTS_DSC_READ_WRITE), + .access_cb = gatt_svr_dsc_read_write_test, + .att_flags = BLE_ATT_F_READ | + BLE_ATT_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_READ_WRITE), + .access_cb = gatt_svr_dsc_read_write_long_test, + .att_flags = BLE_ATT_F_READ | + BLE_ATT_F_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_DSC_READ), + .access_cb = gatt_svr_dsc_read_test, + .att_flags = BLE_ATT_F_READ, + }, { + 0, /* No more descriptors in this characteristic */ + } } + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_WRITE_NO_RSP), + .access_cb = gatt_svr_write_no_rsp_test, + .flags = BLE_GATT_CHR_F_READ | + BLE_GATT_CHR_F_WRITE | + BLE_GATT_CHR_F_WRITE_NO_RSP, + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE_AUTHEN), + .access_cb = gatt_svr_read_write_auth_test, + .flags = BLE_GATT_CHR_F_READ_AUTHEN | + BLE_GATT_CHR_F_READ | + BLE_GATT_CHR_F_WRITE_AUTHEN | + BLE_GATT_CHR_F_WRITE | + BLE_GATT_CHR_F_WRITE_AUTHEN, + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_RELIABLE_WRITE), + .access_cb = gatt_svr_rel_write_test, + .flags = BLE_GATT_CHR_F_WRITE | + BLE_GATT_CHR_F_RELIABLE_WRITE, + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE_ENC), + .access_cb = gatt_svr_read_write_enc_test, + .flags = BLE_GATT_CHR_F_READ_ENC | + BLE_GATT_CHR_F_READ | + BLE_GATT_CHR_F_WRITE | + BLE_GATT_CHR_F_WRITE_ENC, + .min_key_size = 16, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE), + .access_cb = gatt_svr_read_write_long_test, + .flags = BLE_GATT_CHR_F_WRITE | + BLE_GATT_CHR_F_READ, + }, { + .uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE_ALT), + .access_cb = gatt_svr_read_write_long_test, + .flags = BLE_GATT_CHR_F_WRITE | + BLE_GATT_CHR_F_READ, + }, { + .uuid = PTS_UUID_DECLARE(PTS_CHR_NOTIFY), + .access_cb = gatt_svr_read_write_test, + .val_handle = ¬ify_handle, + .flags = BLE_GATT_CHR_F_NOTIFY | + BLE_GATT_CHR_F_INDICATE, + }, { + 0, /* No more characteristics in this service. */ + } }, + }, + + { + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = PTS_UUID_DECLARE_ALT(PTS_SVC), + .characteristics = (struct ble_gatt_chr_def[]) { { + .uuid = PTS_UUID_DECLARE_ALT(PTS_CHR_READ_WRITE), + .access_cb = gatt_svr_read_write_test, + .flags = BLE_GATT_CHR_F_WRITE | + BLE_GATT_CHR_F_READ, + }, { + 0, /* No more characteristics in this service */ + } }, + }, + + { + 0, /* No more services. */ + }, +}; + +static void attr_value_changed_ev(u16_t handle, struct os_mbuf *data) +{ + struct gatt_attr_value_changed_ev *ev; + struct os_mbuf *buf = os_msys_get(0, 0); + + SYS_LOG_DBG(""); + + net_buf_simple_init(buf, 0); + ev = net_buf_simple_add(buf, sizeof(*ev)); + + ev->handle = sys_cpu_to_le16(handle); + ev->data_length = sys_cpu_to_le16(os_mbuf_len(data)); + os_mbuf_appendfrom(buf, data, 0, os_mbuf_len(data)); + + tester_send_buf(BTP_SERVICE_ID_GATT, GATT_EV_ATTR_VALUE_CHANGED, + CONTROLLER_INDEX, buf); +} + +static int +gatt_svr_chr_write(uint16_t conn_handle, uint16_t attr_handle, + struct os_mbuf *om, uint16_t min_len, uint16_t max_len, + void *dst, uint16_t *len) +{ + uint16_t om_len; + int rc; + + om_len = OS_MBUF_PKTLEN(om); + if (om_len < min_len || om_len > max_len) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + rc = ble_hs_mbuf_to_flat(om, dst, max_len, len); + if (rc != 0) { + return BLE_ATT_ERR_UNLIKELY; + } + + attr_value_changed_ev(attr_handle, om); + + return 0; +} + +static uint16_t +extract_uuid16_from_pts_uuid128(const ble_uuid_t *uuid) +{ + const uint8_t *u8ptr; + uint16_t uuid16; + + u8ptr = BLE_UUID128(uuid)->value; + uuid16 = u8ptr[12]; + uuid16 |= (uint16_t)u8ptr[13] << 8; + return uuid16; +} + +static int +gatt_svr_read_write_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_CHR_READ_WRITE: + case PTS_CHR_READ_WRITE_ALT: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(conn_handle, attr_handle, + ctxt->om, 0, + sizeof gatt_svr_pts_static_short_val, + &gatt_svr_pts_static_short_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_short_val, + sizeof gatt_svr_pts_static_short_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +static int +gatt_svr_read_write_long_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_LONG_CHR_READ_WRITE: + case PTS_LONG_CHR_READ_WRITE_ALT: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(conn_handle, attr_handle, + ctxt->om, 0, + sizeof gatt_svr_pts_static_long_val, + &gatt_svr_pts_static_long_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val, + sizeof gatt_svr_pts_static_long_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +static int +gatt_svr_read_write_auth_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_CHR_READ_WRITE_AUTHEN: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(conn_handle, attr_handle, + ctxt->om, 0, + sizeof gatt_svr_pts_static_val, + &gatt_svr_pts_static_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val, + sizeof gatt_svr_pts_static_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +static int +gatt_svr_read_write_enc_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_CHR_READ_WRITE_ENC: + if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val, + sizeof gatt_svr_pts_static_val); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(conn_handle, attr_handle, + ctxt->om, 0, + sizeof gatt_svr_pts_static_val, + &gatt_svr_pts_static_val, NULL); + return rc; + } + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +static int +gatt_svr_dsc_read_write_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_DSC_READ_WRITE: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC) { + rc = gatt_svr_chr_write(conn_handle, attr_handle, + ctxt->om, 0, + sizeof gatt_svr_pts_static_short_val, + &gatt_svr_pts_static_short_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_short_val, + sizeof gatt_svr_pts_static_short_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +static int +gatt_svr_dsc_read_write_long_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_LONG_DSC_READ_WRITE: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC) { + rc = gatt_svr_chr_write(conn_handle, attr_handle, + ctxt->om, 0, + sizeof gatt_svr_pts_static_long_val, + &gatt_svr_pts_static_long_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val, + sizeof gatt_svr_pts_static_long_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +static int +gatt_svr_dsc_read_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_DSC_READ: + if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val, + sizeof gatt_svr_pts_static_long_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +static int +gatt_svr_write_no_rsp_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_CHR_WRITE_NO_RSP: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(conn_handle, attr_handle, + ctxt->om, 0, + sizeof gatt_svr_pts_static_short_val, + &gatt_svr_pts_static_short_val, NULL); + return rc; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_short_val, + sizeof gatt_svr_pts_static_short_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +static int +gatt_svr_rel_write_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16; + int rc; + + uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid); + assert(uuid16 != 0); + + switch (uuid16) { + case PTS_CHR_RELIABLE_WRITE: + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = gatt_svr_chr_write(conn_handle, attr_handle, + ctxt->om, 0, + sizeof gatt_svr_pts_static_val, + &gatt_svr_pts_static_val, NULL); + return rc; + } + + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +static void start_server(u8_t *data, u16_t len) +{ + struct gatt_start_server_rp rp; + + SYS_LOG_DBG(""); + + ble_gatts_show_local(); + + ble_svc_gatt_changed(0x0001, 0xffff); + + rp.db_attr_off = 0; + rp.db_attr_cnt = 0; + + tester_send(BTP_SERVICE_ID_GATT, GATT_START_SERVER, CONTROLLER_INDEX, + (u8_t *) &rp, sizeof(rp)); +} + +/* Convert UUID from BTP command to bt_uuid */ +static u8_t btp2bt_uuid(const u8_t *uuid, u8_t len, + ble_uuid_any_t *bt_uuid) +{ + u16_t le16; + + switch (len) { + case 0x02: /* UUID 16 */ + bt_uuid->u.type = BLE_UUID_TYPE_16; + memcpy(&le16, uuid, sizeof(le16)); + BLE_UUID16(bt_uuid)->value = sys_le16_to_cpu(le16); + break; + case 0x10: /* UUID 128*/ + bt_uuid->u.type = BLE_UUID_TYPE_128; + memcpy(BLE_UUID128(bt_uuid)->value, uuid, 16); + break; + default: + return BTP_STATUS_FAILED; + } + return BTP_STATUS_SUCCESS; +} + +/* + * gatt_buf - cache used by a gatt client (to cache data read/discovered) + * and gatt server (to store attribute user_data). + * It is not intended to be used by client and server at the same time. + */ +static struct { + u16_t len; + u8_t buf[MAX_BUFFER_SIZE]; +} gatt_buf; + +static void *gatt_buf_add(const void *data, size_t len) +{ + void *ptr = gatt_buf.buf + gatt_buf.len; + + if ((len + gatt_buf.len) > MAX_BUFFER_SIZE) { + return NULL; + } + + if (data) { + memcpy(ptr, data, len); + } else { + (void)memset(ptr, 0, len); + } + + gatt_buf.len += len; + + SYS_LOG_DBG("%d/%d used", gatt_buf.len, MAX_BUFFER_SIZE); + + return ptr; +} + +static void *gatt_buf_reserve(size_t len) +{ + return gatt_buf_add(NULL, len); +} + +static void gatt_buf_clear(void) +{ + (void)memset(&gatt_buf, 0, sizeof(gatt_buf)); +} + +static void discover_destroy(void) +{ + gatt_buf_clear(); +} + +static void read_destroy() +{ + gatt_buf_clear(); +} + +static int read_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) +{ + struct gatt_read_rp *rp = (void *) gatt_buf.buf; + u8_t btp_opcode = (uint8_t) (int) arg; + + SYS_LOG_DBG("status=%d", error->status); + + if (error->status != 0 && error->status != BLE_HS_EDONE) { + rp->att_response = (uint8_t) BLE_HS_ATT_ERR(error->status); + tester_send(BTP_SERVICE_ID_GATT, btp_opcode, + CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len); + read_destroy(); + return 0; + } + + if (!gatt_buf_add(attr->om->om_data, attr->om->om_len)) { + tester_rsp(BTP_SERVICE_ID_GATT, btp_opcode, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + read_destroy(); + return 0; + } + + rp->data_length += attr->om->om_len; + tester_send(BTP_SERVICE_ID_GATT, btp_opcode, + CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len); + read_destroy(); + + return 0; +} + +static void read(u8_t *data, u16_t len) +{ + const struct gatt_read_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + /* Clear buffer */ + read_destroy(); + + if (!gatt_buf_reserve(sizeof(struct gatt_read_rp))) { + goto fail; + } + + if (ble_gattc_read(conn.conn_handle, sys_le16_to_cpu(cmd->handle), + read_cb, (void *)GATT_READ)) { + read_destroy(); + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static int read_long_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) +{ + struct gatt_read_rp *rp = (void *) gatt_buf.buf; + u8_t btp_opcode = (uint8_t) (int) arg; + + SYS_LOG_DBG("status=%d", error->status); + + if (error->status != 0 && error->status != BLE_HS_EDONE) { + rp->att_response = (uint8_t) BLE_HS_ATT_ERR(error->status); + tester_send(BTP_SERVICE_ID_GATT, btp_opcode, + CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len); + read_destroy(); + return 0; + } + + if (error->status == BLE_HS_EDONE) { + tester_send(BTP_SERVICE_ID_GATT, btp_opcode, + CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len); + read_destroy(); + return 0; + } + + if (gatt_buf_add(attr->om->om_data, attr->om->om_len) == NULL) { + tester_rsp(BTP_SERVICE_ID_GATT, btp_opcode, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + read_destroy(); + return BLE_HS_ENOMEM; + } + + rp->data_length += attr->om->om_len; + + return 0; +} + +static void read_long(u8_t *data, u16_t len) +{ + const struct gatt_read_long_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + /* Clear buffer */ + read_destroy(); + + if (!gatt_buf_reserve(sizeof(struct gatt_read_rp))) { + goto fail; + } + + if (ble_gattc_read_long(conn.conn_handle, + sys_le16_to_cpu(cmd->handle), + sys_le16_to_cpu(cmd->offset), + read_long_cb, (void *)GATT_READ_LONG)) { + read_destroy(); + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ_LONG, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void read_multiple(u8_t *data, u16_t len) +{ + const struct gatt_read_multiple_cmd *cmd = (void *) data; + u16_t handles[cmd->handles_count]; + struct ble_gap_conn_desc conn; + int rc, i; + + SYS_LOG_DBG(""); + + for (i = 0; i < ARRAY_SIZE(handles); i++) { + handles[i] = sys_le16_to_cpu(cmd->handles[i]); + } + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + /* Clear buffer */ + read_destroy(); + + if (!gatt_buf_reserve(sizeof(struct gatt_read_rp))) { + goto fail; + } + + if (ble_gattc_read_mult(conn.conn_handle, handles, + cmd->handles_count, read_cb, + (void *)GATT_READ_MULTIPLE)) { + read_destroy(); + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ_MULTIPLE, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void write_without_rsp(u8_t *data, u16_t len, u8_t op, bool sign) +{ + const struct gatt_write_without_rsp_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + u8_t status = BTP_STATUS_SUCCESS; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + if (ble_gattc_write_no_rsp_flat(conn.conn_handle, + sys_le16_to_cpu(cmd->handle), cmd->data, + sys_le16_to_cpu(cmd->data_length))) { + status = BTP_STATUS_FAILED; + } + +rsp: + tester_rsp(BTP_SERVICE_ID_GATT, op, CONTROLLER_INDEX, status); +} + +static int write_rsp(uint16_t conn_handle, const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) +{ + uint8_t err = (uint8_t) error->status; + u8_t btp_opcode = (uint8_t) (int) arg; + + SYS_LOG_DBG(""); + + tester_send(BTP_SERVICE_ID_GATT, btp_opcode, + CONTROLLER_INDEX, &err, sizeof(err)); + return 0; +} + +static void write(u8_t *data, u16_t len) +{ + const struct gatt_write_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + if (ble_gattc_write_flat(conn.conn_handle, sys_le16_to_cpu(cmd->handle), + cmd->data, sys_le16_to_cpu(cmd->data_length), + write_rsp, (void *) GATT_WRITE)) { + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_WRITE, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void write_long(u8_t *data, u16_t len) +{ + const struct gatt_write_long_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + struct os_mbuf *om = NULL; + int rc = 0; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + om = ble_hs_mbuf_from_flat(cmd->data, sys_le16_to_cpu(cmd->data_length)); + if (!om) { + SYS_LOG_ERR("Insufficient resources"); + goto fail; + } + + rc = ble_gattc_write_long(conn.conn_handle, + sys_le16_to_cpu(cmd->handle), + sys_le16_to_cpu(cmd->offset), + om, write_rsp, + (void *) GATT_WRITE_LONG); + if (!rc) { + return; + } + +fail: + SYS_LOG_ERR("Failed to send Write Long request, rc=%d", rc); + os_mbuf_free_chain(om); + tester_rsp(BTP_SERVICE_ID_GATT, GATT_WRITE_LONG, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static int reliable_write_rsp(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attrs, + uint8_t num_attrs, + void *arg) +{ + uint8_t err = (uint8_t) error->status; + + SYS_LOG_DBG("Reliable write status %d", err); + + tester_send(BTP_SERVICE_ID_GATT, GATT_RELIABLE_WRITE, + CONTROLLER_INDEX, &err, sizeof(err)); + return 0; +} + +static void reliable_write(u8_t *data, u16_t len) +{ + const struct gatt_reliable_write_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + struct ble_gatt_attr attr; + struct os_mbuf *om = NULL; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + om = ble_hs_mbuf_from_flat(cmd->data, sys_le16_to_cpu(cmd->data_length)); + /* This is required, because Nimble checks if + * the data is longer than offset + */ + if (os_mbuf_extend(om, sys_le16_to_cpu(cmd->offset) + 1) == NULL) { + goto fail; + } + + attr.handle = sys_le16_to_cpu(cmd->handle); + attr.offset = sys_le16_to_cpu(cmd->offset); + attr.om = om; + + if (ble_gattc_write_reliable(conn.conn_handle, &attr, 1, + reliable_write_rsp, NULL)) { + goto fail; + } + + return; + +fail: + os_mbuf_free_chain(om); + + tester_rsp(BTP_SERVICE_ID_GATT, GATT_WRITE_LONG, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static struct bt_gatt_subscribe_params { + u16_t ccc_handle; + u16_t value; + u16_t value_handle; +} subscribe_params; + +static void read_uuid(u8_t *data, u16_t len) +{ + const struct gatt_read_uuid_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + ble_uuid_any_t uuid; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + if (btp2bt_uuid(cmd->uuid, cmd->uuid_length, &uuid)) { + goto fail; + } + + /* Clear buffer */ + read_destroy(); + + if (!gatt_buf_reserve(sizeof(struct gatt_read_rp))) { + goto fail; + } + + if (ble_gattc_read_by_uuid(conn.conn_handle, + sys_le16_to_cpu(cmd->start_handle), + sys_le16_to_cpu(cmd->end_handle), &uuid.u, + read_long_cb, (void *)GATT_READ_UUID)) { + read_destroy(); + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static int disc_prim_uuid_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_svc *gatt_svc, void *arg) +{ + struct gatt_disc_prim_uuid_rp *rp = (void *) gatt_buf.buf; + struct gatt_service *service; + const ble_uuid_any_t *uuid; + u8_t uuid_length; + u8_t opcode = (u8_t) (int) arg; + + SYS_LOG_DBG(""); + + if (error->status != 0 && error->status != BLE_HS_EDONE) { + tester_rsp(BTP_SERVICE_ID_GATT, opcode, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + discover_destroy(); + return 0; + } + + if (error->status == BLE_HS_EDONE) { + tester_send(BTP_SERVICE_ID_GATT, opcode, + CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len); + discover_destroy(); + return 0; + } + + uuid = &gatt_svc->uuid; + uuid_length = (uint8_t) (uuid->u.type == BLE_UUID_TYPE_16 ? 2 : 16); + + service = gatt_buf_reserve(sizeof(*service) + uuid_length); + if (!service) { + tester_rsp(BTP_SERVICE_ID_GATT, opcode, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + discover_destroy(); + return BLE_HS_ENOMEM; + } + + service->start_handle = sys_cpu_to_le16(gatt_svc->start_handle); + service->end_handle = sys_cpu_to_le16(gatt_svc->end_handle); + service->uuid_length = uuid_length; + + if (uuid->u.type == BLE_UUID_TYPE_16) { + u16_t u16 = sys_cpu_to_le16(BLE_UUID16(uuid)->value); + memcpy(service->uuid, &u16, uuid_length); + } else { + memcpy(service->uuid, BLE_UUID128(uuid)->value, + uuid_length); + } + + rp->services_count++; + + return 0; +} + +static int disc_all_desc_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + uint16_t chr_val_handle, + const struct ble_gatt_dsc *gatt_dsc, + void *arg) +{ + struct gatt_disc_all_desc_rp *rp = (void *) gatt_buf.buf; + struct gatt_descriptor *dsc; + const ble_uuid_any_t *uuid; + u8_t uuid_length; + + SYS_LOG_DBG(""); + + if (error->status != 0 && error->status != BLE_HS_EDONE) { + tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_DESC, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + discover_destroy(); + return 0; + } + + if (error->status == BLE_HS_EDONE) { + tester_send(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_DESC, + CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len); + discover_destroy(); + return 0; + } + + uuid = &gatt_dsc->uuid; + uuid_length = (uint8_t) (uuid->u.type == BLE_UUID_TYPE_16 ? 2 : 16); + + dsc = gatt_buf_reserve(sizeof(*dsc) + uuid_length); + if (!dsc) { + tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_DESC, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + discover_destroy(); + return BLE_HS_ENOMEM; + } + + dsc->descriptor_handle = sys_cpu_to_le16(gatt_dsc->handle); + dsc->uuid_length = uuid_length; + + if (uuid->u.type == BLE_UUID_TYPE_16) { + u16_t u16 = sys_cpu_to_le16(BLE_UUID16(uuid)->value); + memcpy(dsc->uuid, &u16, uuid_length); + } else { + memcpy(dsc->uuid, BLE_UUID128(uuid)->value, uuid_length); + } + + rp->descriptors_count++; + + return 0; +} + +static void disc_all_prim_svcs(u8_t *data, u16_t len) +{ + struct ble_gap_conn_desc conn; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + if (!gatt_buf_reserve(sizeof(struct gatt_disc_all_prim_svcs_rp))) { + goto fail; + } + + if (ble_gattc_disc_all_svcs(conn.conn_handle, disc_prim_uuid_cb, + (void *) GATT_DISC_ALL_PRIM_SVCS)) { + discover_destroy(); + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_PRIM_SVCS, + CONTROLLER_INDEX, BTP_STATUS_FAILED); +} + +static void disc_all_desc(u8_t *data, u16_t len) +{ + const struct gatt_disc_all_desc_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + uint16_t start_handle, end_handle; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + if (!gatt_buf_reserve(sizeof(struct gatt_disc_all_desc_rp))) { + goto fail; + } + + start_handle = sys_le16_to_cpu(cmd->start_handle) - 1; + end_handle = sys_le16_to_cpu(cmd->end_handle); + + rc = ble_gattc_disc_all_dscs(conn.conn_handle, start_handle, end_handle, + disc_all_desc_cb, NULL); + + SYS_LOG_DBG("rc=%d", rc); + + if (rc) { + discover_destroy(); + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_DESC, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static int find_included_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_svc *gatt_svc, void *arg) +{ + struct gatt_find_included_rp *rp = (void *) gatt_buf.buf; + struct gatt_included *included; + const ble_uuid_any_t *uuid; + int service_handle = (int) arg; + u8_t uuid_length; + + SYS_LOG_DBG(""); + + if (error->status != 0 && error->status != BLE_HS_EDONE) { + tester_rsp(BTP_SERVICE_ID_GATT, GATT_FIND_INCLUDED, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + discover_destroy(); + return 0; + } + + if (error->status == BLE_HS_EDONE) { + tester_send(BTP_SERVICE_ID_GATT, GATT_FIND_INCLUDED, + CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len); + discover_destroy(); + return 0; + } + + uuid = &gatt_svc->uuid; + uuid_length = (uint8_t) (uuid->u.type == BLE_UUID_TYPE_16 ? 2 : 16); + + + included = gatt_buf_reserve(sizeof(*included) + uuid_length); + if (!included) { + tester_rsp(BTP_SERVICE_ID_GATT, GATT_FIND_INCLUDED, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + discover_destroy(); + return BLE_HS_ENOMEM; + } + + /* FIXME */ + included->included_handle = sys_cpu_to_le16(service_handle + 1 + + rp->services_count); + included->service.start_handle = sys_cpu_to_le16(gatt_svc->start_handle); + included->service.end_handle = sys_cpu_to_le16(gatt_svc->end_handle); + included->service.uuid_length = uuid_length; + + if (uuid->u.type == BLE_UUID_TYPE_16) { + u16_t u16 = sys_cpu_to_le16(BLE_UUID16(uuid)->value); + memcpy(included->service.uuid, &u16, uuid_length); + } else { + memcpy(included->service.uuid, BLE_UUID128(uuid)->value, + uuid_length); + } + + rp->services_count++; + + return 0; +} + +static int disc_chrc_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_chr *gatt_chr, void *arg) +{ + struct gatt_disc_chrc_rp *rp = (void *) gatt_buf.buf; + struct gatt_characteristic *chrc; + const ble_uuid_any_t *uuid; + u8_t btp_opcode = (uint8_t) (int) arg; + u8_t uuid_length; + + SYS_LOG_DBG(""); + + if (error->status != 0 && error->status != BLE_HS_EDONE) { + tester_rsp(BTP_SERVICE_ID_GATT, btp_opcode, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + discover_destroy(); + return 0; + } + + if (error->status == BLE_HS_EDONE) { + tester_send(BTP_SERVICE_ID_GATT, btp_opcode, + CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len); + discover_destroy(); + return 0; + } + + uuid = &gatt_chr->uuid; + uuid_length = (uint8_t) (uuid->u.type == BLE_UUID_TYPE_16 ? 2 : 16); + + chrc = gatt_buf_reserve(sizeof(*chrc) + uuid_length); + if (!chrc) { + tester_rsp(BTP_SERVICE_ID_GATT, btp_opcode, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + discover_destroy(); + return BLE_HS_ENOMEM; + } + + chrc->characteristic_handle = sys_cpu_to_le16(gatt_chr->def_handle); + chrc->properties = gatt_chr->properties; + chrc->value_handle = sys_cpu_to_le16(gatt_chr->val_handle); + chrc->uuid_length = uuid_length; + + if (uuid->u.type == BLE_UUID_TYPE_16) { + u16_t u16 = sys_cpu_to_le16(BLE_UUID16(uuid)->value); + memcpy(chrc->uuid, &u16, uuid_length); + } else { + memcpy(chrc->uuid, BLE_UUID128(uuid)->value, + uuid_length); + } + + rp->characteristics_count++; + + return 0; +} + +static void disc_chrc_uuid(u8_t *data, u16_t len) +{ + const struct gatt_disc_chrc_uuid_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + uint16_t start_handle, end_handle; + ble_uuid_any_t uuid; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + if (btp2bt_uuid(cmd->uuid, cmd->uuid_length, &uuid)) { + goto fail; + } + + if (!gatt_buf_reserve(sizeof(struct gatt_disc_chrc_rp))) { + goto fail; + } + + start_handle = sys_le16_to_cpu(cmd->start_handle); + end_handle = sys_le16_to_cpu(cmd->end_handle); + + if (ble_gattc_disc_chrs_by_uuid(conn.conn_handle, start_handle, + end_handle, &uuid.u, disc_chrc_cb, + (void *)GATT_DISC_CHRC_UUID)) { + discover_destroy(); + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_CHRC_UUID, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void disc_prim_uuid(u8_t *data, u16_t len) +{ + const struct gatt_disc_prim_uuid_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + ble_uuid_any_t uuid; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + if (btp2bt_uuid(cmd->uuid, cmd->uuid_length, &uuid)) { + goto fail; + } + + if (!gatt_buf_reserve(sizeof(struct gatt_disc_prim_uuid_rp))) { + goto fail; + } + + if (ble_gattc_disc_svc_by_uuid(conn.conn_handle, + &uuid.u, disc_prim_uuid_cb, + (void *) GATT_DISC_PRIM_UUID)) { + discover_destroy(); + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_PRIM_UUID, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void disc_all_chrc(u8_t *data, u16_t len) +{ + const struct gatt_disc_all_chrc_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + uint16_t start_handle, end_handle; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + SYS_LOG_DBG("Conn find failed"); + goto fail; + } + + if (!gatt_buf_reserve(sizeof(struct gatt_disc_chrc_rp))) { + SYS_LOG_DBG("Buf reserve failed"); + goto fail; + } + + start_handle = sys_le16_to_cpu(cmd->start_handle); + end_handle = sys_le16_to_cpu(cmd->end_handle); + + rc = ble_gattc_disc_all_chrs(conn.conn_handle, start_handle, end_handle, + disc_chrc_cb, (void *)GATT_DISC_ALL_CHRC); + if (rc) { + discover_destroy(); + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_CHRC, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void find_included(u8_t *data, u16_t len) +{ + const struct gatt_find_included_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + uint16_t start_handle, end_handle; + int service_handle_arg; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + if (!gatt_buf_reserve(sizeof(struct gatt_find_included_rp))) { + goto fail; + } + + start_handle = sys_le16_to_cpu(cmd->start_handle); + end_handle = sys_le16_to_cpu(cmd->end_handle); + service_handle_arg = start_handle; + + if (ble_gattc_find_inc_svcs(conn.conn_handle, start_handle, end_handle, + find_included_cb, + (void *)service_handle_arg)) { + discover_destroy(); + goto fail; + } + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_FIND_INCLUDED, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static int exchange_func(uint16_t conn_handle, + const struct ble_gatt_error *error, + uint16_t mtu, void *arg) +{ + SYS_LOG_DBG(""); + + if (error->status) { + tester_rsp(BTP_SERVICE_ID_GATT, GATT_EXCHANGE_MTU, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + + return 0; +} + + tester_rsp(BTP_SERVICE_ID_GATT, GATT_EXCHANGE_MTU, CONTROLLER_INDEX, + BTP_STATUS_SUCCESS); + + return 0; +} + +static void exchange_mtu(u8_t *data, u16_t len) +{ + struct ble_gap_conn_desc conn; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + goto fail; + } + + if (ble_gattc_exchange_mtu(conn.conn_handle, exchange_func, NULL)) { + goto fail; + } + + return; +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_EXCHANGE_MTU, + CONTROLLER_INDEX, BTP_STATUS_FAILED); +} + +static int enable_subscription(u16_t conn_handle, u16_t ccc_handle, + u16_t value) +{ + u8_t op; + + SYS_LOG_DBG(""); + + op = (uint8_t) (value == 0x0001 ? GATT_CFG_NOTIFY : GATT_CFG_INDICATE); + + if (ble_gattc_write_flat(conn_handle, ccc_handle, + &value, sizeof(value), NULL, NULL)) { + return -EINVAL; + } + + subscribe_params.ccc_handle = value; + + tester_rsp(BTP_SERVICE_ID_GATT, op, CONTROLLER_INDEX, + BTP_STATUS_SUCCESS); + return 0; +} + +static int disable_subscription(u16_t conn_handle, u16_t ccc_handle) +{ + u16_t value = 0x00; + + SYS_LOG_DBG(""); + + /* Fail if CCC handle doesn't match */ + if (ccc_handle != subscribe_params.ccc_handle) { + SYS_LOG_ERR("CCC handle doesn't match"); + return -EINVAL; + } + + if (ble_gattc_write_no_rsp_flat(conn_handle, ccc_handle, + &value, sizeof(value))) { + return -EINVAL; + } + + subscribe_params.ccc_handle = 0; + return 0; +} + +static void config_subscription(u8_t *data, u16_t len, u8_t op) +{ + const struct gatt_cfg_notify_cmd *cmd = (void *) data; + struct ble_gap_conn_desc conn; + u16_t ccc_handle = sys_le16_to_cpu(cmd->ccc_handle); + u8_t status; + int rc; + + SYS_LOG_DBG(""); + + rc = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + if (rc) { + tester_rsp(BTP_SERVICE_ID_GATT, op, CONTROLLER_INDEX, + BTP_STATUS_FAILED); + return; + } + + if (cmd->enable) { + u16_t value; + + if (op == GATT_CFG_NOTIFY) { + value = 0x0001; + } else { + value = 0x0002; + } + + /* on success response will be sent from callback */ + if (enable_subscription(conn.conn_handle, + ccc_handle, value) == 0) { + return; + } + + status = BTP_STATUS_FAILED; + } else { + if (disable_subscription(conn.conn_handle, ccc_handle) < 0) { + status = BTP_STATUS_FAILED; + } else { + status = BTP_STATUS_SUCCESS; + } + } + + SYS_LOG_DBG("Config subscription (op %u) status %u", op, status); + + tester_rsp(BTP_SERVICE_ID_GATT, op, CONTROLLER_INDEX, status); +} + +#define BTP_PERM_F_READ 0x01 +#define BTP_PERM_F_WRITE 0x02 +#define BTP_PERM_F_READ_ENC 0x04 +#define BTP_PERM_F_WRITE_ENC 0x08 +#define BTP_PERM_F_READ_AUTHEN 0x10 +#define BTP_PERM_F_WRITE_AUTHEN 0x20 +#define BTP_PERM_F_READ_AUTHOR 0x40 +#define BTP_PERM_F_WRITE_AUTHOR 0x80 + +static int flags_hs2btp_map[] = { + BTP_PERM_F_READ, + BTP_PERM_F_WRITE, + BTP_PERM_F_READ_ENC, + BTP_PERM_F_READ_AUTHEN, + BTP_PERM_F_READ_AUTHOR, + BTP_PERM_F_WRITE_ENC, + BTP_PERM_F_WRITE_AUTHEN, + BTP_PERM_F_WRITE_AUTHOR, +}; + +static u8_t flags_hs2btp(u8_t flags) +{ + int i; + u8_t ret = 0; + + for (i = 0; i < 8; ++i) { + if (flags & BIT(i)) { + ret |= flags_hs2btp_map[i]; + } + } + + return ret; +} + +static void get_attrs(u8_t *data, u16_t len) +{ + const struct gatt_get_attributes_cmd *cmd = (void *) data; + struct gatt_get_attributes_rp *rp; + struct gatt_attr *gatt_attr; + struct os_mbuf *buf = os_msys_get(0, 0); + u16_t start_handle, end_handle; + struct ble_att_svr_entry *entry = NULL; + ble_uuid_any_t uuid; + ble_uuid_t *uuid_ptr = NULL; + u8_t count = 0; + char str[BLE_UUID_STR_LEN]; + + SYS_LOG_DBG(""); + + memset(str, 0, sizeof(str)); + memset(&uuid, 0, sizeof(uuid)); + start_handle = sys_le16_to_cpu(cmd->start_handle); + end_handle = sys_le16_to_cpu(cmd->end_handle); + + if (cmd->type_length) { + if (btp2bt_uuid(cmd->type, cmd->type_length, &uuid)) { + goto fail; + } + + ble_uuid_to_str(&uuid.u, str); + SYS_LOG_DBG("start 0x%04x end 0x%04x, uuid %s", start_handle, + end_handle, str); + + uuid_ptr = &uuid.u; + } else { + SYS_LOG_DBG("start 0x%04x end 0x%04x", start_handle, end_handle); + } + + net_buf_simple_init(buf, 0); + rp = net_buf_simple_add(buf, sizeof(*rp)); + + entry = ble_att_svr_find_by_uuid(entry, uuid_ptr, end_handle); + while (entry) { + + if (entry->ha_handle_id < start_handle) { + entry = ble_att_svr_find_by_uuid(entry, + uuid_ptr, end_handle); + continue; + } + + gatt_attr = net_buf_simple_add(buf, sizeof(*gatt_attr)); + gatt_attr->handle = sys_cpu_to_le16(entry->ha_handle_id); + gatt_attr->permission = flags_hs2btp(entry->ha_flags); + + if (entry->ha_uuid->type == BLE_UUID_TYPE_16) { + gatt_attr->type_length = 2; + net_buf_simple_add_le16(buf, + BLE_UUID16(entry->ha_uuid)->value); + } else { + gatt_attr->type_length = 16; + net_buf_simple_add_mem(buf, + BLE_UUID128(entry->ha_uuid)->value, + gatt_attr->type_length); + } + + count++; + + entry = ble_att_svr_find_by_uuid(entry, uuid_ptr, end_handle); + } + + rp->attrs_count = count; + + tester_send_buf(BTP_SERVICE_ID_GATT, GATT_GET_ATTRIBUTES, + CONTROLLER_INDEX, buf); + + goto free; +fail: + tester_rsp(BTP_SERVICE_ID_GATT, GATT_GET_ATTRIBUTES, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +free: + os_mbuf_free_chain(buf); +} + +static void get_attr_val(u8_t *data, u16_t len) +{ + const struct gatt_get_attribute_value_cmd *cmd = (void *) data; + struct gatt_get_attribute_value_rp *rp; + struct ble_gap_conn_desc conn; + struct os_mbuf *buf = os_msys_get(0, 0); + u16_t handle = sys_cpu_to_le16(cmd->handle); + uint8_t out_att_err; + int conn_status; + + conn_status = ble_gap_conn_find_by_addr((ble_addr_t *)data, &conn); + + if (conn_status) { + net_buf_simple_init(buf, 0); + rp = net_buf_simple_add(buf, sizeof(*rp)); + + ble_att_svr_read_handle(BLE_HS_CONN_HANDLE_NONE, + handle, 0, buf, + &out_att_err); + + rp->att_response = out_att_err; + rp->value_length = os_mbuf_len(buf) - sizeof(*rp); + + tester_send_buf(BTP_SERVICE_ID_GATT, GATT_GET_ATTRIBUTE_VALUE, + CONTROLLER_INDEX, buf); + + goto free; + } else { + net_buf_simple_init(buf, 0); + rp = net_buf_simple_add(buf, sizeof(*rp)); + + ble_att_svr_read_handle(conn.conn_handle, + handle, 0, buf, + &out_att_err); + + rp->att_response = out_att_err; + rp->value_length = os_mbuf_len(buf) - sizeof(*rp); + + tester_send_buf(BTP_SERVICE_ID_GATT, GATT_GET_ATTRIBUTE_VALUE, + CONTROLLER_INDEX, buf); + + goto free; + } + +free: + os_mbuf_free_chain(buf); +} + +static void change_database(u8_t *data, u16_t len) +{ + const struct gatt_change_database *cmd = (void *) data; + + SYS_LOG_DBG("") + + ble_gatts_show_local(); + + ble_svc_gatt_changed(cmd->start_handle, cmd->end_handle); + + tester_rsp(BTP_SERVICE_ID_GATT, GATT_CHANGE_DATABASE, CONTROLLER_INDEX, + BTP_STATUS_SUCCESS); + + return; +} + +static void supported_commands(u8_t *data, u16_t len) +{ + u8_t cmds[4]; + struct gatt_read_supported_commands_rp *rp = (void *) cmds; + + SYS_LOG_DBG(""); + + memset(cmds, 0, sizeof(cmds)); + + tester_set_bit(cmds, GATT_READ_SUPPORTED_COMMANDS); + tester_set_bit(cmds, GATT_START_SERVER); + tester_set_bit(cmds, GATT_EXCHANGE_MTU); + tester_set_bit(cmds, GATT_DISC_ALL_PRIM_SVCS); + tester_set_bit(cmds, GATT_DISC_PRIM_UUID); + tester_set_bit(cmds, GATT_FIND_INCLUDED); + tester_set_bit(cmds, GATT_DISC_ALL_CHRC); + tester_set_bit(cmds, GATT_DISC_CHRC_UUID); + tester_set_bit(cmds, GATT_DISC_ALL_DESC); + tester_set_bit(cmds, GATT_READ); + tester_set_bit(cmds, GATT_READ_LONG); + tester_set_bit(cmds, GATT_READ_MULTIPLE); + tester_set_bit(cmds, GATT_WRITE_WITHOUT_RSP); +#if 0 + tester_set_bit(cmds, GATT_SIGNED_WRITE_WITHOUT_RSP); +#endif + tester_set_bit(cmds, GATT_WRITE); + tester_set_bit(cmds, GATT_WRITE_LONG); + tester_set_bit(cmds, GATT_CFG_NOTIFY); + tester_set_bit(cmds, GATT_CFG_INDICATE); + tester_set_bit(cmds, GATT_GET_ATTRIBUTES); + tester_set_bit(cmds, GATT_GET_ATTRIBUTE_VALUE); + tester_set_bit(cmds, GATT_CHANGE_DATABASE); + + tester_send(BTP_SERVICE_ID_GATT, GATT_READ_SUPPORTED_COMMANDS, + CONTROLLER_INDEX, (u8_t *) rp, sizeof(cmds)); +} + +enum attr_type { + BLE_GATT_ATTR_SVC = 0, + BLE_GATT_ATTR_CHR, + BLE_GATT_ATTR_DSC, +}; + +void tester_handle_gatt(u8_t opcode, u8_t index, u8_t *data, + u16_t len) +{ + switch (opcode) { + case GATT_READ_SUPPORTED_COMMANDS: + supported_commands(data, len); + return; + case GATT_START_SERVER: + start_server(data, len); + return; + case GATT_EXCHANGE_MTU: + exchange_mtu(data, len); + return; + case GATT_DISC_ALL_PRIM_SVCS: + disc_all_prim_svcs(data, len); + return; + case GATT_DISC_PRIM_UUID: + disc_prim_uuid(data, len); + return; + case GATT_FIND_INCLUDED: + find_included(data, len); + return; + case GATT_DISC_ALL_CHRC: + disc_all_chrc(data, len); + return; + case GATT_DISC_CHRC_UUID: + disc_chrc_uuid(data, len); + return; + case GATT_DISC_ALL_DESC: + disc_all_desc(data, len); + return; + case GATT_CHANGE_DATABASE: + change_database(data, len); + return; + case GATT_READ: + read(data, len); + return; + case GATT_READ_UUID: + read_uuid(data, len); + return; + case GATT_READ_LONG: + read_long(data, len); + return; + case GATT_READ_MULTIPLE: + read_multiple(data, len); + return; + case GATT_WRITE_WITHOUT_RSP: + write_without_rsp(data, len, opcode, false); + return; +#if 0 + case GATT_SIGNED_WRITE_WITHOUT_RSP: + write_without_rsp(data, len, opcode, true); + return; +#endif + case GATT_WRITE: + write(data, len); + return; + case GATT_WRITE_LONG: + write_long(data, len); + return; + case GATT_RELIABLE_WRITE: + reliable_write(data, len); + return; + case GATT_CFG_NOTIFY: + case GATT_CFG_INDICATE: + config_subscription(data, len, opcode); + return; + case GATT_GET_ATTRIBUTES: + get_attrs(data, len); + return; + case GATT_GET_ATTRIBUTE_VALUE: + get_attr_val(data, len); + return; + default: + tester_rsp(BTP_SERVICE_ID_GATT, opcode, index, + BTP_STATUS_UNKNOWN_CMD); + return; + } +} + +int tester_gatt_notify_rx_ev(u16_t conn_handle, u16_t attr_handle, + u8_t indication, struct os_mbuf *om) +{ + struct gatt_notification_ev *ev; + struct ble_gap_conn_desc conn; + struct os_mbuf *buf = os_msys_get(0, 0); + const ble_addr_t *addr; + + SYS_LOG_DBG(""); + + if (!subscribe_params.ccc_handle) { + goto fail; + } + + if (ble_gap_conn_find(conn_handle, &conn)) { + goto fail; + } + + net_buf_simple_init(buf, 0); + ev = net_buf_simple_add(buf, sizeof(*ev)); + + addr = &conn.peer_ota_addr; + + ev->address_type = addr->type; + memcpy(ev->address, addr->val, sizeof(ev->address)); + ev->type = (u8_t) (indication ? 0x02 : 0x01); + ev->handle = sys_cpu_to_le16(attr_handle); + ev->data_length = sys_cpu_to_le16(os_mbuf_len(om)); + os_mbuf_appendfrom(buf, om, 0, os_mbuf_len(om)); + + tester_send_buf(BTP_SERVICE_ID_GATT, GATT_EV_NOTIFICATION, + CONTROLLER_INDEX, buf); + +fail: + os_mbuf_free_chain(buf); + return 0; +} + +void notify_test_stop(void) +{ + os_callout_stop(¬ify_tx_timer); +} + +void notify_test_reset(void) +{ + int rc; + + rc = os_callout_reset(¬ify_tx_timer, OS_TICKS_PER_SEC); + assert(rc == 0); +} + +void notify_test(struct os_event *ev) +{ + static uint8_t ntf[1]; + struct os_mbuf *om; + int rc; + + if (!notify_state && !indicate_state) { + notify_test_stop(); + notify_value = 90; + return; + } + + ntf[0] = notify_value; + + notify_value++; + if (notify_value == 160) { + notify_value = 90; + } + + om = ble_hs_mbuf_from_flat(ntf, sizeof(ntf)); + + if (notify_state) { + rc = ble_gattc_notify_custom(myconn_handle, notify_handle, om); + assert(rc == 0); + } + + if (indicate_state) { + rc = ble_gattc_indicate_custom(myconn_handle, notify_handle, om); + assert(rc == 0); + } +} + +int tester_gatt_subscribe_ev(u16_t conn_handle, u16_t attr_handle, u8_t reason, + u8_t prev_notify, u8_t cur_notify, + u8_t prev_indicate, u8_t cur_indicate) +{ + SYS_LOG_DBG(""); + myconn_handle = conn_handle; + + if (cur_notify == 0 && cur_indicate == 0) { + SYS_LOG_INF("Unsubscribed"); + memset(&subscribe_params, 0, sizeof(subscribe_params)); + return 0; + } + + if (cur_notify) { + SYS_LOG_INF("Subscribed to notifications"); + if (attr_handle == notify_handle) { + notify_state = cur_notify; + } + } + + if (cur_indicate) { + SYS_LOG_INF("Subscribed to indications"); + if (attr_handle == notify_handle) { + indicate_state = cur_indicate; + } + } + + + if (notify_state || indicate_state) { + notify_test_reset(); + } else { + notify_test_stop(); + } + + return 0; +} + +void +gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) +{ + char buf[BLE_UUID_STR_LEN]; + + switch (ctxt->op) { + case BLE_GATT_REGISTER_OP_SVC: + MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n", + ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf), + ctxt->svc.handle); + break; + + case BLE_GATT_REGISTER_OP_CHR: + MODLOG_DFLT(DEBUG, "registering characteristic %s with " + "def_handle=%d val_handle=%d\n", + ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf), + ctxt->chr.def_handle, + ctxt->chr.val_handle); + break; + + case BLE_GATT_REGISTER_OP_DSC: + MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n", + ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf), + ctxt->dsc.handle); + break; + + default: + assert(0); + break; + } +} + +int gatt_svr_init(void) +{ + int rc; + + rc = ble_gatts_count_cfg(gatt_svr_inc_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_add_svcs(gatt_svr_inc_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_count_cfg(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + rc = ble_gatts_add_svcs(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + return 0; +} + +u8_t tester_init_gatt(void) +{ + os_callout_init(¬ify_tx_timer, os_eventq_dflt_get(), + notify_test, NULL); + + return BTP_STATUS_SUCCESS; +} + +u8_t tester_unregister_gatt(void) +{ + return BTP_STATUS_SUCCESS; +} diff --git a/src/libs/mynewt-nimble/apps/bttester/src/glue.c b/src/libs/mynewt-nimble/apps/bttester/src/glue.c new file mode 100644 index 00000000..6cd7643c --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bttester/src/glue.c @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "syscfg/syscfg.h" + +#if !MYNEWT_VAL(BLE_MESH) +#include +#include +#include "os/os.h" +#include "os/os_mbuf.h" +#include "glue.h" + + +#define ASSERT_NOT_CHAIN(om) assert(SLIST_NEXT(om, om_next) == NULL) + +const char *bt_hex(const void *buf, size_t len) +{ + static const char hex[] = "0123456789abcdef"; + static char hexbufs[4][137]; + static u8_t curbuf; + const u8_t *b = buf; + char *str; + int i; + + str = hexbufs[curbuf++]; + curbuf %= ARRAY_SIZE(hexbufs); + + len = min(len, (sizeof(hexbufs[0]) - 1) / 2); + + for (i = 0; i < len; i++) { + str[i * 2] = hex[b[i] >> 4]; + str[i * 2 + 1] = hex[b[i] & 0xf]; + } + + str[i * 2] = '\0'; + + return str; +} + +struct os_mbuf * NET_BUF_SIMPLE(uint16_t size) +{ + struct os_mbuf *buf; + + buf = os_msys_get(size, 0); + assert(buf); + + return buf; +} + +/* This is by purpose */ +void net_buf_simple_init(struct os_mbuf *buf, + size_t reserve_head) +{ + /* This is called in Zephyr after init. + * Note in Mynewt case we don't care abour reserved head*/ + buf->om_data = &buf->om_databuf[buf->om_pkthdr_len] + reserve_head; + buf->om_len = 0; +} + +void +net_buf_simple_add_le16(struct os_mbuf *om, uint16_t val) +{ + val = htole16(val); + os_mbuf_append(om, &val, sizeof(val)); + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_simple_add_be16(struct os_mbuf *om, uint16_t val) +{ + val = htobe16(val); + os_mbuf_append(om, &val, sizeof(val)); + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_simple_add_be32(struct os_mbuf *om, uint32_t val) +{ + val = htobe32(val); + os_mbuf_append(om, &val, sizeof(val)); + ASSERT_NOT_CHAIN(om); +} + +void +net_buf_simple_add_u8(struct os_mbuf *om, uint8_t val) +{ + os_mbuf_append(om, &val, 1); + ASSERT_NOT_CHAIN(om); +} + +void* +net_buf_simple_add(struct os_mbuf *om, uint8_t len) +{ + void * tmp; + + tmp = os_mbuf_extend(om, len); + ASSERT_NOT_CHAIN(om); + + return tmp; +} + +uint8_t * +net_buf_simple_push(struct os_mbuf *om, uint8_t len) +{ + uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len]; + + assert(headroom >= len); + om->om_data -= len; + om->om_len += len; + + return om->om_data; +} +#endif diff --git a/src/libs/mynewt-nimble/apps/bttester/src/glue.h b/src/libs/mynewt-nimble/apps/bttester/src/glue.h new file mode 100644 index 00000000..e563331e --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bttester/src/glue.h @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef __GLUE_H__ +#define __GLUE_H__ + +#include "os/endian.h" + +#define u8_t uint8_t +#define s8_t int8_t +#define u16_t uint16_t +#define u32_t uint32_t +#define s32_t int32_t + +#ifndef BIT +#define BIT(n) (1UL << (n)) +#endif + +#define __packed __attribute__((__packed__)) + +#define sys_le16_to_cpu le16toh + +struct bt_data { + u8_t type; + u8_t data_len; + const u8_t *data; +}; + +#define BT_DATA(_type, _data, _data_len) \ + { \ + .type = (_type), \ + .data_len = (_data_len), \ + .data = (const u8_t *)(_data), \ + } + +struct os_mbuf * NET_BUF_SIMPLE(uint16_t size); +void net_buf_simple_init(struct os_mbuf *buf, size_t reserve_head); +void net_buf_simple_add_le16(struct os_mbuf *om, uint16_t val); +void net_buf_simple_add_u8(struct os_mbuf *om, uint8_t val); +void *net_buf_simple_add(struct os_mbuf *om, uint8_t len); +uint8_t *net_buf_simple_push(struct os_mbuf *om, uint8_t len); + +#define net_buf_simple_add_mem(a,b,c) os_mbuf_append(a,b,c) + +const char *bt_hex(const void *buf, size_t len); + +#endif /* __GLUE_H__ */ diff --git a/src/libs/mynewt-nimble/apps/bttester/src/l2cap.c b/src/libs/mynewt-nimble/apps/bttester/src/l2cap.c new file mode 100644 index 00000000..45b904a1 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bttester/src/l2cap.c @@ -0,0 +1,477 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* l2cap.c - Bluetooth L2CAP Tester */ + +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" + +#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) + +#include "console/console.h" +#include "host/ble_gap.h" +#include "host/ble_l2cap.h" + +#include "bttester.h" + +#define CONTROLLER_INDEX 0 +#define CHANNELS MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) +#define TESTER_COC_MTU (230) +#define TESTER_COC_BUF_COUNT (3 * MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)) + +static os_membuf_t tester_sdu_coc_mem[ + OS_MEMPOOL_SIZE(TESTER_COC_BUF_COUNT, TESTER_COC_MTU) +]; + +struct os_mbuf_pool sdu_os_mbuf_pool; +static struct os_mempool sdu_coc_mbuf_mempool; + +static struct channel { + u8_t chan_id; /* Internal number that identifies L2CAP channel. */ + u8_t state; + struct ble_l2cap_chan *chan; +} channels[CHANNELS]; + +static u8_t recv_cb_buf[TESTER_COC_MTU + sizeof(struct l2cap_data_received_ev)]; + +struct channel *find_channel(struct ble_l2cap_chan *chan) { + int i; + + for (i = 0; i < CHANNELS; ++i) { + if (channels[i].chan == chan) { + return &channels[i]; + } + } + + return NULL; +} + +static void +tester_l2cap_coc_recv(struct ble_l2cap_chan *chan, struct os_mbuf *sdu) +{ + SYS_LOG_DBG("LE CoC SDU received, chan: 0x%08lx, data len %d", + (uint32_t) chan, OS_MBUF_PKTLEN(sdu)); + + os_mbuf_free_chain(sdu); + sdu = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0); + assert(sdu != NULL); + + ble_l2cap_recv_ready(chan, sdu); +} + +static void recv_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan, + struct os_mbuf *buf, void *arg) +{ + struct l2cap_data_received_ev *ev = (void *) recv_cb_buf; + struct channel *channel = arg; + + ev->chan_id = channel->chan_id; + ev->data_length = buf->om_len; + memcpy(ev->data, buf->om_data, buf->om_len); + + tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_DATA_RECEIVED, + CONTROLLER_INDEX, recv_cb_buf, sizeof(*ev) + buf->om_len); + + tester_l2cap_coc_recv(chan, buf); +} + +static void unstalled_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan, + int status, void *arg) +{ + if (status) { + tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_SEND_DATA, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + } else { + tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_SEND_DATA, + CONTROLLER_INDEX, BTP_STATUS_SUCCESS); + } +} + +static struct channel *get_free_channel(void) +{ + u8_t i; + struct channel *chan; + + for (i = 0; i < CHANNELS; i++) { + if (channels[i].state) { + continue; + } + + chan = &channels[i]; + chan->chan_id = i; + + return chan; + } + + return NULL; +} + +static void connected_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan, + void *arg) +{ + struct l2cap_connected_ev ev; + struct ble_gap_conn_desc desc; + struct channel *channel; + + channel = get_free_channel(); + if (!channel) { + assert(0); + } + + channel->chan = chan; + channel->state = 0; + + ev.chan_id = channel->chan_id; + channel->state = 1; + channel->chan = chan; + /* TODO: ev.psm */ + + if (!ble_gap_conn_find(conn_handle, &desc)) { + ev.address_type = desc.peer_ota_addr.type; + memcpy(ev.address, desc.peer_ota_addr.val, + sizeof(ev.address)); + } + + tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_CONNECTED, CONTROLLER_INDEX, + (u8_t *) &ev, sizeof(ev)); +} + +static void disconnected_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan, + void *arg) +{ + struct l2cap_disconnected_ev ev; + struct ble_gap_conn_desc desc; + struct channel *channel; + + memset(&ev, 0, sizeof(struct l2cap_disconnected_ev)); + + channel = find_channel(chan); + if (channel != NULL) { + channel->state = 0; + channel->chan = chan; + + ev.chan_id = channel->chan_id; + /* TODO: ev.result */ + /* TODO: ev.psm */ + } + + if (!ble_gap_conn_find(conn_handle, &desc)) { + ev.address_type = desc.peer_ota_addr.type; + memcpy(ev.address, desc.peer_ota_addr.val, + sizeof(ev.address)); + } + + tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_DISCONNECTED, + CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev)); +} + +static int accept_cb(uint16_t conn_handle, uint16_t peer_mtu, + struct ble_l2cap_chan *chan) +{ + struct os_mbuf *sdu_rx; + + SYS_LOG_DBG("LE CoC accepting, chan: 0x%08lx, peer_mtu %d", + (uint32_t) chan, peer_mtu); + + sdu_rx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0); + if (!sdu_rx) { + return BLE_HS_ENOMEM; + } + + ble_l2cap_recv_ready(chan, sdu_rx); + + return 0; +} + +static int +tester_l2cap_event(struct ble_l2cap_event *event, void *arg) +{ + struct ble_l2cap_chan_info chan_info; + + switch (event->type) { + case BLE_L2CAP_EVENT_COC_CONNECTED: + if (event->connect.status) { + console_printf("LE COC error: %d\n", event->connect.status); + disconnected_cb(event->connect.conn_handle, + event->connect.chan, arg); + return 0; + } + + ble_l2cap_get_chan_info(event->connect.chan, &chan_info); + + console_printf("LE COC connected, conn: %d, chan: 0x%08lx, scid: 0x%04x, " + "dcid: 0x%04x, our_mtu: 0x%04x, peer_mtu: 0x%04x\n", + event->connect.conn_handle, + (uint32_t) event->connect.chan, + chan_info.scid, + chan_info.dcid, + chan_info.our_l2cap_mtu, + chan_info.peer_l2cap_mtu); + + connected_cb(event->connect.conn_handle, + event->connect.chan, arg); + + return 0; + case BLE_L2CAP_EVENT_COC_DISCONNECTED: + console_printf("LE CoC disconnected, chan: 0x%08lx\n", + (uint32_t) event->disconnect.chan); + + disconnected_cb(event->disconnect.conn_handle, + event->disconnect.chan, arg); + return 0; + case BLE_L2CAP_EVENT_COC_ACCEPT: + console_printf("LE CoC accept, chan: 0x%08lx, handle: %u, sdu_size: %u\n", + (uint32_t) event->accept.chan, + event->accept.conn_handle, + event->accept.peer_sdu_size); + + return accept_cb(event->accept.conn_handle, + event->accept.peer_sdu_size, + event->accept.chan); + + case BLE_L2CAP_EVENT_COC_DATA_RECEIVED: + console_printf("LE CoC data received, chan: 0x%08lx, handle: %u, sdu_len: %u\n", + (uint32_t) event->receive.chan, + event->receive.conn_handle, + event->receive.sdu_rx->om_len); + recv_cb(event->receive.conn_handle, event->receive.chan, + event->receive.sdu_rx, arg); + return 0; + case BLE_L2CAP_EVENT_COC_TX_UNSTALLED: + console_printf("LE CoC tx unstalled, chan: 0x%08lx, handle: %u, status: %d\n", + (uint32_t) event->tx_unstalled.chan, + event->tx_unstalled.conn_handle, + event->tx_unstalled.status); + unstalled_cb(event->tx_unstalled.conn_handle, + event->tx_unstalled.chan, + event->tx_unstalled.status, arg); + return 0; + default: + return 0; + } +} + +static void connect(u8_t *data, u16_t len) +{ + const struct l2cap_connect_cmd *cmd = (void *) data; + struct l2cap_connect_rp rp; + struct ble_gap_conn_desc desc; + struct channel *chan; + struct os_mbuf *sdu_rx; + ble_addr_t *addr = (void *) data; + int rc; + + SYS_LOG_DBG("connect: type: %d addr: %s", addr->type, bt_hex(addr->val, 6)); + + rc = ble_gap_conn_find_by_addr(addr, &desc); + if (rc) { + SYS_LOG_ERR("GAP conn find failed"); + goto fail; + } + + chan = get_free_channel(); + if (!chan) { + SYS_LOG_ERR("No free channels"); + goto fail; + } + + sdu_rx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0); + if (sdu_rx == NULL) { + SYS_LOG_ERR("Failed to alloc buf"); + goto fail; + } + + rc = ble_l2cap_connect(desc.conn_handle, htole16(cmd->psm), + TESTER_COC_MTU, sdu_rx, + tester_l2cap_event, chan); + if (rc) { + SYS_LOG_ERR("L2CAP connect failed\n"); + goto fail; + } + + rp.chan_id = chan->chan_id; + + tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_CONNECT, CONTROLLER_INDEX, + (u8_t *) &rp, sizeof(rp)); + + return; + +fail: + tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_CONNECT, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void disconnect(u8_t *data, u16_t len) +{ + const struct l2cap_disconnect_cmd *cmd = (void *) data; + struct channel *chan; + u8_t status; + int err; + + SYS_LOG_DBG(""); + + chan = &channels[cmd->chan_id]; + + err = ble_l2cap_disconnect(chan->chan); + if (err) { + status = BTP_STATUS_FAILED; + goto rsp; + } + + status = BTP_STATUS_SUCCESS; + +rsp: + tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_DISCONNECT, CONTROLLER_INDEX, + status); +} + +static void send_data(u8_t *data, u16_t len) +{ + const struct l2cap_send_data_cmd *cmd = (void *) data; + struct channel *chan = &channels[cmd->chan_id]; + struct os_mbuf *sdu_tx = NULL; + int rc; + u16_t data_len = sys_le16_to_cpu(cmd->data_len); + + SYS_LOG_DBG("cmd->chan_id=%d", cmd->chan_id); + + /* FIXME: For now, fail if data length exceeds buffer length */ + if (data_len > TESTER_COC_MTU) { + SYS_LOG_ERR("Data length exceeds buffer length"); + goto fail; + } + + sdu_tx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0); + if (sdu_tx == NULL) { + SYS_LOG_ERR("No memory in the test sdu pool\n"); + goto fail; + } + + os_mbuf_append(sdu_tx, cmd->data, data_len); + + /* ble_l2cap_send takes ownership of the sdu */ + rc = ble_l2cap_send(chan->chan, sdu_tx); + if (rc == 0 || rc == BLE_HS_ESTALLED) { + tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_SEND_DATA, CONTROLLER_INDEX, + BTP_STATUS_SUCCESS); + return; + } + + SYS_LOG_ERR("Unable to send data: %d", rc); + os_mbuf_free_chain(sdu_tx); + +fail: + tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_SEND_DATA, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void listen(u8_t *data, u16_t len) +{ + const struct l2cap_listen_cmd *cmd = (void *) data; + int rc; + + SYS_LOG_DBG(""); + + /* TODO: Handle cmd->transport flag */ + rc = ble_l2cap_create_server(cmd->psm, TESTER_COC_MTU, + tester_l2cap_event, NULL); + if (rc) { + goto fail; + } + + tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_LISTEN, CONTROLLER_INDEX, + BTP_STATUS_SUCCESS); + return; + +fail: + tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_LISTEN, CONTROLLER_INDEX, + BTP_STATUS_FAILED); +} + +static void supported_commands(u8_t *data, u16_t len) +{ + u8_t cmds[1]; + struct l2cap_read_supported_commands_rp *rp = (void *) cmds; + + memset(cmds, 0, sizeof(cmds)); + + tester_set_bit(cmds, L2CAP_READ_SUPPORTED_COMMANDS); + tester_set_bit(cmds, L2CAP_CONNECT); + tester_set_bit(cmds, L2CAP_DISCONNECT); + tester_set_bit(cmds, L2CAP_LISTEN); + tester_set_bit(cmds, L2CAP_SEND_DATA); + + tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_READ_SUPPORTED_COMMANDS, + CONTROLLER_INDEX, (u8_t *) rp, sizeof(cmds)); +} + +void tester_handle_l2cap(u8_t opcode, u8_t index, u8_t *data, + u16_t len) +{ + switch (opcode) { + case L2CAP_READ_SUPPORTED_COMMANDS: + supported_commands(data, len); + return; + case L2CAP_CONNECT: + connect(data, len); + return; + case L2CAP_DISCONNECT: + disconnect(data, len); + return; + case L2CAP_SEND_DATA: + send_data(data, len); + return; + case L2CAP_LISTEN: + listen(data, len); + return; + default: + tester_rsp(BTP_SERVICE_ID_L2CAP, opcode, index, + BTP_STATUS_UNKNOWN_CMD); + return; + } +} + +u8_t tester_init_l2cap(void) +{ + int rc; + + /* For testing we want to support all the available channels */ + rc = os_mempool_init(&sdu_coc_mbuf_mempool, TESTER_COC_BUF_COUNT, + TESTER_COC_MTU, tester_sdu_coc_mem, + "tester_coc_sdu_pool"); + assert(rc == 0); + + rc = os_mbuf_pool_init(&sdu_os_mbuf_pool, &sdu_coc_mbuf_mempool, + TESTER_COC_MTU, TESTER_COC_BUF_COUNT); + assert(rc == 0); + + return BTP_STATUS_SUCCESS; +} + +u8_t tester_unregister_l2cap(void) +{ + return BTP_STATUS_SUCCESS; +} + +#endif diff --git a/src/libs/mynewt-nimble/apps/bttester/src/main.c b/src/libs/mynewt-nimble/apps/bttester/src/main.c new file mode 100644 index 00000000..ea130805 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bttester/src/main.c @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* main.c - Application main entry point */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sysinit/sysinit.h" + +#include "modlog/modlog.h" +#include "host/ble_uuid.h" +#include "host/ble_hs.h" + +#include "bttester.h" + +static void on_reset(int reason) +{ + MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason); +} + +static void on_sync(void) +{ + MODLOG_DFLT(INFO, "Bluetooth initialized\n"); + + tester_init(); +} + +int main(int argc, char **argv) +{ + int rc; + +#ifdef ARCH_sim + mcu_sim_parse_args(argc, argv); +#endif + + /* Initialize OS */ + sysinit(); + + /* Initialize the NimBLE host configuration. */ + ble_hs_cfg.reset_cb = on_reset; + ble_hs_cfg.sync_cb = on_sync; + ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb, + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + + rc = gatt_svr_init(); + assert(rc == 0); + + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} diff --git a/src/libs/mynewt-nimble/apps/bttester/src/mesh.c b/src/libs/mynewt-nimble/apps/bttester/src/mesh.c new file mode 100644 index 00000000..e18a2a4e --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bttester/src/mesh.c @@ -0,0 +1,970 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* mesh.c - Bluetooth Mesh Tester */ + +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" + +#if MYNEWT_VAL(BLE_MESH) + +#include + +#include "mesh/mesh.h" +#include "mesh/glue.h" +#include "mesh/testing.h" +#include "console/console.h" + +#include "bttester.h" + +extern u8_t own_addr_type; + +#define CONTROLLER_INDEX 0 +#define CID_LOCAL 0xffff + +/* Health server data */ +#define CUR_FAULTS_MAX 4 +#define HEALTH_TEST_ID 0x00 + +static u8_t cur_faults[CUR_FAULTS_MAX]; +static u8_t reg_faults[CUR_FAULTS_MAX * 2]; + +/* Provision node data */ +static u8_t net_key[16]; +static u16_t net_key_idx; +static u8_t flags; +static u32_t iv_index; +static u16_t addr; +static u8_t dev_key[16]; +static u8_t input_size; + +/* Configured provisioning data */ +static u8_t dev_uuid[16]; +static u8_t static_auth[16]; + +/* Vendor Model data */ +#define VND_MODEL_ID_1 0x1234 + +/* Model send data */ +#define MODEL_BOUNDS_MAX 2 + +static struct model_data { + struct bt_mesh_model *model; + u16_t addr; + u16_t appkey_idx; +} model_bound[MODEL_BOUNDS_MAX]; + +static struct { + u16_t local; + u16_t dst; + u16_t net_idx; +} net = { + .local = BT_MESH_ADDR_UNASSIGNED, + .dst = BT_MESH_ADDR_UNASSIGNED, +}; + +static void supported_commands(u8_t *data, u16_t len) +{ + struct os_mbuf *buf = NET_BUF_SIMPLE(BTP_DATA_MAX_SIZE); + + net_buf_simple_init(buf, 0); + + /* 1st octet */ + memset(net_buf_simple_add(buf, 1), 0, 1); + tester_set_bit(buf->om_data, MESH_READ_SUPPORTED_COMMANDS); + tester_set_bit(buf->om_data, MESH_CONFIG_PROVISIONING); + tester_set_bit(buf->om_data, MESH_PROVISION_NODE); + tester_set_bit(buf->om_data, MESH_INIT); + tester_set_bit(buf->om_data, MESH_RESET); + tester_set_bit(buf->om_data, MESH_INPUT_NUMBER); + tester_set_bit(buf->om_data, MESH_INPUT_STRING); + /* 2nd octet */ + tester_set_bit(buf->om_data, MESH_IVU_TEST_MODE); + tester_set_bit(buf->om_data, MESH_IVU_TOGGLE_STATE); + tester_set_bit(buf->om_data, MESH_NET_SEND); + tester_set_bit(buf->om_data, MESH_HEALTH_GENERATE_FAULTS); + tester_set_bit(buf->om_data, MESH_HEALTH_CLEAR_FAULTS); + tester_set_bit(buf->om_data, MESH_LPN); + tester_set_bit(buf->om_data, MESH_LPN_POLL); + tester_set_bit(buf->om_data, MESH_MODEL_SEND); + /* 3rd octet */ + memset(net_buf_simple_add(buf, 1), 0, 1); +#if MYNEWT_VAL(BLE_MESH_TESTING) + tester_set_bit(buf->om_data, MESH_LPN_SUBSCRIBE); + tester_set_bit(buf->om_data, MESH_LPN_UNSUBSCRIBE); + tester_set_bit(buf->om_data, MESH_RPL_CLEAR); +#endif /* CONFIG_BT_TESTING */ + tester_set_bit(buf->om_data, MESH_PROXY_IDENTITY); + + tester_send_buf(BTP_SERVICE_ID_MESH, MESH_READ_SUPPORTED_COMMANDS, + CONTROLLER_INDEX, buf); +} + +static struct bt_mesh_cfg_srv cfg_srv = { + .relay = BT_MESH_RELAY_ENABLED, + .beacon = BT_MESH_BEACON_ENABLED, +#if MYNEWT_VAL(BLE_MESH_FRIEND) + .frnd = BT_MESH_FRIEND_ENABLED, +#else + .frnd = BT_MESH_FRIEND_NOT_SUPPORTED, +#endif +#if MYNEWT_VAL(BLE_MESH_GATT_PROXY) + .gatt_proxy = BT_MESH_GATT_PROXY_ENABLED, +#else + .gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED, +#endif + .default_ttl = 7, + + /* 3 transmissions with 20ms interval */ + .net_transmit = BT_MESH_TRANSMIT(2, 20), + .relay_retransmit = BT_MESH_TRANSMIT(2, 20), +}; + +static void get_faults(u8_t *faults, u8_t faults_size, u8_t *dst, u8_t *count) +{ + u8_t i, limit = *count; + + for (i = 0, *count = 0; i < faults_size && *count < limit; i++) { + if (faults[i]) { + *dst++ = faults[i]; + (*count)++; + } + } +} + +static int fault_get_cur(struct bt_mesh_model *model, u8_t *test_id, + u16_t *company_id, u8_t *faults, u8_t *fault_count) +{ + SYS_LOG_DBG(""); + + *test_id = HEALTH_TEST_ID; + *company_id = CID_LOCAL; + + get_faults(cur_faults, sizeof(cur_faults), faults, fault_count); + + return 0; +} + +static int fault_get_reg(struct bt_mesh_model *model, u16_t company_id, + u8_t *test_id, u8_t *faults, u8_t *fault_count) +{ + SYS_LOG_DBG("company_id 0x%04x", company_id); + + if (company_id != CID_LOCAL) { + return -EINVAL; + } + + *test_id = HEALTH_TEST_ID; + + get_faults(reg_faults, sizeof(reg_faults), faults, fault_count); + + return 0; +} + +static int fault_clear(struct bt_mesh_model *model, uint16_t company_id) +{ + SYS_LOG_DBG("company_id 0x%04x", company_id); + + if (company_id != CID_LOCAL) { + return -EINVAL; + } + + memset(reg_faults, 0, sizeof(reg_faults)); + + return 0; +} + +static int fault_test(struct bt_mesh_model *model, uint8_t test_id, + uint16_t company_id) +{ + SYS_LOG_DBG("test_id 0x%02x company_id 0x%04x", test_id, company_id); + + if (company_id != CID_LOCAL || test_id != HEALTH_TEST_ID) { + return -EINVAL; + } + + return 0; +} + +static const struct bt_mesh_health_srv_cb health_srv_cb = { + .fault_get_cur = fault_get_cur, + .fault_get_reg = fault_get_reg, + .fault_clear = fault_clear, + .fault_test = fault_test, +}; + +static struct bt_mesh_health_srv health_srv = { + .cb = &health_srv_cb, +}; + +static struct bt_mesh_model_pub health_pub; + +static void +health_pub_init(void) +{ + health_pub.msg = BT_MESH_HEALTH_FAULT_MSG(CUR_FAULTS_MAX); +} + +static struct bt_mesh_cfg_cli cfg_cli = { +}; + +void show_faults(u8_t test_id, u16_t cid, u8_t *faults, size_t fault_count) +{ + size_t i; + + if (!fault_count) { + SYS_LOG_DBG("Health Test ID 0x%02x Company ID 0x%04x: " + "no faults", test_id, cid); + return; + } + + SYS_LOG_DBG("Health Test ID 0x%02x Company ID 0x%04x Fault Count %zu: ", + test_id, cid, fault_count); + + for (i = 0; i < fault_count; i++) { + SYS_LOG_DBG("0x%02x", faults[i]); + } +} + +static void health_current_status(struct bt_mesh_health_cli *cli, u16_t addr, + u8_t test_id, u16_t cid, u8_t *faults, + size_t fault_count) +{ + SYS_LOG_DBG("Health Current Status from 0x%04x", addr); + show_faults(test_id, cid, faults, fault_count); +} + +static struct bt_mesh_health_cli health_cli = { + .current_status = health_current_status, +}; + +static struct bt_mesh_model root_models[] = { + BT_MESH_MODEL_CFG_SRV(&cfg_srv), + BT_MESH_MODEL_CFG_CLI(&cfg_cli), + BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub), + BT_MESH_MODEL_HEALTH_CLI(&health_cli), +}; + +static struct bt_mesh_model vnd_models[] = { + BT_MESH_MODEL_VND(CID_LOCAL, VND_MODEL_ID_1, BT_MESH_MODEL_NO_OPS, NULL, + NULL), +}; + +static struct bt_mesh_elem elements[] = { + BT_MESH_ELEM(0, root_models, vnd_models), +}; + +static void link_open(bt_mesh_prov_bearer_t bearer) +{ + struct mesh_prov_link_open_ev ev; + + SYS_LOG_DBG("bearer 0x%02x", bearer); + + switch (bearer) { + case BT_MESH_PROV_ADV: + ev.bearer = MESH_PROV_BEARER_PB_ADV; + break; + case BT_MESH_PROV_GATT: + ev.bearer = MESH_PROV_BEARER_PB_GATT; + break; + default: + SYS_LOG_ERR("Invalid bearer"); + + return; + } + + tester_send(BTP_SERVICE_ID_MESH, MESH_EV_PROV_LINK_OPEN, + CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev)); +} + +static void link_close(bt_mesh_prov_bearer_t bearer) +{ + struct mesh_prov_link_closed_ev ev; + + SYS_LOG_DBG("bearer 0x%02x", bearer); + + switch (bearer) { + case BT_MESH_PROV_ADV: + ev.bearer = MESH_PROV_BEARER_PB_ADV; + break; + case BT_MESH_PROV_GATT: + ev.bearer = MESH_PROV_BEARER_PB_GATT; + break; + default: + SYS_LOG_ERR("Invalid bearer"); + + return; + } + + tester_send(BTP_SERVICE_ID_MESH, MESH_EV_PROV_LINK_CLOSED, + CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev)); +} + +static int output_number(bt_mesh_output_action_t action, u32_t number) +{ + struct mesh_out_number_action_ev ev; + + SYS_LOG_DBG("action 0x%04x number 0x%08lx", action, number); + + ev.action = sys_cpu_to_le16(action); + ev.number = sys_cpu_to_le32(number); + + tester_send(BTP_SERVICE_ID_MESH, MESH_EV_OUT_NUMBER_ACTION, + CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev)); + + return 0; +} + +static int output_string(const char *str) +{ + struct mesh_out_string_action_ev *ev; + struct os_mbuf *buf = NET_BUF_SIMPLE(BTP_DATA_MAX_SIZE); + + SYS_LOG_DBG("str %s", str); + + net_buf_simple_init(buf, 0); + + ev = net_buf_simple_add(buf, sizeof(*ev)); + ev->string_len = strlen(str); + + net_buf_simple_add_mem(buf, str, ev->string_len); + + tester_send_buf(BTP_SERVICE_ID_MESH, MESH_EV_OUT_STRING_ACTION, + CONTROLLER_INDEX, buf); + + os_mbuf_free_chain(buf); + return 0; +} + +static int input(bt_mesh_input_action_t action, u8_t size) +{ + struct mesh_in_action_ev ev; + + SYS_LOG_DBG("action 0x%04x number 0x%02x", action, size); + + input_size = size; + + ev.action = sys_cpu_to_le16(action); + ev.size = size; + + tester_send(BTP_SERVICE_ID_MESH, MESH_EV_IN_ACTION, CONTROLLER_INDEX, + (u8_t *) &ev, sizeof(ev)); + + return 0; +} + +static void prov_complete(u16_t net_idx, u16_t addr) +{ + SYS_LOG_DBG("net_idx 0x%04x addr 0x%04x", net_idx, addr); + + net.net_idx = net_idx, + net.local = addr; + net.dst = addr; + + tester_send(BTP_SERVICE_ID_MESH, MESH_EV_PROVISIONED, CONTROLLER_INDEX, + NULL, 0); +} + +static void prov_reset(void) +{ + SYS_LOG_DBG(""); + + bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT); +} + +static const struct bt_mesh_comp comp = { + .cid = CID_LOCAL, + .elem = elements, + .elem_count = ARRAY_SIZE(elements), +}; + +static struct bt_mesh_prov prov = { + .uuid = dev_uuid, + .static_val = static_auth, + .static_val_len = sizeof(static_auth), + .output_number = output_number, + .output_string = output_string, + .input = input, + .link_open = link_open, + .link_close = link_close, + .complete = prov_complete, + .reset = prov_reset, +}; + +static void config_prov(u8_t *data, u16_t len) +{ + const struct mesh_config_provisioning_cmd *cmd = (void *) data; + + SYS_LOG_DBG(""); + + memcpy(dev_uuid, cmd->uuid, sizeof(dev_uuid)); + memcpy(static_auth, cmd->static_auth, sizeof(static_auth)); + + prov.output_size = cmd->out_size; + prov.output_actions = sys_le16_to_cpu(cmd->out_actions); + prov.input_size = cmd->in_size; + prov.input_actions = sys_le16_to_cpu(cmd->in_actions); + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_CONFIG_PROVISIONING, + CONTROLLER_INDEX, BTP_STATUS_SUCCESS); +} + +static void provision_node(u8_t *data, u16_t len) +{ + const struct mesh_provision_node_cmd *cmd = (void *) data; + + SYS_LOG_DBG(""); + + memcpy(dev_key, cmd->dev_key, sizeof(dev_key)); + memcpy(net_key, cmd->net_key, sizeof(net_key)); + + addr = sys_le16_to_cpu(cmd->addr); + flags = cmd->flags; + iv_index = sys_le32_to_cpu(cmd->iv_index); + net_key_idx = sys_le16_to_cpu(cmd->net_key_idx); + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_PROVISION_NODE, + CONTROLLER_INDEX, BTP_STATUS_SUCCESS); +} + +static void init(u8_t *data, u16_t len) +{ + u8_t status = BTP_STATUS_SUCCESS; + int err; + + SYS_LOG_DBG(""); + + err = bt_mesh_init(own_addr_type, &prov, &comp); + if (err) { + status = BTP_STATUS_FAILED; + + goto rsp; + } + + if (addr) { + err = bt_mesh_provision(net_key, net_key_idx, flags, iv_index, + addr, dev_key); + if (err) { + status = BTP_STATUS_FAILED; + } + } else { + err = bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT); + if (err) { + status = BTP_STATUS_FAILED; + } + } + + /* Set device key for vendor model */ + vnd_models[0].keys[0] = BT_MESH_KEY_DEV; + +rsp: + tester_rsp(BTP_SERVICE_ID_MESH, MESH_INIT, CONTROLLER_INDEX, + status); +} + +static void reset(u8_t *data, u16_t len) +{ + SYS_LOG_DBG(""); + + bt_mesh_reset(); + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_RESET, CONTROLLER_INDEX, + BTP_STATUS_SUCCESS); +} + +static void input_number(u8_t *data, u16_t len) +{ + const struct mesh_input_number_cmd *cmd = (void *) data; + u8_t status = BTP_STATUS_SUCCESS; + u32_t number; + int err; + + number = sys_le32_to_cpu(cmd->number); + + SYS_LOG_DBG("number 0x%04lx", number); + + err = bt_mesh_input_number(number); + if (err) { + status = BTP_STATUS_FAILED; + } + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_INPUT_NUMBER, CONTROLLER_INDEX, + status); +} + +static void input_string(u8_t *data, u16_t len) +{ + const struct mesh_input_string_cmd *cmd = (void *) data; + u8_t status = BTP_STATUS_SUCCESS; + u8_t str_auth[16]; + int err; + + SYS_LOG_DBG(""); + + if (cmd->string_len > sizeof(str_auth)) { + SYS_LOG_ERR("Too long input (%u chars required)", input_size); + status = BTP_STATUS_FAILED; + goto rsp; + } else if (cmd->string_len < input_size) { + SYS_LOG_ERR("Too short input (%u chars required)", input_size); + status = BTP_STATUS_FAILED; + goto rsp; + } + + strncpy((char *)str_auth, (char *)cmd->string, cmd->string_len); + + err = bt_mesh_input_string((char *)str_auth); + if (err) { + status = BTP_STATUS_FAILED; + } + +rsp: + tester_rsp(BTP_SERVICE_ID_MESH, MESH_INPUT_STRING, CONTROLLER_INDEX, + status); +} + +static void ivu_test_mode(u8_t *data, u16_t len) +{ + const struct mesh_ivu_test_mode_cmd *cmd = (void *) data; + + SYS_LOG_DBG("enable 0x%02x", cmd->enable); + + bt_mesh_iv_update_test(cmd->enable ? true : false); + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_IVU_TEST_MODE, CONTROLLER_INDEX, + BTP_STATUS_SUCCESS); +} + +static void ivu_toggle_state(u8_t *data, u16_t len) +{ + bool result; + + SYS_LOG_DBG(""); + + result = bt_mesh_iv_update(); + if (!result) { + SYS_LOG_ERR("Failed to toggle the IV Update state"); + } + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_IVU_TOGGLE_STATE, CONTROLLER_INDEX, + result ? BTP_STATUS_SUCCESS : BTP_STATUS_FAILED); +} + +static void lpn(u8_t *data, u16_t len) +{ + struct mesh_lpn_set_cmd *cmd = (void *) data; + bool enable; + int err; + + SYS_LOG_DBG("enable 0x%02x", cmd->enable); + + enable = cmd->enable ? true : false; + err = bt_mesh_lpn_set(enable); + if (err) { + SYS_LOG_ERR("Failed to toggle LPN (err %d)", err); + } + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_LPN, CONTROLLER_INDEX, + err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS); +} + +static void lpn_poll(u8_t *data, u16_t len) +{ + int err; + + SYS_LOG_DBG(""); + + err = bt_mesh_lpn_poll(); + if (err) { + SYS_LOG_ERR("Failed to send poll msg (err %d)", err); + } + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_LPN_POLL, CONTROLLER_INDEX, + err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS); +} + +static void net_send(u8_t *data, u16_t len) +{ + struct mesh_net_send_cmd *cmd = (void *) data; + struct os_mbuf *msg = NET_BUF_SIMPLE(UINT8_MAX); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net.net_idx, + .app_idx = BT_MESH_KEY_DEV, + .addr = sys_le16_to_cpu(cmd->dst), + .send_ttl = cmd->ttl, + }; + int err; + + SYS_LOG_DBG("ttl 0x%02x dst 0x%04x payload_len %d", ctx.send_ttl, + ctx.addr, cmd->payload_len); + + net_buf_simple_add_mem(msg, cmd->payload, cmd->payload_len); + + err = bt_mesh_model_send(&vnd_models[0], &ctx, msg, NULL, NULL); + if (err) { + SYS_LOG_ERR("Failed to send (err %d)", err); + } + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_NET_SEND, CONTROLLER_INDEX, + err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS); + + os_mbuf_free_chain(msg); +} + +static void health_generate_faults(u8_t *data, u16_t len) +{ + struct mesh_health_generate_faults_rp *rp; + struct os_mbuf *buf = NET_BUF_SIMPLE(sizeof(*rp) + sizeof(cur_faults) + + sizeof(reg_faults)); + u8_t some_faults[] = { 0x01, 0x02, 0x03, 0xff, 0x06 }; + u8_t cur_faults_count, reg_faults_count; + + rp = net_buf_simple_add(buf, sizeof(*rp)); + + cur_faults_count = min(sizeof(cur_faults), sizeof(some_faults)); + memcpy(cur_faults, some_faults, cur_faults_count); + net_buf_simple_add_mem(buf, cur_faults, cur_faults_count); + rp->cur_faults_count = cur_faults_count; + + reg_faults_count = min(sizeof(reg_faults), sizeof(some_faults)); + memcpy(reg_faults, some_faults, reg_faults_count); + net_buf_simple_add_mem(buf, reg_faults, reg_faults_count); + rp->reg_faults_count = reg_faults_count; + + bt_mesh_fault_update(&elements[0]); + + tester_send_buf(BTP_SERVICE_ID_MESH, MESH_HEALTH_GENERATE_FAULTS, + CONTROLLER_INDEX, buf); +} + +static void health_clear_faults(u8_t *data, u16_t len) +{ + SYS_LOG_DBG(""); + + memset(cur_faults, 0, sizeof(cur_faults)); + memset(reg_faults, 0, sizeof(reg_faults)); + + bt_mesh_fault_update(&elements[0]); + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_HEALTH_CLEAR_FAULTS, + CONTROLLER_INDEX, BTP_STATUS_SUCCESS); +} + +static void model_send(u8_t *data, u16_t len) +{ + struct mesh_model_send_cmd *cmd = (void *) data; + struct os_mbuf *msg = NET_BUF_SIMPLE(UINT8_MAX); + struct bt_mesh_msg_ctx ctx = { + .net_idx = net.net_idx, + .app_idx = BT_MESH_KEY_DEV, + .addr = sys_le16_to_cpu(cmd->dst), + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + struct bt_mesh_model *model = NULL; + int err, i; + u16_t src = sys_le16_to_cpu(cmd->src); + + /* Lookup source address */ + for (i = 0; i < ARRAY_SIZE(model_bound); i++) { + if (bt_mesh_model_elem(model_bound[i].model)->addr == src) { + model = model_bound[i].model; + ctx.app_idx = model_bound[i].appkey_idx; + + break; + } + } + + if (!model) { + SYS_LOG_ERR("Model not found"); + err = -EINVAL; + + goto fail; + } + + SYS_LOG_DBG("src 0x%04x dst 0x%04x model %p payload_len %d", src, + ctx.addr, model, cmd->payload_len); + + net_buf_simple_add_mem(msg, cmd->payload, cmd->payload_len); + + err = bt_mesh_model_send(model, &ctx, msg, NULL, NULL); + if (err) { + SYS_LOG_ERR("Failed to send (err %d)", err); + } + +fail: + tester_rsp(BTP_SERVICE_ID_MESH, MESH_MODEL_SEND, CONTROLLER_INDEX, + err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS); + + os_mbuf_free_chain(msg); +} + +#if MYNEWT_VAL(BLE_MESH_TESTING) +static void lpn_subscribe(u8_t *data, u16_t len) +{ + struct mesh_lpn_subscribe_cmd *cmd = (void *) data; + u16_t address = sys_le16_to_cpu(cmd->address); + int err; + + SYS_LOG_DBG("address 0x%04x", address); + + err = bt_test_mesh_lpn_group_add(address); + if (err) { + SYS_LOG_ERR("Failed to subscribe (err %d)", err); + } + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_LPN_SUBSCRIBE, CONTROLLER_INDEX, + err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS); +} + +static void lpn_unsubscribe(u8_t *data, u16_t len) +{ + struct mesh_lpn_unsubscribe_cmd *cmd = (void *) data; + u16_t address = sys_le16_to_cpu(cmd->address); + int err; + + SYS_LOG_DBG("address 0x%04x", address); + + err = bt_test_mesh_lpn_group_remove(&address, 1); + if (err) { + SYS_LOG_ERR("Failed to unsubscribe (err %d)", err); + } + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_LPN_UNSUBSCRIBE, CONTROLLER_INDEX, + err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS); +} + +static void rpl_clear(u8_t *data, u16_t len) +{ + int err; + + SYS_LOG_DBG(""); + + err = bt_test_mesh_rpl_clear(); + if (err) { + SYS_LOG_ERR("Failed to clear RPL (err %d)", err); + } + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_RPL_CLEAR, CONTROLLER_INDEX, + err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS); +} +#endif /* MYNEWT_VAL(BLE_MESH_TESTING) */ + +static void proxy_identity_enable(u8_t *data, u16_t len) +{ + int err; + + SYS_LOG_DBG(""); + + err = bt_mesh_proxy_identity_enable(); + if (err) { + SYS_LOG_ERR("Failed to enable proxy identity (err %d)", err); + } + + tester_rsp(BTP_SERVICE_ID_MESH, MESH_PROXY_IDENTITY, CONTROLLER_INDEX, + err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS); +} + +void tester_handle_mesh(u8_t opcode, u8_t index, u8_t *data, u16_t len) +{ + switch (opcode) { + case MESH_READ_SUPPORTED_COMMANDS: + supported_commands(data, len); + break; + case MESH_CONFIG_PROVISIONING: + config_prov(data, len); + break; + case MESH_PROVISION_NODE: + provision_node(data, len); + break; + case MESH_INIT: + init(data, len); + break; + case MESH_RESET: + reset(data, len); + break; + case MESH_INPUT_NUMBER: + input_number(data, len); + break; + case MESH_INPUT_STRING: + input_string(data, len); + break; + case MESH_IVU_TEST_MODE: + ivu_test_mode(data, len); + break; + case MESH_IVU_TOGGLE_STATE: + ivu_toggle_state(data, len); + break; + case MESH_LPN: + lpn(data, len); + break; + case MESH_LPN_POLL: + lpn_poll(data, len); + break; + case MESH_NET_SEND: + net_send(data, len); + break; + case MESH_HEALTH_GENERATE_FAULTS: + health_generate_faults(data, len); + break; + case MESH_HEALTH_CLEAR_FAULTS: + health_clear_faults(data, len); + break; + case MESH_MODEL_SEND: + model_send(data, len); + break; +#if MYNEWT_VAL(BLE_MESH_TESTING) + case MESH_LPN_SUBSCRIBE: + lpn_subscribe(data, len); + break; + case MESH_LPN_UNSUBSCRIBE: + lpn_unsubscribe(data, len); + break; + case MESH_RPL_CLEAR: + rpl_clear(data, len); + break; +#endif /* MYNEWT_VAL(BLE_MESH_TESTING) */ + case MESH_PROXY_IDENTITY: + proxy_identity_enable(data, len); + break; + default: + tester_rsp(BTP_SERVICE_ID_MESH, opcode, index, + BTP_STATUS_UNKNOWN_CMD); + break; + } +} + +void net_recv_ev(u8_t ttl, u8_t ctl, u16_t src, u16_t dst, const void *payload, + size_t payload_len) +{ + struct os_mbuf *buf = NET_BUF_SIMPLE(UINT8_MAX); + struct mesh_net_recv_ev *ev; + + SYS_LOG_DBG("ttl 0x%02x ctl 0x%02x src 0x%04x dst 0x%04x " + "payload_len %d", ttl, ctl, src, dst, payload_len); + + if (payload_len > net_buf_simple_tailroom(buf)) { + SYS_LOG_ERR("Payload size exceeds buffer size"); + + goto done; + } + + ev = net_buf_simple_add(buf, sizeof(*ev)); + ev->ttl = ttl; + ev->ctl = ctl; + ev->src = sys_cpu_to_le16(src); + ev->dst = sys_cpu_to_le16(dst); + ev->payload_len = payload_len; + net_buf_simple_add_mem(buf, payload, payload_len); + + tester_send_buf(BTP_SERVICE_ID_MESH, MESH_EV_NET_RECV, CONTROLLER_INDEX, + buf); +done: + os_mbuf_free_chain(buf); +} + +static void model_bound_cb(u16_t addr, struct bt_mesh_model *model, + u16_t key_idx) +{ + int i; + + SYS_LOG_DBG("remote addr 0x%04x key_idx 0x%04x model %p", + addr, key_idx, model); + + for (i = 0; i < ARRAY_SIZE(model_bound); i++) { + if (!model_bound[i].model) { + model_bound[i].model = model; + model_bound[i].addr = addr; + model_bound[i].appkey_idx = key_idx; + + return; + } + } + + SYS_LOG_ERR("model_bound is full"); +} + +static void model_unbound_cb(u16_t addr, struct bt_mesh_model *model, + u16_t key_idx) +{ + int i; + + SYS_LOG_DBG("remote addr 0x%04x key_idx 0x%04x model %p", + addr, key_idx, model); + + for (i = 0; i < ARRAY_SIZE(model_bound); i++) { + if (model_bound[i].model == model) { + model_bound[i].model = NULL; + model_bound[i].addr = 0x0000; + model_bound[i].appkey_idx = BT_MESH_KEY_UNUSED; + + return; + } + } + + SYS_LOG_INF("model not found"); +} + +static void invalid_bearer_cb(u8_t opcode) +{ + struct mesh_invalid_bearer_ev ev = { + .opcode = opcode, + }; + + SYS_LOG_DBG("opcode 0x%02x", opcode); + + tester_send(BTP_SERVICE_ID_MESH, MESH_EV_INVALID_BEARER, + CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev)); +} + +static void incomp_timer_exp_cb(void) +{ + tester_send(BTP_SERVICE_ID_MESH, MESH_EV_INCOMP_TIMER_EXP, + CONTROLLER_INDEX, NULL, 0); +} + +static struct bt_test_cb bt_test_cb = { + .mesh_net_recv = net_recv_ev, + .mesh_model_bound = model_bound_cb, + .mesh_model_unbound = model_unbound_cb, + .mesh_prov_invalid_bearer = invalid_bearer_cb, + .mesh_trans_incomp_timer_exp = incomp_timer_exp_cb, +}; + +u8_t tester_init_mesh(void) +{ + health_pub_init(); + + if (IS_ENABLED(CONFIG_BT_TESTING)) { + bt_test_cb_register(&bt_test_cb); + } + + return BTP_STATUS_SUCCESS; +} + +u8_t tester_unregister_mesh(void) +{ + return BTP_STATUS_SUCCESS; +} + +#endif /* MYNEWT_VAL(BLE_MESH) */ diff --git a/src/libs/mynewt-nimble/apps/bttester/src/rtt_pipe.c b/src/libs/mynewt-nimble/apps/bttester/src/rtt_pipe.c new file mode 100644 index 00000000..379345a0 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bttester/src/rtt_pipe.c @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "syscfg/syscfg.h" + +#if MYNEWT_VAL(BTTESTER_PIPE_RTT) + +#include "os/mynewt.h" +#include "console/console.h" +#include "rtt/SEGGER_RTT.h" + +#include "bttester_pipe.h" + +static struct hal_timer rtt_timer; + +static bttester_pipe_recv_cb app_cb; + +static u8_t *recv_buf; +static size_t recv_buf_len; +static size_t recv_off; + +static uint8_t rtt_buf_up[MYNEWT_VAL(BTTESTER_RTT_BUFFER_SIZE_UP)]; +static uint8_t rtt_buf_down[MYNEWT_VAL(BTTESTER_RTT_BUFFER_SIZE_DOWN)]; +static int rtt_index_up, rtt_index_down; + +#define RTT_INPUT_POLL_INTERVAL_MIN 10 /* ms */ +#define RTT_INPUT_POLL_INTERVAL_STEP 10 /* ms */ +#define RTT_INPUT_POLL_INTERVAL_MAX 250 /* ms */ + +static int rtt_pipe_get_char(unsigned int index) +{ + char c; + int r; + + r = (int)SEGGER_RTT_Read(index, &c, 1u); + if (r == 1) { + r = (int)(unsigned char)c; + } else { + r = -1; + } + + return r; +} + +static void +rtt_pipe_poll_func(void *arg) +{ + static uint32_t itvl_ms = RTT_INPUT_POLL_INTERVAL_MIN; + static int key = -1; + int avail = recv_buf_len - recv_off; + + if (key < 0) { + key = rtt_pipe_get_char((unsigned int) rtt_index_down); + } + + if (key < 0) { + itvl_ms += RTT_INPUT_POLL_INTERVAL_STEP; + itvl_ms = min(itvl_ms, RTT_INPUT_POLL_INTERVAL_MAX); + } else { + while (key >= 0 && avail > 0) { + recv_buf[recv_off] = (u8_t) key; + recv_off++; + avail = recv_buf_len - recv_off; + key = rtt_pipe_get_char((unsigned int) rtt_index_down); + } + + /* + * Call application callback with received data. Application + * may provide new buffer or alter data offset. + */ + recv_buf = app_cb(recv_buf, &recv_off); + + itvl_ms = RTT_INPUT_POLL_INTERVAL_MIN; + } + + os_cputime_timer_relative(&rtt_timer, itvl_ms * 1000); +} + +int +bttester_pipe_send(const u8_t *data, int len) +{ + SEGGER_RTT_Write((unsigned int) rtt_index_up, data, (unsigned int) len); + return 0; +} + +void +bttester_pipe_register(u8_t *buf, size_t len, bttester_pipe_recv_cb cb) +{ + recv_buf = buf; + recv_buf_len = len; + app_cb = cb; +} + +int +bttester_pipe_init(void) +{ + rtt_index_up = SEGGER_RTT_AllocUpBuffer(MYNEWT_VAL(BTTESTER_RTT_BUFFER_NAME), + rtt_buf_up, sizeof(rtt_buf_up), + SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL); + + if (rtt_index_up < 0) { + return -1; + } + + rtt_index_down = SEGGER_RTT_AllocDownBuffer(MYNEWT_VAL(BTTESTER_RTT_BUFFER_NAME), + rtt_buf_down, sizeof(rtt_buf_down), + SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL); + + if (rtt_index_down < 0) { + return -1; + } + + console_printf("Using up-buffer #%d\n", rtt_index_up); + console_printf("Using down-buffer #%d\n", rtt_index_down); + + os_cputime_timer_init(&rtt_timer, rtt_pipe_poll_func, NULL); + os_cputime_timer_relative(&rtt_timer, 200000); + return 0; +} +#endif /* MYNEWT_VAL(BTTESTER_PIPE_RTT) */ diff --git a/src/libs/mynewt-nimble/apps/bttester/src/uart_pipe.c b/src/libs/mynewt-nimble/apps/bttester/src/uart_pipe.c new file mode 100644 index 00000000..ecbefa02 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bttester/src/uart_pipe.c @@ -0,0 +1,281 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "syscfg/syscfg.h" + +#if MYNEWT_VAL(BTTESTER_PIPE_UART) + +#include "os/mynewt.h" +#include "uart/uart.h" + +#include "bttester_pipe.h" + +static u8_t *recv_buf; +static size_t recv_buf_len; +static bttester_pipe_recv_cb app_cb; +static size_t recv_off; + +struct uart_pipe_ring { + uint8_t head; + uint8_t tail; + uint16_t size; + uint8_t *buf; +}; + +static struct uart_dev *uart_dev; +static struct uart_pipe_ring cr_tx; +static uint8_t cr_tx_buf[MYNEWT_VAL(BTTESTER_BTP_DATA_SIZE_MAX)]; +typedef void (*console_write_char)(struct uart_dev*, uint8_t); +static console_write_char write_char_cb; + +static struct uart_pipe_ring cr_rx; +static uint8_t cr_rx_buf[MYNEWT_VAL(BTTESTER_BTP_DATA_SIZE_MAX)]; +static volatile bool uart_console_rx_stalled; + +struct os_event rx_ev; + +static inline int +inc_and_wrap(int i, int max) +{ + return (i + 1) & (max - 1); +} + +static void +uart_pipe_ring_add_char(struct uart_pipe_ring *cr, char ch) +{ + cr->buf[cr->head] = ch; + cr->head = inc_and_wrap(cr->head, cr->size); +} + +static uint8_t +uart_pipe_ring_pull_char(struct uart_pipe_ring *cr) +{ + uint8_t ch; + + ch = cr->buf[cr->tail]; + cr->tail = inc_and_wrap(cr->tail, cr->size); + return ch; +} + +static bool +uart_pipe_ring_is_full(const struct uart_pipe_ring *cr) +{ + return inc_and_wrap(cr->head, cr->size) == cr->tail; +} + +static bool +uart_pipe_ring_is_empty(const struct uart_pipe_ring *cr) +{ + return cr->head == cr->tail; +} + +static void +uart_pipe_queue_char(struct uart_dev *uart_dev, uint8_t ch) +{ + int sr; + + if ((uart_dev->ud_dev.od_flags & OS_DEV_F_STATUS_OPEN) == 0) { + return; + } + + OS_ENTER_CRITICAL(sr); + while (uart_pipe_ring_is_full(&cr_tx)) { + /* TX needs to drain */ + uart_start_tx(uart_dev); + OS_EXIT_CRITICAL(sr); + if (os_started()) { + os_time_delay(1); + } + OS_ENTER_CRITICAL(sr); + } + uart_pipe_ring_add_char(&cr_tx, ch); + OS_EXIT_CRITICAL(sr); +} + +/* + * Interrupts disabled when console_tx_char/console_rx_char are called. + * Characters sent only in blocking mode. + */ +static int +uart_console_tx_char(void *arg) +{ + if (uart_pipe_ring_is_empty(&cr_tx)) { + return -1; + } + return uart_pipe_ring_pull_char(&cr_tx); +} + +/* + * Interrupts disabled when console_tx_char/console_rx_char are called. + */ +static int +uart_console_rx_char(void *arg, uint8_t byte) +{ + if (uart_pipe_ring_is_full(&cr_rx)) { + uart_console_rx_stalled = true; + return -1; + } + + uart_pipe_ring_add_char(&cr_rx, byte); + + if (!rx_ev.ev_queued) { + os_eventq_put(os_eventq_dflt_get(), &rx_ev); + } + + return 0; +} + +static int +uart_pipe_handle_char(int key) +{ + recv_buf[recv_off] = (u8_t) key; + recv_off++; + + return 0; +} + +static void +uart_console_rx_char_event(struct os_event *ev) +{ + static int b = -1; + int sr; + int ret; + + /* We may have unhandled character - try it first */ + if (b >= 0) { + ret = uart_pipe_handle_char(b); + if (ret < 0) { + return; + } + } + + while (!uart_pipe_ring_is_empty(&cr_rx)) { + OS_ENTER_CRITICAL(sr); + b = uart_pipe_ring_pull_char(&cr_rx); + OS_EXIT_CRITICAL(sr); + + /* If UART RX was stalled due to a full receive buffer, restart RX now + * that we have removed a byte from the buffer. + */ + if (uart_console_rx_stalled) { + uart_console_rx_stalled = false; + uart_start_rx(uart_dev); + } + + ret = uart_pipe_handle_char(b); + if (ret < 0) { + return; + } + } + + /* + * Call application callback with received data. Application + * may provide new buffer or alter data offset. + */ + recv_buf = app_cb(recv_buf, &recv_off); + + b = -1; +} + +int +bttester_pipe_send(const u8_t *data, int len) +{ + int i; + + /* Assure that there is a write cb installed; this enables to debug + * code that is faulting before the console was initialized. + */ + if (!write_char_cb) { + return -1; + } + + for (i = 0; i < len; ++i) { + write_char_cb(uart_dev, data[i]); + } + + uart_start_tx(uart_dev); + + return 0; +} + +int +bttester_pipe_send_buf(struct os_mbuf *buf) +{ + int i, len; + struct os_mbuf *om; + + /* Assure that there is a write cb installed; this enables to debug + * code that is faulting before the console was initialized. + */ + if (!write_char_cb) { + return -1; + } + + for (om = buf; om; om = SLIST_NEXT(om, om_next)) { + len = om->om_len; + for (i = 0; i < len; ++i) { + write_char_cb(uart_dev, om->om_data[i]); + } + } + + uart_start_tx(uart_dev); + + return 0; +} + +int +bttester_pipe_init(void) +{ + struct uart_conf uc = { + .uc_speed = MYNEWT_VAL(CONSOLE_UART_BAUD), + .uc_databits = 8, + .uc_stopbits = 1, + .uc_parity = UART_PARITY_NONE, + .uc_flow_ctl = MYNEWT_VAL(CONSOLE_UART_FLOW_CONTROL), + .uc_tx_char = uart_console_tx_char, + .uc_rx_char = uart_console_rx_char, + }; + + cr_tx.size = sizeof(cr_tx_buf); + cr_tx.buf = cr_tx_buf; + write_char_cb = uart_pipe_queue_char; + + cr_rx.size = sizeof(cr_rx_buf); + cr_rx.buf = cr_rx_buf; + + rx_ev.ev_cb = uart_console_rx_char_event; + + if (!uart_dev) { + uart_dev = (struct uart_dev *)os_dev_open(MYNEWT_VAL(CONSOLE_UART_DEV), + OS_TIMEOUT_NEVER, &uc); + if (!uart_dev) { + return -1; + } + } + return 0; +} + +void +bttester_pipe_register(u8_t *buf, size_t len, bttester_pipe_recv_cb cb) +{ + recv_buf = buf; + recv_buf_len = len; + app_cb = cb; +} +#endif /* MYNEWT_VAL(BTTESTER_PIPE_UART) */ diff --git a/src/libs/mynewt-nimble/apps/bttester/syscfg.yml b/src/libs/mynewt-nimble/apps/bttester/syscfg.yml new file mode 100644 index 00000000..d0fffe13 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/bttester/syscfg.yml @@ -0,0 +1,122 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Package: apps/blemesh + +syscfg.defs: + BTTESTER_PIPE_UART: + description: 'Set communication pipe to UART' + value: 1 + + BTTESTER_PIPE_RTT: + description: 'Set communication pipe to RTT' + value: 0 + + BTTESTER_RTT_BUFFER_NAME: + description: Bttester rtt pipe buffer name + value: '"bttester"' + + BTTESTER_RTT_BUFFER_SIZE_UP: + description: Bttester upstream buffer size + value: MYNEWT_VAL(BTTESTER_BTP_DATA_SIZE_MAX) + + BTTESTER_RTT_BUFFER_SIZE_DOWN: + description: Bttester downstream buffer size + value: MYNEWT_VAL(BTTESTER_BTP_DATA_SIZE_MAX) + + BTTESTER_PRIVACY_MODE: + description: Enable privacy mode (RPA or NRPA) + value: 0 + + BTTESTER_USE_NRPA: + description: Use Non Resolvable Private Address + value: 0 + + BTTESTER_LTD_ADV_TIMEOUT: + description: Limited advertising timeout + value: 30000 + + BTTESTER_CONN_RETRY: + description: Retry connections when connection failed to be established + value: 3 + + BTTESTER_BTP_DATA_SIZE_MAX: + description: Maximum BTP payload + value: 2048 + + BTTESTER_CONN_PARAM_UPDATE: + description: Trigger conn param update after connection establish + value: 0 + + BTTESTER_DEBUG: + description: Enable debug logging + value: 0 + + BTTESTER_BTP_LOG: + description: Enable logging BTP traffic + value: 0 + +syscfg.vals: + OS_MAIN_STACK_SIZE: 512 + SHELL_TASK: 0 + SHELL_NEWTMGR: 0 + LOG_LEVEL: 12 + MSYS_1_BLOCK_COUNT: 48 + + BLE_MONITOR_RTT: 1 + CONSOLE_RTT: 0 + CONSOLE_UART: 0 + RTT_NUM_BUFFERS_UP: 0 + RTT_NUM_BUFFERS_DOWN: 0 + + BLE_L2CAP_COC_MAX_NUM: 2 + BLE_L2CAP_SIG_MAX_PROCS: 2 + # Some testcases require MPS < MTU + BLE_L2CAP_COC_MPS: 100 + BLE_RPA_TIMEOUT: 30 + BLE_SM_BONDING: 1 + BLE_SM_MITM: 0 + BLE_SM_SC: 1 + BLE_SM_OUR_KEY_DIST: 7 + BLE_SM_THEIR_KEY_DIST: 7 + BLE_SVC_GAP_CENTRAL_ADDRESS_RESOLUTION: 1 + BLE_SVC_GAP_PPCP_MIN_CONN_INTERVAL: 9 + BLE_SVC_GAP_PPCP_MAX_CONN_INTERVAL: 30 + BLE_SVC_GAP_PPCP_SUPERVISION_TMO: 2000 + + BLE_MESH: 1 + BLE_MESH_SHELL: 0 + BLE_MESH_PROV: 1 + BLE_MESH_RELAY: 1 + BLE_MESH_PB_ADV: 1 + BLE_MESH_PB_GATT: 1 + BLE_MESH_LOW_POWER: 1 + BLE_MESH_LPN_AUTO: 0 + BLE_MESH_GATT_PROXY: 1 + BLE_MESH_LABEL_COUNT: 2 + BLE_MESH_SUBNET_COUNT: 2 + BLE_MESH_MODEL_GROUP_COUNT: 2 + BLE_MESH_APP_KEY_COUNT: 4 + BLE_MESH_IV_UPDATE_TEST: 1 + BLE_MESH_TESTING: 1 + BLE_MESH_FRIEND: 1 + BLE_MESH_CFG_CLI: 1 + BLE_MESH_RX_SDU_MAX: 110 + + BLE_MESH_ADV_BUF_COUNT: 20 + BLE_MESH_TX_SEG_MAX: 6 diff --git a/src/libs/mynewt-nimble/apps/ext_advertiser/pkg.yml b/src/libs/mynewt-nimble/apps/ext_advertiser/pkg.yml new file mode 100644 index 00000000..097764b2 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/ext_advertiser/pkg.yml @@ -0,0 +1,40 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: apps/ext_advertiser +pkg.type: app +pkg.description: Extended Advertising sample application. +pkg.author: "Szymon Janc" +pkg.email: "szymon.janc@codecoup.pl" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - nimble/controller + - nimble/host + - nimble/host/util + - nimble/host/services/gap + - nimble/host/services/gatt + - nimble/host/store/config + - nimble/transport/ram + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/sysinit" + - "@apache-mynewt-core/sys/id" diff --git a/src/libs/mynewt-nimble/apps/ext_advertiser/src/main.c b/src/libs/mynewt-nimble/apps/ext_advertiser/src/main.c new file mode 100644 index 00000000..6bbc23d5 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/ext_advertiser/src/main.c @@ -0,0 +1,464 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include + +#include "os/mynewt.h" +#include "console/console.h" +#include "config/config.h" +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "host/util/util.h" + +#include"patterns.h" + +static uint8_t id_addr_type; + +static void start_legacy_duration(uint8_t pattern, bool configure); +static void start_ext_max_events(uint8_t pattern, bool configure); + +static int +start_ext_max_events_gap_event(struct ble_gap_event *event, void *arg) +{ + static uint8_t pattern = 1; + + switch (event->type) { + case BLE_GAP_EVENT_ADV_COMPLETE: + break; + default: + assert(0); + return 0; + } + + assert(event->adv_complete.instance == 4); + assert(event->adv_complete.reason == BLE_HS_ETIMEOUT); + assert(event->adv_complete.num_ext_adv_events == 10); + + console_printf("instance %u terminated\n", event->adv_complete.instance); + + pattern++; + + start_ext_max_events(pattern, false); + + return 0; +} + +/* Starts advertising instance with 100 max events and changing adv data pattern + * and SID. + */ +static void +start_ext_max_events(uint8_t pattern, bool configure) +{ + struct ble_gap_ext_adv_params params; + static uint8_t adv_data[600]; + struct os_mbuf *data; + uint8_t instance = 4; + ble_addr_t addr; + int events = 10; + int rc; + + if (configure) { + /* use defaults for non-set params */ + memset (¶ms, 0, sizeof(params)); + + /* advertise using random addr */ + params.own_addr_type = BLE_OWN_ADDR_RANDOM; + + params.primary_phy = BLE_HCI_LE_PHY_1M; + params.secondary_phy = BLE_HCI_LE_PHY_1M; + params.tx_power = 127; + params.sid = pattern % 16; + + /* allow larger interval, 400 * 0.625ms with 100 events will give up to + * ~2.5 seconds for instance + */ + params.itvl_min = BLE_GAP_ADV_FAST_INTERVAL1_MIN; + params.itvl_max = 400; + + /* configure instance 0 */ + rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL, + start_ext_max_events_gap_event, NULL); + assert (rc == 0); + + /* set random (NRPA) address for instance */ + rc = ble_hs_id_gen_rnd(1, &addr); + assert (rc == 0); + + rc = ble_gap_ext_adv_set_addr(instance, &addr ); + assert (rc == 0); + } + + /* in this case both advertising data and scan response is allowed, but + * both are limited to 31 bytes each + */ + + /* get mbuf for adv data */ + data = os_msys_get_pkthdr(600, 0); + assert(data); + + memset(adv_data, pattern, sizeof(adv_data)); + + /* fill mbuf with adv data */ + rc = os_mbuf_append(data, adv_data, 600); + assert(rc == 0); + + rc = ble_gap_ext_adv_set_data(instance, data); + assert (rc == 0); + + /* start advertising */ + rc = ble_gap_ext_adv_start(instance, 0, events); + assert (rc == 0); + + console_printf("instance %u started (PDUs with max events %d)\n", + instance, events); +} + +static int +start_legacy_duration_gap_event(struct ble_gap_event *event, void *arg) +{ + static uint8_t pattern = 1; + + switch (event->type) { + case BLE_GAP_EVENT_ADV_COMPLETE: + break; + default: + assert(0); + return 0; + } + + assert(event->adv_complete.instance == 3); + assert(event->adv_complete.reason == BLE_HS_ETIMEOUT); + + console_printf("instance %u terminated\n", event->adv_complete.instance); + + pattern++; + + start_legacy_duration(pattern, false); + + return 0; +} + +/* Starts advertising instance with 5sec timeout and changing adv data pattern + * and SID. + */ +static void +start_legacy_duration(uint8_t pattern, bool configure) +{ + struct ble_gap_ext_adv_params params; + uint8_t adv_data[31]; + struct os_mbuf *data; + uint8_t instance = 3; + ble_addr_t addr; + int duration = 500; /* 5seconds, 10ms units */ + int rc; + + if (configure) { + /* use defaults for non-set params */ + memset (¶ms, 0, sizeof(params)); + + /* enable advertising using legacy PDUs */ + params.legacy_pdu = 1; + + /* advertise using random addr */ + params.own_addr_type = BLE_OWN_ADDR_RANDOM; + + params.primary_phy = BLE_HCI_LE_PHY_1M; + params.secondary_phy = BLE_HCI_LE_PHY_1M; + params.tx_power = 127; + params.sid = pattern % 16; + + /* configure instance 0 */ + rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL, + start_legacy_duration_gap_event, NULL); + assert (rc == 0); + + /* set random (NRPA) address for instance */ + rc = ble_hs_id_gen_rnd(1, &addr); + assert (rc == 0); + + rc = ble_gap_ext_adv_set_addr(instance, &addr ); + assert (rc == 0); + } + + /* in this case both advertising data and scan response is allowed, but + * both are limited to 31 bytes each + */ + + /* get mbuf for adv data */ + data = os_msys_get_pkthdr(31, 0); + assert(data); + + memset(adv_data, pattern, sizeof(adv_data)); + + /* fill mbuf with adv data */ + rc = os_mbuf_append(data, adv_data, 31); + assert(rc == 0); + + rc = ble_gap_ext_adv_set_data(instance, data); + assert (rc == 0); + + /* start advertising */ + rc = ble_gap_ext_adv_start(instance, duration, 0); + assert (rc == 0); + + console_printf("instance %u started (legacy PDUs with duration %d)\n", + instance, duration); +} + +/* this is simple non-connectable scannable instance using legacy PUDs that + * runs forever + */ +static void +start_scannable_legacy_ext(void) +{ + struct ble_gap_ext_adv_params params; + struct os_mbuf *data; + uint8_t instance = 2; + ble_addr_t addr; + int rc; + + /* use defaults for non-set params */ + memset (¶ms, 0, sizeof(params)); + + /* enable scannable advertising using legacy PDUs */ + params.scannable = 1; + params.legacy_pdu = 1; + + /* advertise using random addr */ + params.own_addr_type = BLE_OWN_ADDR_RANDOM; + + params.primary_phy = BLE_HCI_LE_PHY_1M; + params.secondary_phy = BLE_HCI_LE_PHY_1M; + params.tx_power = 127; + params.sid = 2; + + /* configure instance 0 */ + rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL, NULL, NULL); + assert (rc == 0); + + /* set random (NRPA) address for instance */ + rc = ble_hs_id_gen_rnd(1, &addr); + assert (rc == 0); + + rc = ble_gap_ext_adv_set_addr(instance, &addr ); + assert (rc == 0); + + /* in this case both advertising data and scan response is allowed, but + * both are limited to 31 bytes each + */ + + /* get mbuf for adv data */ + data = os_msys_get_pkthdr(31, 0); + assert(data); + + /* fill mbuf with adv data */ + rc = os_mbuf_append(data, ext_adv_pattern_1, 31); + assert(rc == 0); + + rc = ble_gap_ext_adv_set_data(instance, data); + assert (rc == 0); + + /* get mbuf for scan rsp data */ + data = os_msys_get_pkthdr(31, 0); + assert(data); + + /* fill mbuf with scan rsp data */ + rc = os_mbuf_append(data, ext_adv_pattern_1 + 31, 31); + assert(rc == 0); + + rc = ble_gap_ext_adv_rsp_set_data(instance, data); + assert (rc == 0); + + /* start advertising */ + rc = ble_gap_ext_adv_start(instance, 0, 0); + assert (rc == 0); + + console_printf("instance %u started (scannable legacy PDUs)\n", instance); +} + +static int +scannable_ext_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + default: + break; + } + + return 0; +} + +/* this is simple scannable instance that runs forever + * TODO Get scan request notifications. + */ +static void +start_scannable_ext(void) +{ + struct ble_gap_ext_adv_params params; + struct os_mbuf *data; + uint8_t instance = 1; + ble_addr_t addr; + int rc; + + /* use defaults for non-set params */ + memset (¶ms, 0, sizeof(params)); + + /* enable scannable advertising */ + params.scannable = 1; + + /* enable scan request notification */ + params.scan_req_notif = 1; + + /* advertise using random addr */ + params.own_addr_type = BLE_OWN_ADDR_RANDOM; + + params.primary_phy = BLE_HCI_LE_PHY_1M; + params.secondary_phy = BLE_HCI_LE_PHY_1M; + params.tx_power = 127; + params.sid = 1; + + /* configure instance 0 */ + rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL, + scannable_ext_gap_event, NULL); + assert (rc == 0); + + /* set random (NRPA) address for instance */ + rc = ble_hs_id_gen_rnd(1, &addr); + assert (rc == 0); + + rc = ble_gap_ext_adv_set_addr(instance, &addr ); + assert (rc == 0); + + /* in this case only scan response is allowed */ + + /* get mbuf for scan rsp data */ + data = os_msys_get_pkthdr(sizeof(ext_adv_pattern_1), 0); + assert(data); + + /* fill mbuf with scan rsp data */ + rc = os_mbuf_append(data, ext_adv_pattern_1, sizeof(ext_adv_pattern_1)); + assert(rc == 0); + + rc = ble_gap_ext_adv_rsp_set_data(instance, data); + assert (rc == 0); + + /* start advertising */ + rc = ble_gap_ext_adv_start(instance, 0, 0); + assert (rc == 0); + + console_printf("instance %u started (scannable)\n", instance); +} + +/* this is simple non-connectable instance that runs forever */ +static void +start_non_connectable_ext(void) +{ + struct ble_gap_ext_adv_params params; + struct os_mbuf *data; + uint8_t instance = 0; + int rc; + + /* use defaults for non-set params */ + memset (¶ms, 0, sizeof(params)); + + /* advertise using ID addr */ + params.own_addr_type = id_addr_type; + + params.primary_phy = BLE_HCI_LE_PHY_1M; + params.secondary_phy = BLE_HCI_LE_PHY_1M; + params.tx_power = 127; + params.sid = 0; + + /* configure instance */ + rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL, NULL, NULL); + assert (rc == 0); + + /* in this case only advertisign data is allowed */ + + /* get mbuf for adv data */ + data = os_msys_get_pkthdr(sizeof(ext_adv_pattern_1), 0); + assert(data); + + /* fill mbuf with adv data */ + rc = os_mbuf_append(data, ext_adv_pattern_1, sizeof(ext_adv_pattern_1)); + assert(rc == 0); + + rc = ble_gap_ext_adv_set_data(instance, data); + assert (rc == 0); + + /* start advertising */ + rc = ble_gap_ext_adv_start(instance, 0, 0); + assert (rc == 0); + + console_printf("instance %u started (non-con non-scan)\n", instance); +} + +static void +on_sync(void) +{ + int rc; + + console_printf("Synced, starting advertising\n"); + + /* Make sure we have proper identity address set (public preferred) */ + rc = ble_hs_util_ensure_addr(0); + assert(rc == 0); + + /* configure global address */ + rc = ble_hs_id_infer_auto(0, &id_addr_type); + assert(rc == 0); + + start_non_connectable_ext(); + + start_scannable_ext(); + + start_scannable_legacy_ext(); + + start_legacy_duration(0, true); + + start_ext_max_events(0, true); +} + +/* + * main + * + * The main task for the project. This function initializes the packages, + * then starts serving events from default event queue. + * + * @return int NOTE: this function should never return! + */ +int +main(void) +{ + /* Initialize OS */ + sysinit(); + + console_printf("Extended Advertising sample application\n"); + + /* Set sync callback */ + ble_hs_cfg.sync_cb = on_sync; + + /* As the last thing, process events from default event queue */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} diff --git a/src/libs/mynewt-nimble/apps/ext_advertiser/src/patterns.h b/src/libs/mynewt-nimble/apps/ext_advertiser/src/patterns.h new file mode 100644 index 00000000..9485d0d4 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/ext_advertiser/src/patterns.h @@ -0,0 +1,186 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +static const uint8_t ext_adv_pattern_1[] = { + 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x08, 0x00, 0x0a, + 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, + 0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1e, + 0x00, 0x20, 0x00, 0x22, 0x00, 0x24, 0x00, 0x26, 0x00, 0x28, + 0x00, 0x2a, 0x00, 0x2c, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x32, + 0x00, 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, + 0x00, 0x3e, 0x00, 0x40, 0x00, 0x42, 0x00, 0x44, 0x00, 0x46, + 0x00, 0x48, 0x00, 0x4a, 0x00, 0x4c, 0x00, 0x4e, 0x00, 0x50, + 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, 0x00, 0x58, 0x00, 0x5a, + 0x00, 0x5c, 0x00, 0x5e, 0x00, 0x60, 0x00, 0x62, 0x00, 0x64, + 0x00, 0x66, 0x00, 0x68, 0x00, 0x6a, 0x00, 0x6c, 0x00, 0x6e, + 0x00, 0x70, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x78, + 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7e, 0x00, 0x80, 0x00, 0x82, + 0x00, 0x84, 0x00, 0x86, 0x00, 0x88, 0x00, 0x8a, 0x00, 0x8c, + 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, + 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, 0x9e, 0x00, 0xa0, + 0x00, 0xa2, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, + 0x00, 0xac, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, + 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbc, 0x00, 0xbe, + 0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc8, + 0x00, 0xca, 0x00, 0xcc, 0x00, 0xce, 0x00, 0xd0, 0x00, 0xd2, + 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd8, 0x00, 0xda, 0x00, 0xdc, + 0x00, 0xde, 0x00, 0xe0, 0x00, 0xe2, 0x00, 0xe4, 0x00, 0xe6, + 0x00, 0xe8, 0x00, 0xea, 0x00, 0xec, 0x00, 0xee, 0x00, 0xf0, + 0x00, 0xf2, 0x00, 0xf4, 0x00, 0xf6, 0x00, 0xf8, 0x00, 0xfa, + 0x00, 0xfc, 0x00, 0xfe, 0x01, 0x01, 0x01, 0x03, 0x01, 0x05, + 0x01, 0x07, 0x01, 0x09, 0x01, 0x0b, 0x01, 0x0d, 0x01, 0x0f, + 0x01, 0x11, 0x01, 0x13, 0x01, 0x15, 0x01, 0x17, 0x01, 0x19, + 0x01, 0x1b, 0x01, 0x1d, 0x01, 0x1f, 0x01, 0x21, 0x01, 0x23, + 0x01, 0x25, 0x01, 0x27, 0x01, 0x29, 0x01, 0x2b, 0x01, 0x2d, + 0x01, 0x2f, 0x01, 0x31, 0x01, 0x33, 0x01, 0x35, 0x01, 0x37, + 0x01, 0x39, 0x01, 0x3b, 0x01, 0x3d, 0x01, 0x3f, 0x01, 0x41, + 0x01, 0x43, 0x01, 0x45, 0x01, 0x47, 0x01, 0x49, 0x01, 0x4b, + 0x01, 0x4d, 0x01, 0x4f, 0x01, 0x51, 0x01, 0x53, 0x01, 0x55, + 0x01, 0x57, 0x01, 0x59, 0x01, 0x5b, 0x01, 0x5d, 0x01, 0x5f, + 0x01, 0x61, 0x01, 0x63, 0x01, 0x65, 0x01, 0x67, 0x01, 0x69, + 0x01, 0x6b, 0x01, 0x6d, 0x01, 0x6f, 0x01, 0x71, 0x01, 0x73, + 0x01, 0x75, 0x01, 0x77, 0x01, 0x79, 0x01, 0x7b, 0x01, 0x7d, + 0x01, 0x7f, 0x01, 0x81, 0x01, 0x83, 0x01, 0x85, 0x01, 0x87, + 0x01, 0x89, 0x01, 0x8b, 0x01, 0x8d, 0x01, 0x8f, 0x01, 0x91, + 0x01, 0x93, 0x01, 0x95, 0x01, 0x97, 0x01, 0x99, 0x01, 0x9b, + 0x01, 0x9d, 0x01, 0x9f, 0x01, 0xa1, 0x01, 0xa3, 0x01, 0xa5, + 0x01, 0xa7, 0x01, 0xa9, 0x01, 0xab, 0x01, 0xad, 0x01, 0xaf, + 0x01, 0xb1, 0x01, 0xb3, 0x01, 0xb5, 0x01, 0xb7, 0x01, 0xb9, + 0x01, 0xbb, 0x01, 0xbd, 0x01, 0xbf, 0x01, 0xc1, 0x01, 0xc3, + 0x01, 0xc5, 0x01, 0xc7, 0x01, 0xc9, 0x01, 0xcb, 0x01, 0xcd, + 0x01, 0xcf, 0x01, 0xd1, 0x01, 0xd3, 0x01, 0xd5, 0x01, 0xd7, + 0x01, 0xd9, 0x01, 0xdb, 0x01, 0xdd, 0x01, 0xdf, 0x01, 0xe1, + 0x01, 0xe3, 0x01, 0xe5, 0x01, 0xe7, 0x01, 0xe9, 0x01, 0xeb, + 0x01, 0xed, 0x01, 0xef, 0x01, 0xf1, 0x01, 0xf3, 0x01, 0xf5, + 0x01, 0xf7, 0x01, 0xf9, 0x01, 0xfb, 0x01, 0xfd, 0x02, 0x00, + 0x02, 0x02, 0x02, 0x04, 0x02, 0x06, 0x02, 0x08, 0x02, 0x0a, + 0x02, 0x0c, 0x02, 0x0e, 0x02, 0x10, 0x02, 0x12, 0x02, 0x14, + 0x02, 0x16, 0x02, 0x18, 0x02, 0x1a, 0x02, 0x1c, 0x02, 0x1e, + 0x02, 0x20, 0x02, 0x22, 0x02, 0x24, 0x02, 0x26, 0x02, 0x28, + 0x02, 0x2a, 0x02, 0x2c, 0x02, 0x2e, 0x02, 0x30, 0x02, 0x32, + 0x02, 0x34, 0x02, 0x36, 0x02, 0x38, 0x02, 0x3a, 0x02, 0x3c, + 0x02, 0x3e, 0x02, 0x40, 0x02, 0x42, 0x02, 0x44, 0x02, 0x46, + 0x02, 0x48, 0x02, 0x4a, 0x02, 0x4c, 0x02, 0x4e, 0x02, 0x50, + 0x02, 0x52, 0x02, 0x54, 0x02, 0x56, 0x02, 0x58, 0x02, 0x5a, + 0x02, 0x5c, 0x02, 0x5e, 0x02, 0x60, 0x02, 0x62, 0x02, 0x64, + 0x02, 0x66, 0x02, 0x68, 0x02, 0x6a, 0x02, 0x6c, 0x02, 0x6e, + 0x02, 0x70, 0x02, 0x72, 0x02, 0x74, 0x02, 0x76, 0x02, 0x78, + 0x02, 0x7a, 0x02, 0x7c, 0x02, 0x7e, 0x02, 0x80, 0x02, 0x82, + 0x02, 0x84, 0x02, 0x86, 0x02, 0x88, 0x02, 0x8a, 0x02, 0x8c, + 0x02, 0x8e, 0x02, 0x90, 0x02, 0x92, 0x02, 0x94, 0x02, 0x96, + 0x02, 0x98, 0x02, 0x9a, 0x02, 0x9c, 0x02, 0x9e, 0x02, 0xa0, + 0x02, 0xa2, 0x02, 0xa4, 0x02, 0xa6, 0x02, 0xa8, 0x02, 0xaa, + 0x02, 0xac, 0x02, 0xae, 0x02, 0xb0, 0x02, 0xb2, 0x02, 0xb4, + 0x02, 0xb6, 0x02, 0xb8, 0x02, 0xba, 0x02, 0xbc, 0x02, 0xbe, + 0x02, 0xc0, 0x02, 0xc2, 0x02, 0xc4, 0x02, 0xc6, 0x02, 0xc8, + 0x02, 0xca, 0x02, 0xcc, 0x02, 0xce, 0x02, 0xd0, 0x02, 0xd2, + 0x02, 0xd4, 0x02, 0xd6, 0x02, 0xd8, 0x02, 0xda, 0x02, 0xdc, + 0x02, 0xde, 0x02, 0xe0, 0x02, 0xe2, 0x02, 0xe4, 0x02, 0xe6, + 0x02, 0xe8, 0x02, 0xea, 0x02, 0xec, 0x02, 0xee, 0x02, 0xf0, + 0x02, 0xf2, 0x02, 0xf4, 0x02, 0xf6, 0x02, 0xf8, 0x02, 0xfa, + 0x02, 0xfc, 0x02, 0xfe, 0x03, 0x01, 0x03, 0x03, 0x03, 0x05, + 0x03, 0x07, 0x03, 0x09, 0x03, 0x0b, 0x03, 0x0d, 0x03, 0x0f, + 0x03, 0x11, 0x03, 0x13, 0x03, 0x15, 0x03, 0x17, 0x03, 0x19, + 0x03, 0x1b, 0x03, 0x1d, 0x03, 0x1f, 0x03, 0x21, 0x03, 0x23, + 0x03, 0x25, 0x03, 0x27, 0x03, 0x29, 0x03, 0x2b, 0x03, 0x2d, + 0x03, 0x2f, 0x03, 0x31, 0x03, 0x33, 0x03, 0x35, 0x03, 0x37, + 0x03, 0x39, 0x03, 0x3b, 0x03, 0x3d, 0x03, 0x3f, 0x03, 0x41, + 0x03, 0x43, 0x03, 0x45, 0x03, 0x47, 0x03, 0x49, 0x03, 0x4b, + 0x03, 0x4d, 0x03, 0x4f, 0x03, 0x51, 0x03, 0x53, 0x03, 0x55, + 0x03, 0x57, 0x03, 0x59, 0x03, 0x5b, 0x03, 0x5d, 0x03, 0x5f, + 0x03, 0x61, 0x03, 0x63, 0x03, 0x65, 0x03, 0x67, 0x03, 0x69, + 0x03, 0x6b, 0x03, 0x6d, 0x03, 0x6f, 0x03, 0x71, 0x03, 0x73, + 0x03, 0x75, 0x03, 0x77, 0x03, 0x79, 0x03, 0x7b, 0x03, 0x7d, + 0x03, 0x7f, 0x03, 0x81, 0x03, 0x83, 0x03, 0x85, 0x03, 0x87, + 0x03, 0x89, 0x03, 0x8b, 0x03, 0x8d, 0x03, 0x8f, 0x03, 0x91, + 0x03, 0x93, 0x03, 0x95, 0x03, 0x97, 0x03, 0x99, 0x03, 0x9b, + 0x03, 0x9d, 0x03, 0x9f, 0x03, 0xa1, 0x03, 0xa3, 0x03, 0xa5, + 0x03, 0xa7, 0x03, 0xa9, 0x03, 0xab, 0x03, 0xad, 0x03, 0xaf, + 0x03, 0xb1, 0x03, 0xb3, 0x03, 0xb5, 0x03, 0xb7, 0x03, 0xb9, + 0x03, 0xbb, 0x03, 0xbd, 0x03, 0xbf, 0x03, 0xc1, 0x03, 0xc3, + 0x03, 0xc5, 0x03, 0xc7, 0x03, 0xc9, 0x03, 0xcb, 0x03, 0xcd, + 0x03, 0xcf, 0x03, 0xd1, 0x03, 0xd3, 0x03, 0xd5, 0x03, 0xd7, + 0x03, 0xd9, 0x03, 0xdb, 0x03, 0xdd, 0x03, 0xdf, 0x03, 0xe1, + 0x03, 0xe3, 0x03, 0xe5, 0x03, 0xe7, 0x03, 0xe9, 0x03, 0xeb, + 0x03, 0xed, 0x03, 0xef, 0x03, 0xf1, 0x03, 0xf3, 0x03, 0xf5, + 0x03, 0xf7, 0x03, 0xf9, 0x03, 0xfb, 0x03, 0xfd, 0x04, 0x00, + 0x04, 0x02, 0x04, 0x04, 0x04, 0x06, 0x04, 0x08, 0x04, 0x0a, + 0x04, 0x0c, 0x04, 0x0e, 0x04, 0x10, 0x04, 0x12, 0x04, 0x14, + 0x04, 0x16, 0x04, 0x18, 0x04, 0x1a, 0x04, 0x1c, 0x04, 0x1e, + 0x04, 0x20, 0x04, 0x22, 0x04, 0x24, 0x04, 0x26, 0x04, 0x28, + 0x04, 0x2a, 0x04, 0x2c, 0x04, 0x2e, 0x04, 0x30, 0x04, 0x32, + 0x04, 0x34, 0x04, 0x36, 0x04, 0x38, 0x04, 0x3a, 0x04, 0x3c, + 0x04, 0x3e, 0x04, 0x40, 0x04, 0x42, 0x04, 0x44, 0x04, 0x46, + 0x04, 0x48, 0x04, 0x4a, 0x04, 0x4c, 0x04, 0x4e, 0x04, 0x50, + 0x04, 0x52, 0x04, 0x54, 0x04, 0x56, 0x04, 0x58, 0x04, 0x5a, + 0x04, 0x5c, 0x04, 0x5e, 0x04, 0x60, 0x04, 0x62, 0x04, 0x64, + 0x04, 0x66, 0x04, 0x68, 0x04, 0x6a, 0x04, 0x6c, 0x04, 0x6e, + 0x04, 0x70, 0x04, 0x72, 0x04, 0x74, 0x04, 0x76, 0x04, 0x78, + 0x04, 0x7a, 0x04, 0x7c, 0x04, 0x7e, 0x04, 0x80, 0x04, 0x82, + 0x04, 0x84, 0x04, 0x86, 0x04, 0x88, 0x04, 0x8a, 0x04, 0x8c, + 0x04, 0x8e, 0x04, 0x90, 0x04, 0x92, 0x04, 0x94, 0x04, 0x96, + 0x04, 0x98, 0x04, 0x9a, 0x04, 0x9c, 0x04, 0x9e, 0x04, 0xa0, + 0x04, 0xa2, 0x04, 0xa4, 0x04, 0xa6, 0x04, 0xa8, 0x04, 0xaa, + 0x04, 0xac, 0x04, 0xae, 0x04, 0xb0, 0x04, 0xb2, 0x04, 0xb4, + 0x04, 0xb6, 0x04, 0xb8, 0x04, 0xba, 0x04, 0xbc, 0x04, 0xbe, + 0x04, 0xc0, 0x04, 0xc2, 0x04, 0xc4, 0x04, 0xc6, 0x04, 0xc8, + 0x04, 0xca, 0x04, 0xcc, 0x04, 0xce, 0x04, 0xd0, 0x04, 0xd2, + 0x04, 0xd4, 0x04, 0xd6, 0x04, 0xd8, 0x04, 0xda, 0x04, 0xdc, + 0x04, 0xde, 0x04, 0xe0, 0x04, 0xe2, 0x04, 0xe4, 0x04, 0xe6, + 0x04, 0xe8, 0x04, 0xea, 0x04, 0xec, 0x04, 0xee, 0x04, 0xf0, + 0x04, 0xf2, 0x04, 0xf4, 0x04, 0xf6, 0x04, 0xf8, 0x04, 0xfa, + 0x04, 0xfc, 0x04, 0xfe, 0x05, 0x01, 0x05, 0x03, 0x05, 0x05, + 0x05, 0x07, 0x05, 0x09, 0x05, 0x0b, 0x05, 0x0d, 0x05, 0x0f, + 0x05, 0x11, 0x05, 0x13, 0x05, 0x15, 0x05, 0x17, 0x05, 0x19, + 0x05, 0x1b, 0x05, 0x1d, 0x05, 0x1f, 0x05, 0x21, 0x05, 0x23, + 0x05, 0x25, 0x05, 0x27, 0x05, 0x29, 0x05, 0x2b, 0x05, 0x2d, + 0x05, 0x2f, 0x05, 0x31, 0x05, 0x33, 0x05, 0x35, 0x05, 0x37, + 0x05, 0x39, 0x05, 0x3b, 0x05, 0x3d, 0x05, 0x3f, 0x05, 0x41, + 0x05, 0x43, 0x05, 0x45, 0x05, 0x47, 0x05, 0x49, 0x05, 0x4b, + 0x05, 0x4d, 0x05, 0x4f, 0x05, 0x51, 0x05, 0x53, 0x05, 0x55, + 0x05, 0x57, 0x05, 0x59, 0x05, 0x5b, 0x05, 0x5d, 0x05, 0x5f, + 0x05, 0x61, 0x05, 0x63, 0x05, 0x65, 0x05, 0x67, 0x05, 0x69, + 0x05, 0x6b, 0x05, 0x6d, 0x05, 0x6f, 0x05, 0x71, 0x05, 0x73, + 0x05, 0x75, 0x05, 0x77, 0x05, 0x79, 0x05, 0x7b, 0x05, 0x7d, + 0x05, 0x7f, 0x05, 0x81, 0x05, 0x83, 0x05, 0x85, 0x05, 0x87, + 0x05, 0x89, 0x05, 0x8b, 0x05, 0x8d, 0x05, 0x8f, 0x05, 0x91, + 0x05, 0x93, 0x05, 0x95, 0x05, 0x97, 0x05, 0x99, 0x05, 0x9b, + 0x05, 0x9d, 0x05, 0x9f, 0x05, 0xa1, 0x05, 0xa3, 0x05, 0xa5, + 0x05, 0xa7, 0x05, 0xa9, 0x05, 0xab, 0x05, 0xad, 0x05, 0xaf, + 0x05, 0xb1, 0x05, 0xb3, 0x05, 0xb5, 0x05, 0xb7, 0x05, 0xb9, + 0x05, 0xbb, 0x05, 0xbd, 0x05, 0xbf, 0x05, 0xc1, 0x05, 0xc3, + 0x05, 0xc5, 0x05, 0xc7, 0x05, 0xc9, 0x05, 0xcb, 0x05, 0xcd, + 0x05, 0xcf, 0x05, 0xd1, 0x05, 0xd3, 0x05, 0xd5, 0x05, 0xd7, + 0x05, 0xd9, 0x05, 0xdb, 0x05, 0xdd, 0x05, 0xdf, 0x05, 0xe1, + 0x05, 0xe3, 0x05, 0xe5, 0x05, 0xe7, 0x05, 0xe9, 0x05, 0xeb, + 0x05, 0xed, 0x05, 0xef, 0x05, 0xf1, 0x05, 0xf3, 0x05, 0xf5, + 0x05, 0xf7, 0x05, 0xf9, 0x05, 0xfb, 0x05, 0xfd, 0x06, 0x00, + 0x06, 0x02, 0x06, 0x04, 0x06, 0x06, 0x06, 0x08, 0x06, 0x0a, + 0x06, 0x0c, 0x06, 0x0e, 0x06, 0x10, 0x06, 0x12, 0x06, 0x14, + 0x06, 0x16, 0x06, 0x18, 0x06, 0x1a, 0x06, 0x1c, 0x06, 0x1e, + 0x06, 0x20, 0x06, 0x22, 0x06, 0x24, 0x06, 0x26, 0x06, 0x28, + 0x06, 0x2a, 0x06, 0x2c, 0x06, 0x2e, 0x06, 0x30, 0x06, 0x32, + 0x06, 0x34, 0x06, 0x36, 0x06, 0x38, 0x06, 0x3a, 0x06, 0x3c, + 0x06, 0x3e, 0x06, 0x40, 0x06, 0x42, 0x06, 0x44, 0x06, 0x46, + 0x06, 0x48, 0x06, 0x4a, 0x06, 0x4c, 0x06, 0x4e, 0x06, 0x50, + 0x06, 0x52, 0x06, 0x54, 0x06, 0x56, 0x06, 0x58, 0x06, 0x5a, + 0x06, 0x5c, 0x06, 0x5e, 0x06, 0x60, 0x06, 0x62, 0x06, 0x64, + 0x06, 0x66, 0x06, 0x68, 0x06, 0x6a, 0x06, 0x6c, 0x06, 0x6e, + 0x06, 0x70, 0x06, 0x72, 0x06, 0x74, 0x06, 0x76, 0x06, 0x78 +}; diff --git a/src/libs/mynewt-nimble/apps/ext_advertiser/syscfg.yml b/src/libs/mynewt-nimble/apps/ext_advertiser/syscfg.yml new file mode 100644 index 00000000..0702ea79 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/ext_advertiser/syscfg.yml @@ -0,0 +1,45 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +syscfg.vals: + # Disable not used GAP roles (we only do non-connectable + # advertising here) + BLE_ROLE_BROADCASTER: 1 + BLE_ROLE_CENTRAL: 0 + BLE_ROLE_OBSERVER: 0 + BLE_ROLE_PERIPHERAL: 0 + + # Disable unused eddystone featdure. + BLE_EDDYSTONE: 0 + + # Enable Extended Advertising + BLE_EXT_ADV: 1 + + # Max advertising data size + BLE_EXT_ADV_MAX_SIZE: 1650 + + # Number of multi-advertising instances. Note that due + # to historical reasonds total number of advertising + # instances is BLE_MULTI_ADV_INSTANCES + 1 as instance + # 0 is always available + BLE_MULTI_ADV_INSTANCES: 4 + + # Controller uses msys pool for storing advertising data and scan responses. + # Since we advertise a lot of data (~4k in total) at the same time we need + # to increase block count. + MSYS_1_BLOCK_COUNT: 24 diff --git a/src/libs/mynewt-nimble/apps/peripheral/pkg.yml b/src/libs/mynewt-nimble/apps/peripheral/pkg.yml new file mode 100755 index 00000000..6167edab --- /dev/null +++ b/src/libs/mynewt-nimble/apps/peripheral/pkg.yml @@ -0,0 +1,37 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: "apps/peripheral" +pkg.type: app +pkg.description: "Basic perihperal application" +pkg.author: "Krzysztof Kopyściński krzysztof.kopyscinski@codecoup.pl" + + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-nimble/nimble/host" + - "@apache-mynewt-nimble/nimble/host/util/" + - "@apache-mynewt-nimble/nimble/host/services/gap" + - "@apache-mynewt-nimble/nimble/host/store/config" + - "@apache-mynewt-nimble/nimble/transport" + diff --git a/src/libs/mynewt-nimble/apps/peripheral/src/main.c b/src/libs/mynewt-nimble/apps/peripheral/src/main.c new file mode 100755 index 00000000..f22579a7 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/peripheral/src/main.c @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include + +#include "os/os.h" +#include "sysinit/sysinit.h" +#include "log/log.h" +#include "host/ble_hs.h" +#include "host/util/util.h" +#include "services/gap/ble_svc_gap.h" + +static uint8_t g_own_addr_type; +static uint16_t conn_handle; +static const char *device_name = "Mynewt"; + +/* adv_event() calls advertise(), so forward declaration is required */ +static void advertise(void); + +static int +adv_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_ADV_COMPLETE: + MODLOG_DFLT(INFO,"Advertising completed, termination code: %d\n", + event->adv_complete.reason); + advertise(); + break; + case BLE_GAP_EVENT_CONNECT: + assert(event->connect.status == 0); + MODLOG_DFLT(INFO, "connection %s; status=%d\n", + event->connect.status == 0 ? "established" : "failed", + event->connect.status); + break; + case BLE_GAP_EVENT_CONN_UPDATE_REQ: + /* connected device requests update of connection parameters, + and these are being filled in - NULL sets default values */ + MODLOG_DFLT(INFO, "updating conncetion parameters...\n"); + event->conn_update_req.conn_handle = conn_handle; + event->conn_update_req.peer_params = NULL; + MODLOG_DFLT(INFO, "connection parameters updated!\n"); + break; + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "disconnect; reason=%d\n", + event->disconnect.reason); + + /* reset conn_handle */ + conn_handle = BLE_HS_CONN_HANDLE_NONE; + + /* Connection terminated; resume advertising */ + advertise(); + break; + default: + MODLOG_DFLT(ERROR, "Advertising event not handled," + "event code: %u\n", event->type); + break; + } + return 0; +} + +static void +advertise(void) +{ + int rc; + + /* set adv parameters */ + struct ble_gap_adv_params adv_params; + struct ble_hs_adv_fields fields; + /* advertising payload is split into advertising data and advertising + response, because all data cannot fit into single packet; name of device + is sent as response to scan request */ + struct ble_hs_adv_fields rsp_fields; + + /* fill all fields and parameters with zeros */ + memset(&adv_params, 0, sizeof(adv_params)); + memset(&fields, 0, sizeof(fields)); + memset(&rsp_fields, 0, sizeof(rsp_fields)); + + adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; + adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + + fields.flags = BLE_HS_ADV_F_DISC_GEN | + BLE_HS_ADV_F_BREDR_UNSUP; + fields.uuids128 = BLE_UUID128(BLE_UUID128_DECLARE( + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff)); + fields.num_uuids128 = 1; + fields.uuids128_is_complete = 0;; + fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + + rsp_fields.name = (uint8_t *)device_name; + rsp_fields.name_len = strlen(device_name); + rsp_fields.name_is_complete = 1; + + rc = ble_gap_adv_set_fields(&fields); + assert(rc == 0); + + rc = ble_gap_adv_rsp_set_fields(&rsp_fields); + + MODLOG_DFLT(INFO,"Starting advertising...\n"); + + rc = ble_gap_adv_start(g_own_addr_type, NULL, 100, + &adv_params, adv_event, NULL); + assert(rc == 0); +} + +static void +on_sync(void) +{ + int rc; + + /* g_own_addr_type will store type of addres our BSP uses */ + rc = ble_hs_util_ensure_addr(0); + assert(rc == 0); + rc = ble_hs_id_infer_auto(0, &g_own_addr_type); + assert(rc == 0); + /* begin advertising */ + advertise(); +} + +static void +on_reset(int reason) +{ + MODLOG_DFLT(INFO, "Resetting state; reason=%d\n", reason); +} + +int +main(int argc, char **argv) +{ + int rc; + + /* Initialize all packages. */ + sysinit(); + + ble_hs_cfg.sync_cb = on_sync; + ble_hs_cfg.reset_cb = on_reset; + + rc = ble_svc_gap_device_name_set(device_name); + assert(rc == 0); + + /* As the last thing, process events from default event queue. */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + + return 0; +} diff --git a/src/libs/mynewt-nimble/apps/scanner/pkg.yml b/src/libs/mynewt-nimble/apps/scanner/pkg.yml new file mode 100644 index 00000000..15c2adbb --- /dev/null +++ b/src/libs/mynewt-nimble/apps/scanner/pkg.yml @@ -0,0 +1,35 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: "apps/scanner" +pkg.type: app +pkg.description: "Basic scanning application" +pkg.author: "Krzysztof Kopyściński " + + +pkg.deps: + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/stats/full" + - "@apache-mynewt-core/sys/log/modlog" + - "@apache-mynewt-nimble/nimble/host" + - "@apache-mynewt-nimble/nimble/host/util/" + - "@apache-mynewt-nimble/nimble/host/store/config" + - "@apache-mynewt-nimble/nimble/transport" \ No newline at end of file diff --git a/src/libs/mynewt-nimble/apps/scanner/src/main.c b/src/libs/mynewt-nimble/apps/scanner/src/main.c new file mode 100644 index 00000000..d21bba4b --- /dev/null +++ b/src/libs/mynewt-nimble/apps/scanner/src/main.c @@ -0,0 +1,261 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "sysinit/sysinit.h" +#include "os/os.h" +#include "console/console.h" +#include "host/ble_hs.h" +#include "host/util/util.h" +#include "console/console.h" +#include "log/log.h" + +/* scan_event() calls scan(), so forward declaration is required */ +static void scan(void); + +static void +ble_app_set_addr(void) +{ + ble_addr_t addr; + int rc; + + /* generate new non-resolvable private address */ + rc = ble_hs_id_gen_rnd(1, &addr); + assert(rc == 0); + + /* set generated address */ + rc = ble_hs_id_set_rnd(addr.val); + assert(rc == 0); +} + +static void +print_uuid(const ble_uuid_t *uuid) +{ + char buf[BLE_UUID_STR_LEN]; + + MODLOG_DFLT(DEBUG, "%s", ble_uuid_to_str(uuid, buf)); +} + +/* Utility function to log an array of bytes. */ +static void +print_bytes(const uint8_t *bytes, int len) +{ + int i; + + for (i = 0; i < len; i++) { + MODLOG_DFLT(DEBUG, "%s0x%02x", i != 0 ? ":" : "", bytes[i]); + } +} + +static char * +addr_str(const void *addr) +{ + static char buf[6 * 2 + 5 + 1]; + const uint8_t *u8p; + + u8p = addr; + sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", + u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); + + return buf; +} + +static void +print_adv_fields(const struct ble_hs_adv_fields *fields) +{ + char s[BLE_HS_ADV_MAX_SZ]; + const uint8_t *u8p; + int i; + + if (fields->flags != 0) { + MODLOG_DFLT(DEBUG, " flags=0x%02x\n", fields->flags); + } + + if (fields->uuids16 != NULL) { + MODLOG_DFLT(DEBUG, " uuids16(%scomplete)=", + fields->uuids16_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids16; i++) { + print_uuid(&fields->uuids16[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uuids32 != NULL) { + MODLOG_DFLT(DEBUG, " uuids32(%scomplete)=", + fields->uuids32_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids32; i++) { + print_uuid(&fields->uuids32[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uuids128 != NULL) { + MODLOG_DFLT(DEBUG, " uuids128(%scomplete)=", + fields->uuids128_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids128; i++) { + print_uuid(&fields->uuids128[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->name != NULL) { + assert(fields->name_len < sizeof s - 1); + memcpy(s, fields->name, fields->name_len); + s[fields->name_len] = '\0'; + MODLOG_DFLT(DEBUG, " name(%scomplete)=%s\n", + fields->name_is_complete ? "" : "in", s); + } + + if (fields->tx_pwr_lvl_is_present) { + MODLOG_DFLT(DEBUG, " tx_pwr_lvl=%d\n", fields->tx_pwr_lvl); + } + + if (fields->slave_itvl_range != NULL) { + MODLOG_DFLT(DEBUG, " slave_itvl_range="); + print_bytes(fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->svc_data_uuid16 != NULL) { + MODLOG_DFLT(DEBUG, " svc_data_uuid16="); + print_bytes(fields->svc_data_uuid16, fields->svc_data_uuid16_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->public_tgt_addr != NULL) { + MODLOG_DFLT(DEBUG, " public_tgt_addr="); + u8p = fields->public_tgt_addr; + for (i = 0; i < fields->num_public_tgt_addrs; i++) { + MODLOG_DFLT(DEBUG, "public_tgt_addr=%s ", addr_str(u8p)); + u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN; + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->appearance_is_present) { + MODLOG_DFLT(DEBUG, " appearance=0x%04x\n", fields->appearance); + } + + if (fields->adv_itvl_is_present) { + MODLOG_DFLT(DEBUG, " adv_itvl=0x%04x\n", fields->adv_itvl); + } + + if (fields->svc_data_uuid32 != NULL) { + MODLOG_DFLT(DEBUG, " svc_data_uuid32="); + print_bytes(fields->svc_data_uuid32, fields->svc_data_uuid32_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->svc_data_uuid128 != NULL) { + MODLOG_DFLT(DEBUG, " svc_data_uuid128="); + print_bytes(fields->svc_data_uuid128, fields->svc_data_uuid128_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uri != NULL) { + MODLOG_DFLT(DEBUG, " uri="); + print_bytes(fields->uri, fields->uri_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->mfg_data != NULL) { + MODLOG_DFLT(DEBUG, " mfg_data="); + print_bytes(fields->mfg_data, fields->mfg_data_len); + MODLOG_DFLT(DEBUG, "\n"); + } +} + +static int +scan_event(struct ble_gap_event *event, void *arg) +{ + struct ble_hs_adv_fields fields; + int rc; + switch (event->type) { + /* advertising report has been received during discovery procedure */ + case BLE_GAP_EVENT_DISC: + MODLOG_DFLT(ERROR, "Advertising report received!\n"); + rc = ble_hs_adv_parse_fields(&fields, event->disc.data, + event->disc.length_data); + if (rc != 0) { + return 0; + } + print_adv_fields(&fields); + return 0; + /* discovery procedure has terminated */ + case BLE_GAP_EVENT_DISC_COMPLETE: + MODLOG_DFLT(INFO, "Discovery completed, terminaton code: %d\n", + event->disc_complete.reason); + scan(); + return 0; + default: + MODLOG_DFLT(ERROR, "Discovery event not handled\n"); + return 0; + } +} + +static void +scan(void) +{ + /* set scan parameters */ + struct ble_gap_disc_params scan_params; + scan_params.itvl = 500; + scan_params.window = 250; + scan_params.filter_policy = 0; + scan_params.limited = 0; + scan_params.passive = 1; + scan_params.filter_duplicates = 1; + /* performs discovery procedure; value of own_addr_type is hard-coded, + because NRPA is used */ + ble_gap_disc(BLE_OWN_ADDR_RANDOM, 1000, &scan_params, scan_event, NULL); +} + +static void +on_sync(void) +{ + /* Generate a non-resolvable private address. */ + ble_app_set_addr(); + + /* begin scanning */ + scan(); +} + +static void +on_reset(int reason) +{ + console_printf("Resetting state; reason=%d\n", reason); +} + +int +main(int argc, char **argv) +{ + /* Initialize all packages. */ + sysinit(); + + ble_hs_cfg.sync_cb = on_sync; + ble_hs_cfg.reset_cb = on_reset; + + /* As the last thing, process events from default event queue. */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + + return 0; +} diff --git a/src/libs/mynewt-nimble/docs/.gitignore b/src/libs/mynewt-nimble/docs/.gitignore new file mode 100644 index 00000000..2abe8a03 --- /dev/null +++ b/src/libs/mynewt-nimble/docs/.gitignore @@ -0,0 +1,5 @@ +xml +node_modules +_build +doxygen_* +*.pyc diff --git a/src/libs/mynewt-nimble/docs/Makefile b/src/libs/mynewt-nimble/docs/Makefile new file mode 100644 index 00000000..9c8793a1 --- /dev/null +++ b/src/libs/mynewt-nimble/docs/Makefile @@ -0,0 +1,25 @@ +# Make a preview site for Sphinx & Doxygen output + +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SPHINXPROJ = Mynewt +SOURCEDIR = . +BUILDDIR = _build/sphinx + +.PHONY: Makefile clean preview doxygen + +clean: + rm -rf _build + +preview: _build doxygen sphinx + +_build: + mkdir -p _build + +doxygen: + mkdir -p _build/html + cd .. && doxygen docs/doxygen.xml + +sphinx: + sphinx-build . _build/sphinx + mv _build/sphinx _build/html/documentation diff --git a/src/libs/mynewt-nimble/docs/README.rst b/src/libs/mynewt-nimble/docs/README.rst new file mode 100644 index 00000000..ef2871c6 --- /dev/null +++ b/src/libs/mynewt-nimble/docs/README.rst @@ -0,0 +1,33 @@ +NimBLE Bluetooth Stack Documentation +################################# + +This folder holds the documentation for the NimBLE Bluetooth stack from the +`Apache Mynewt`_ project. It is built using `Sphinx`_. +The source code also contains inline comments in `Doxygen`_ +format to document the APIs. + +The complete project documentation can be found at `mynewt documentation`_ + +.. contents:: + +Writing Documentation +======================= + +See: https://github.com/apache/mynewt-documentation#writing-documentation + +Previewing Changes +========================== + +In order to preview any changes you make you must first install a Sphinx +toolchain as described at https://github.com/apache/mynewt-documentation#id3. + Then: + +.. code-block:: bash + + $ cd docs + $ make clean && make preview && (cd _build/html && python -m SimpleHTTPServer 8080) + +.. _Apache Mynewt: https://mynewt.apache.org/ +.. _mynewt documentation: https://github.com/apache/mynewt-documentation +.. _Sphinx: http://www.sphinx-doc.org/ +.. _Doxygen: http://www.doxygen.org/ diff --git a/src/libs/mynewt-nimble/docs/ble_hs/ble_att.rst b/src/libs/mynewt-nimble/docs/ble_hs/ble_att.rst new file mode 100644 index 00000000..2025784d --- /dev/null +++ b/src/libs/mynewt-nimble/docs/ble_hs/ble_att.rst @@ -0,0 +1,22 @@ +NimBLE Host ATT Client Reference +-------------------------------- + +Introduction +~~~~~~~~~~~~ + +The Attribute Protocol (ATT) is a mid-level protocol that all BLE devices use to exchange data. Data is exchanged when +an ATT client reads or writes an attribute belonging to an ATT server. Any device that needs to send or receive data +must support both the client and server functionality of the ATT protocol. The only devices which do not support ATT +are the most basic ones: broadcasters and observers (i.e., beaconing devices and listening devices). + +Most ATT functionality is not interesting to an application. Rather than use ATT directly, an application uses the +higher level GATT profile, which sits directly above ATT in the host. NimBLE exposes the few bits of ATT functionality +which are not encompassed by higher level GATT functions. This section documents the ATT functionality that the NimBLE +host exposes to the application. + +API +~~~~~~ + +.. doxygengroup:: bt_host + :content-only: + :members: diff --git a/src/libs/mynewt-nimble/docs/ble_hs/ble_gap.rst b/src/libs/mynewt-nimble/docs/ble_hs/ble_gap.rst new file mode 100644 index 00000000..d5c99854 --- /dev/null +++ b/src/libs/mynewt-nimble/docs/ble_hs/ble_gap.rst @@ -0,0 +1,14 @@ +NimBLE Host GAP Reference +------------------------- + +Introduction +~~~~~~~~~~~~ + +The Generic Access Profile (GAP) is responsible for all connecting, advertising, scanning, and connection updating operations. + +API +~~~~~~ + +.. doxygengroup:: bt_host_gap + :content-only: + :members: diff --git a/src/libs/mynewt-nimble/docs/ble_hs/ble_gattc.rst b/src/libs/mynewt-nimble/docs/ble_hs/ble_gattc.rst new file mode 100644 index 00000000..4668c5d9 --- /dev/null +++ b/src/libs/mynewt-nimble/docs/ble_hs/ble_gattc.rst @@ -0,0 +1,15 @@ +NimBLE Host GATT Client Reference +--------------------------------- + +Introduction +~~~~~~~~~~~~ + +The Generic Attribute Profile (GATT) manages all activities involving services, characteristics, and descriptors. The +client half of the GATT API initiates GATT procedures. + +API +~~~~~~ + +.. doxygengroup:: bt_gatt + :content-only: + :members: diff --git a/src/libs/mynewt-nimble/docs/ble_hs/ble_gatts.rst b/src/libs/mynewt-nimble/docs/ble_hs/ble_gatts.rst new file mode 100644 index 00000000..0a823f0b --- /dev/null +++ b/src/libs/mynewt-nimble/docs/ble_hs/ble_gatts.rst @@ -0,0 +1,15 @@ +NimBLE Host GATT Server Reference +--------------------------------- + +Introduction +~~~~~~~~~~~~ + +The Generic Attribute Profile (GATT) manages all activities involving services, characteristics, and descriptors. The +server half of the GATT API handles registration and responding to GATT clients. + +API +~~~~~~ + +.. doxygengroup:: bt_gatt + :content-only: + :members: diff --git a/src/libs/mynewt-nimble/docs/ble_hs/ble_hs.rst b/src/libs/mynewt-nimble/docs/ble_hs/ble_hs.rst new file mode 100644 index 00000000..844ede1e --- /dev/null +++ b/src/libs/mynewt-nimble/docs/ble_hs/ble_hs.rst @@ -0,0 +1,27 @@ +NimBLE Host +----------- + +Introduction +~~~~~~~~~~~~ + +At a high level, the NimBLE stack is divided into two components: + +- Host +- Controller + +This document is an API reference for the host component. If you are +interested in the general structure of the NimBLE stack and its non-host +components, you might want to read the :doc:`../index`. + +The host sits directly below the application, and it serves as the +interface to the application for all BLE operations. + +.. toctree:: + :titlesonly: + + Return Codes + GAP + GATT Client + GATT Server + Identity + ATT diff --git a/src/libs/mynewt-nimble/docs/ble_hs/ble_hs_id.rst b/src/libs/mynewt-nimble/docs/ble_hs/ble_hs_id.rst new file mode 100644 index 00000000..dbb47c94 --- /dev/null +++ b/src/libs/mynewt-nimble/docs/ble_hs/ble_hs_id.rst @@ -0,0 +1,45 @@ +NimBLE Host Identity Reference +------------------------------ + +Introduction +~~~~~~~~~~~~ + +The identity API provides facilities for querying and configuring your device's addresses. BLE's addressing scheme is +quite involved; the summary that follows is only a brief introduction. + +BLE defines four address types: + ++---------------------------------+---------------------------------------------------------------------------------------------------+-------------+----------------------------------------------+ +| Type | Description | Identity? | Configured with | ++=================================+===================================================================================================+=============+==============================================+ +| Public | Address assigned by manufacturer; the three most significant bytes form the manufacturer's OUI. | Yes | N/A; read from controller at startup. | ++---------------------------------+---------------------------------------------------------------------------------------------------+-------------+----------------------------------------------+ +| Static random | Randomly generated address. | Yes | *ble_hs_id_set_rnd()* | ++---------------------------------+---------------------------------------------------------------------------------------------------+-------------+----------------------------------------------+ +| Resolvable private (RPA) | Address randomly generated from an identity address and an identity resolving key (IRK). | No | N/A; generated by controller periodically. | ++---------------------------------+---------------------------------------------------------------------------------------------------+-------------+----------------------------------------------+ +| Non-resolvable private (NRPA) | Randomly generated address. | No | *ble_hs_id_set_rnd()* | ++---------------------------------+---------------------------------------------------------------------------------------------------+-------------+----------------------------------------------+ + +Identity Addresses +^^^^^^^^^^^^^^^^^^ + +The third column in the above table indicates the *identity* property of each address type. An identity address never +changes, and a device can be identified by one of its unique identity addresses. + +Non-identity addresses are used by devices supporting BLE privacy. A device using the privacy feature frequently changes +its own address to a newly-generated non-identity address. By cycling its address, the device makes it impossible for +eavesdroppers to track its location. + +A device can have up to two identity addresses at once: one public and one static random. As indicated in the above table, +the public identity address cannot be configured; the static random identity address can be set by calling *ble_hs_id_set_rnd()*. + +The address type is selected on a per-GAP-procedure basis. Each time you initiate a GAP procedure, you indicate which +address type the device should use for the duration of the procedure. + +Header +~~~~~~ + +.. code-block:: cpp + + #include "host/ble_hs.h" diff --git a/src/libs/mynewt-nimble/docs/ble_hs/ble_hs_return_codes.rst b/src/libs/mynewt-nimble/docs/ble_hs/ble_hs_return_codes.rst new file mode 100644 index 00000000..c69cc4f8 --- /dev/null +++ b/src/libs/mynewt-nimble/docs/ble_hs/ble_hs_return_codes.rst @@ -0,0 +1,437 @@ +NimBLE Host Return Codes +------------------------ + +.. contents:: + :local: + :depth: 2 + +Introduction +~~~~~~~~~~~~ + +Summary +^^^^^^^ + +The NimBLE host reports status to the application via a set of return codes. The host encompasses several layers of the Bluetooth specification that each defines its own set of status codes. Rather than "abstract away" information from lower layers that the application developer might find useful, the NimBLE host aims to indicate precisely what happened when something fails. Consequently, the host utilizes a rather large set of return codes. + +A return code of 0 indicates success. For failure conditions, the return codes are partitioned into five separate sets: + ++---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Set | Condition | ++===========================+=============================================================================================================================================================================================================+ +| Core | Errors detected internally by the NimBLE host. | ++---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ATT | The ATT server has reported a failure via the transmission of an ATT Error Response. The return code corresponds to the value of the Error Code field in the response. | ++---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| HCI | The controller has reported an error to the host via a command complete or command status HCI event. The return code corresponds to the value of the Status field in the event. | ++---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| L2CAP | An L2CAP signaling procedure has failed and an L2CAP Command Reject was sent as a result. The return code corresponds to the value of the Reason field in the command. | ++---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Security manager (us) | The host detected an error during a security manager procedure and sent a Pairing Failed command to the peer. The return code corresponds to the value of the Reason field in the Pairing Failed command. | ++---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Security manager (peer) | A security manager procedure failed because the peer sent us a Pairing Failed command. The return code corresponds to the value of the Reason field in the Pairing Failed command. | ++---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +The return codes in the core set are defined by the NimBLE Host. The other sets are defined in the Bluetooth specification; the codes in this latter group are referred to as *formal status codes*. As defined in the Bluetooth specification, the formal status code sets are not disjoint. That is, they overlap. For example, the spec defines a status code of 1 to have all of the following meanings: + ++---------+----------------------------+ +| Layer | Meaning | ++=========+============================+ +| ATT | Invalid handle. | ++---------+----------------------------+ +| HCI | Unknown HCI command. | ++---------+----------------------------+ +| L2CAP | Signalling MTU exceeded. | ++---------+----------------------------+ +| SM | Passkey entry failed. | ++---------+----------------------------+ + +Clearly, the host can't just return an unadorned formal status code and expect the application to make sense of it. To resolve this ambiguity, the NimBLE host divides the full range of an int into several subranges. Each subrange corresponds to one of the five return code sets. For example, the ATT set is mapped onto the subrange *[0x100, 0x200)*. To indicate an ATT error of 3 (write not permitted), the NimBLE host returns a value 0x103 to the application. + +The host defines a set of convenience macros for converting from a formal status code to NimBLE host status code. These macros are documented in the table below. + ++----------------------------+---------------------------+--------------+ +| Macro | Status code set | Base value | ++============================+===========================+==============+ +| BLE\_HS\_ATT\_ERR() | ATT | 0x100 | ++----------------------------+---------------------------+--------------+ +| BLE\_HS\_HCI\_ERR() | HCI | 0x200 | ++----------------------------+---------------------------+--------------+ +| BLE\_HS\_L2C\_ERR() | L2CAP | 0x300 | ++----------------------------+---------------------------+--------------+ +| BLE\_HS\_SM\_US\_ERR() | Security manager (us) | 0x400 | ++----------------------------+---------------------------+--------------+ +| BLE\_HS\_SM\_PEER\_ERR() | Security manager (peer) | 0x500 | ++----------------------------+---------------------------+--------------+ + +Example +^^^^^^^ + +The following example demonstrates how an application might determine which error is being reported by the host. In this example, the application performs the GAP encryption procedure and checks the return code. To simplify the example, the application uses a hypothetical *my\_blocking\_enc\_proc()* function, which blocks until the pairing operation has completed. + +.. code:: c + + void + encrypt_connection(uint16_t conn_handle) + { + int rc; + + /* Perform a blocking GAP encryption procedure. */ + rc = my_blocking_enc_proc(conn_handle); + switch (rc) { + case 0: + console_printf("success - link successfully encrypted\n"); + break; + + case BLE_HS_ENOTCONN: + console_printf("failure - no connection with handle %d\n", + conn_handle); + break; + + case BLE_HS_ERR_SM_US_BASE(BLE_SM_ERR_CONFIRM_MISMATCH): + console_printf("failure - mismatch in peer's confirm and random " + "commands.\n"); + break; + + case BLE_HS_ERR_SM_PEER_BASE(BLE_SM_ERR_CONFIRM_MISMATCH): + console_printf("failure - peer reports mismatch in our confirm and " + "random commands.\n"); + break; + + default: + console_printf("failure - other error: 0x%04x\n", rc); + break; + } + } + +Return Code Reference +~~~~~~~~~~~~~~~~~~~~~ + +Header +^^^^^^ + +All NimBLE host return codes are made accessible by including the following header: + +.. code:: c + + #include "host/ble_hs.h" + +Return codes - Core +^^^^^^^^^^^^^^^^^^^ + +The precise meaning of each of these error codes depends on the function that returns it. +The API reference for a particular function indicates the conditions under which each of these codes are returned. + ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| Value | Name | Condition | ++=========+==============================+=============================================================================================+ +| 0x00 | *N/A* | Success | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x01 | BLE\_HS\_EAGAIN | Temporary failure; try again. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x02 | BLE\_HS\_EALREADY | Operation already in progress or completed. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x03 | BLE\_HS\_EINVAL | One or more arguments are invalid. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x04 | BLE\_HS\_EMSGSIZE | The provided buffer is too small. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x05 | BLE\_HS\_ENOENT | No entry matching the specified criteria. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x06 | BLE\_HS\_ENOMEM | Operation failed due to resource exhaustion. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x07 | BLE\_HS\_ENOTCONN | No open connection with the specified handle. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x08 | BLE\_HS\_ENOTSUP | Operation disabled at compile time. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x09 | BLE\_HS\_EAPP | Application callback behaved unexpectedly. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x0a | BLE\_HS\_EBADDATA | Command from peer is invalid. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x0b | BLE\_HS\_EOS | Mynewt OS error. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x0c | BLE\_HS\_ECONTROLLER | Event from controller is invalid. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x0d | BLE\_HS\_ETIMEOUT | Operation timed out. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x0e | BLE\_HS\_EDONE | Operation completed successfully. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x0f | BLE\_HS\_EBUSY | Operation cannot be performed until procedure completes. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x10 | BLE\_HS\_EREJECT | Peer rejected a connection parameter update request. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x11 | BLE\_HS\_EUNKNOWN | Unexpected failure; catch all. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x12 | BLE\_HS\_EROLE | Operation requires different role (e.g., central vs. peripheral). | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x13 | BLE\_HS\_ETIMEOUT\_HCI | HCI request timed out; controller unresponsive. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x14 | BLE\_HS\_ENOMEM\_EVT | Controller failed to send event due to memory exhaustion (combined host-controller only). | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x15 | BLE\_HS\_ENOADDR | Operation requires an identity address but none configured. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x16 | BLE\_HS\_ENOTSYNCED | Attempt to use the host before it is synced with controller. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x17 | BLE\_HS\_EAUTHEN | Insufficient authentication. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x18 | BLE\_HS\_EAUTHOR | Insufficient authorization. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x19 | BLE\_HS\_EENCRYPT | Insufficient encryption level. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x1a | BLE\_HS\_EENCRYPT\_KEY\_SZ | Insufficient key size. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x1b | BLE\_HS\_ESTORE\_CAP | Storage at capacity. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ +| 0x1c | BLE\_HS\_ESTORE\_FAIL | Storage IO error. | ++---------+------------------------------+---------------------------------------------------------------------------------------------+ + +Return codes - ATT +^^^^^^^^^^^^^^^^^^ + ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| NimBLE Value | Formal Value | Name | Condition | ++================+================+============================================+===========================================================================================================================================+ +| 0x0101 | 0x01 | BLE\_ATT\_ERR\_INVALID\_HANDLE | The attribute handle given was not valid on this server. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0102 | 0x02 | BLE\_ATT\_ERR\_READ\_NOT\_PERMITTED | The attribute cannot be read. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0103 | 0x03 | BLE\_ATT\_ERR\_WRITE\_NOT\_PERMITTED | The attribute cannot be written. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0104 | 0x04 | BLE\_ATT\_ERR\_INVALID\_PDU | The attribute PDU was invalid. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0105 | 0x05 | BLE\_ATT\_ERR\_INSUFFICIENT\_AUTHEN | The attribute requires authentication before it can be read or written. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0106 | 0x06 | BLE\_ATT\_ERR\_REQ\_NOT\_SUPPORTED | Attribute server does not support the request received from the client. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0107 | 0x07 | BLE\_ATT\_ERR\_INVALID\_OFFSET | Offset specified was past the end of the attribute. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0108 | 0x08 | BLE\_ATT\_ERR\_INSUFFICIENT\_AUTHOR | The attribute requires authorization before it can be read or written. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0109 | 0x09 | BLE\_ATT\_ERR\_PREPARE\_QUEUE\_FULL | Too many prepare writes have been queued. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x010a | 0x0a | BLE\_ATT\_ERR\_ATTR\_NOT\_FOUND | No attribute found within the given attribute handle range. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x010b | 0x0b | BLE\_ATT\_ERR\_ATTR\_NOT\_LONG | The attribute cannot be read or written using the Read Blob Request. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x010c | 0x0c | BLE\_ATT\_ERR\_INSUFFICIENT\_KEY\_SZ | The Encryption Key Size used for encrypting this link is insufficient. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x010d | 0x0d | BLE\_ATT\_ERR\_INVALID\_ATTR\_VALUE\_LEN | The attribute value length is invalid for the operation. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x010e | 0x0e | BLE\_ATT\_ERR\_UNLIKELY | The attribute request that was requested has encountered an error that was unlikely, and therefore could not be completed as requested. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x010f | 0x0f | BLE\_ATT\_ERR\_INSUFFICIENT\_ENC | The attribute requires encryption before it can be read or written. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0110 | 0x10 | BLE\_ATT\_ERR\_UNSUPPORTED\_GROUP | The attribute type is not a supported grouping attribute as defined by a higher layer specification. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0111 | 0x11 | BLE\_ATT\_ERR\_INSUFFICIENT\_RES | Insufficient Resources to complete the request. | ++----------------+----------------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + +Return codes - HCI +^^^^^^^^^^^^^^^^^^ + ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| NimBLE Value | Formal Value | Name | Condition | ++================+================+====================================+================================================================================+ +| 0x0201 | 0x01 | BLE\_ERR\_UNKNOWN\_HCI\_CMD | Unknown HCI Command | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0202 | 0x02 | BLE\_ERR\_UNK\_CONN\_ID | Unknown Connection Identifier | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0203 | 0x03 | BLE\_ERR\_HW\_FAIL | Hardware Failure | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0204 | 0x04 | BLE\_ERR\_PAGE\_TMO | Page Timeout | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0205 | 0x05 | BLE\_ERR\_AUTH\_FAIL | Authentication Failure | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0206 | 0x06 | BLE\_ERR\_PINKEY\_MISSING | PIN or Key Missing | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0207 | 0x07 | BLE\_ERR\_MEM\_CAPACITY | Memory Capacity Exceeded | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0208 | 0x08 | BLE\_ERR\_CONN\_SPVN\_TMO | Connection Timeout | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0209 | 0x09 | BLE\_ERR\_CONN\_LIMIT | Connection Limit Exceeded | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x020a | 0x0a | BLE\_ERR\_SYNCH\_CONN\_LIMIT | Synchronous Connection Limit To A Device Exceeded | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x020b | 0x0b | BLE\_ERR\_ACL\_CONN\_EXISTS | ACL Connection Already Exists | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x020c | 0x0c | BLE\_ERR\_CMD\_DISALLOWED | Command Disallowed | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x020d | 0x0d | BLE\_ERR\_CONN\_REJ\_RESOURCES | Connection Rejected due to Limited Resources | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x020e | 0x0e | BLE\_ERR\_CONN\_REJ\_SECURITY | Connection Rejected Due To Security Reasons | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x020f | 0x0f | BLE\_ERR\_CONN\_REJ\_BD\_ADDR | Connection Rejected due to Unacceptable BD\_ADDR | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0210 | 0x10 | BLE\_ERR\_CONN\_ACCEPT\_TMO | Connection Accept Timeout Exceeded | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0211 | 0x11 | BLE\_ERR\_UNSUPPORTED | Unsupported Feature or Parameter Value | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0212 | 0x12 | BLE\_ERR\_INV\_HCI\_CMD\_PARMS | Invalid HCI Command Parameters | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0213 | 0x13 | BLE\_ERR\_REM\_USER\_CONN\_TERM | Remote User Terminated Connection | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0214 | 0x14 | BLE\_ERR\_RD\_CONN\_TERM\_RESRCS | Remote Device Terminated Connection due to Low Resources | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0215 | 0x15 | BLE\_ERR\_RD\_CONN\_TERM\_PWROFF | Remote Device Terminated Connection due to Power Off | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0216 | 0x16 | BLE\_ERR\_CONN\_TERM\_LOCAL | Connection Terminated By Local Host | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0217 | 0x17 | BLE\_ERR\_REPEATED\_ATTEMPTS | Repeated Attempts | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0218 | 0x18 | BLE\_ERR\_NO\_PAIRING | Pairing Not Allowed | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0219 | 0x19 | BLE\_ERR\_UNK\_LMP | Unknown LMP PDU | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x021a | 0x1a | BLE\_ERR\_UNSUPP\_REM\_FEATURE | Unsupported Remote Feature / Unsupported LMP Feature | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x021b | 0x1b | BLE\_ERR\_SCO\_OFFSET | SCO Offset Rejected | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x021c | 0x1c | BLE\_ERR\_SCO\_ITVL | SCO Interval Rejected | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x021d | 0x1d | BLE\_ERR\_SCO\_AIR\_MODE | SCO Air Mode Rejected | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x021e | 0x1e | BLE\_ERR\_INV\_LMP\_LL\_PARM | Invalid LMP Parameters / Invalid LL Parameters | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x021f | 0x1f | BLE\_ERR\_UNSPECIFIED | Unspecified Error | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0220 | 0x20 | BLE\_ERR\_UNSUPP\_LMP\_LL\_PARM | Unsupported LMP Parameter Value / Unsupported LL Parameter Value | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0221 | 0x21 | BLE\_ERR\_NO\_ROLE\_CHANGE | Role Change Not Allowed | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0222 | 0x22 | BLE\_ERR\_LMP\_LL\_RSP\_TMO | LMP Response Timeout / LL Response Timeout | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0223 | 0x23 | BLE\_ERR\_LMP\_COLLISION | LMP Error Transaction Collision | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0224 | 0x24 | BLE\_ERR\_LMP\_PDU | LMP PDU Not Allowed | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0225 | 0x25 | BLE\_ERR\_ENCRYPTION\_MODE | Encryption Mode Not Acceptable | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0226 | 0x26 | BLE\_ERR\_LINK\_KEY\_CHANGE | Link Key cannot be Changed | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0227 | 0x27 | BLE\_ERR\_UNSUPP\_QOS | Requested QoS Not Supported | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0228 | 0x28 | BLE\_ERR\_INSTANT\_PASSED | Instant Passed | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0229 | 0x29 | BLE\_ERR\_UNIT\_KEY\_PAIRING | Pairing With Unit Key Not Supported | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x022a | 0x2a | BLE\_ERR\_DIFF\_TRANS\_COLL | Different Transaction Collision | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x022c | 0x2c | BLE\_ERR\_QOS\_PARM | QoS Unacceptable Parameter | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x022d | 0x2d | BLE\_ERR\_QOS\_REJECTED | QoS Rejected | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x022e | 0x2e | BLE\_ERR\_CHAN\_CLASS | Channel Classification Not Supported | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x022f | 0x2f | BLE\_ERR\_INSUFFICIENT\_SEC | Insufficient Security | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0230 | 0x30 | BLE\_ERR\_PARM\_OUT\_OF\_RANGE | Parameter Out Of Mandatory Range | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0232 | 0x32 | BLE\_ERR\_PENDING\_ROLE\_SW | Role Switch Pending | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0234 | 0x34 | BLE\_ERR\_RESERVED\_SLOT | Reserved Slot Violation | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0235 | 0x35 | BLE\_ERR\_ROLE\_SW\_FAIL | Role Switch Failed | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0236 | 0x36 | BLE\_ERR\_INQ\_RSP\_TOO\_BIG | Extended Inquiry Response Too Large | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0237 | 0x37 | BLE\_ERR\_SEC\_SIMPLE\_PAIR | Secure Simple Pairing Not Supported By Host | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0238 | 0x38 | BLE\_ERR\_HOST\_BUSY\_PAIR | Host Busy - Pairing | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0239 | 0x39 | BLE\_ERR\_CONN\_REJ\_CHANNEL | Connection Rejected due to No Suitable Channel Found | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x023a | 0x3a | BLE\_ERR\_CTLR\_BUSY | Controller Busy | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x023b | 0x3b | BLE\_ERR\_CONN\_PARMS | Unacceptable Connection Parameters | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x023c | 0x3c | BLE\_ERR\_DIR\_ADV\_TMO | Directed Advertising Timeout | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x023d | 0x3d | BLE\_ERR\_CONN\_TERM\_MIC | Connection Terminated due to MIC Failure | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x023e | 0x3e | BLE\_ERR\_CONN\_ESTABLISHMENT | Connection Failed to be Established | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x023f | 0x3f | BLE\_ERR\_MAC\_CONN\_FAIL | MAC Connection Failed | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ +| 0x0240 | 0x40 | BLE\_ERR\_COARSE\_CLK\_ADJ | Coarse Clock Adjustment Rejected but Will Try to Adjust Using Clock Dragging | ++----------------+----------------+------------------------------------+--------------------------------------------------------------------------------+ + +Return codes - L2CAP +^^^^^^^^^^^^^^^^^^^^ + ++----------------+----------------+----------------------------------------------+------------------------------------------------------+ +| NimBLE Value | Formal Value | Name | Condition | ++================+================+==============================================+======================================================+ +| 0x0300 | 0x00 | BLE\_L2CAP\_SIG\_ERR\_CMD\_NOT\_UNDERSTOOD | Invalid or unsupported incoming L2CAP sig command. | ++----------------+----------------+----------------------------------------------+------------------------------------------------------+ +| 0x0301 | 0x01 | BLE\_L2CAP\_SIG\_ERR\_MTU\_EXCEEDED | Incoming packet too large. | ++----------------+----------------+----------------------------------------------+------------------------------------------------------+ +| 0x0302 | 0x02 | BLE\_L2CAP\_SIG\_ERR\_INVALID\_CID | No channel with specified ID. | ++----------------+----------------+----------------------------------------------+------------------------------------------------------+ + +Return codes - Security manager (us) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| NimBLE Value | Formal Value | Name | Condition | ++================+================+===================================+===========================================================================================================================================+ +| 0x0401 | 0x01 | BLE\_SM\_ERR\_PASSKEY | The user input of passkey failed, for example, the user cancelled the operation. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0402 | 0x02 | BLE\_SM\_ERR\_OOB | The OOB data is not available. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0403 | 0x03 | BLE\_SM\_ERR\_AUTHREQ | The pairing procedure cannot be performed as authentication requirements cannot be met due to IO capabilities of one or both devices. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0404 | 0x04 | BLE\_SM\_ERR\_CONFIRM\_MISMATCH | The confirm value does not match the calculated compare value. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0405 | 0x05 | BLE\_SM\_ERR\_PAIR\_NOT\_SUPP | Pairing is not supported by the device. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0406 | 0x06 | BLE\_SM\_ERR\_ENC\_KEY\_SZ | The resultant encryption key size is insufficient for the security requirements of this device. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0407 | 0x07 | BLE\_SM\_ERR\_CMD\_NOT\_SUPP | The SMP command received is not supported on this device. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0408 | 0x08 | BLE\_SM\_ERR\_UNSPECIFIED | Pairing failed due to an unspecified reason. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0409 | 0x09 | BLE\_SM\_ERR\_REPEATED | Pairing or authentication procedure is disallowed because too little time has elapsed since last pairing request or security request. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x040a | 0x0a | BLE\_SM\_ERR\_INVAL | The Invalid Parameters error code indicates that the command length is invalid or that a parameter is outside of the specified range. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x040b | 0x0b | BLE\_SM\_ERR\_DHKEY | Indicates to the remote device that the DHKey Check value received doesn’t match the one calculated by the local device. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x040c | 0x0c | BLE\_SM\_ERR\_NUMCMP | Indicates that the confirm values in the numeric comparison protocol do not match. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x040d | 0x0d | BLE\_SM\_ERR\_ALREADY | Indicates that the pairing over the LE transport failed due to a Pairing Request sent over the BR/EDR transport in process. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x040e | 0x0e | BLE\_SM\_ERR\_CROSS\_TRANS | Indicates that the BR/EDR Link Key generated on the BR/EDR transport cannot be used to derive and distribute keys for the LE transport. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ + +Return codes - Security manager (peer) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| NimBLE Value | Formal Value | Name | Condition | ++================+================+===================================+===========================================================================================================================================+ +| 0x0501 | 0x01 | BLE\_SM\_ERR\_PASSKEY | The user input of passkey failed, for example, the user cancelled the operation. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0502 | 0x02 | BLE\_SM\_ERR\_OOB | The OOB data is not available. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0503 | 0x03 | BLE\_SM\_ERR\_AUTHREQ | The pairing procedure cannot be performed as authentication requirements cannot be met due to IO capabilities of one or both devices. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0504 | 0x04 | BLE\_SM\_ERR\_CONFIRM\_MISMATCH | The confirm value does not match the calculated compare value. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0505 | 0x05 | BLE\_SM\_ERR\_PAIR\_NOT\_SUPP | Pairing is not supported by the device. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0506 | 0x06 | BLE\_SM\_ERR\_ENC\_KEY\_SZ | The resultant encryption key size is insufficient for the security requirements of this device. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0507 | 0x07 | BLE\_SM\_ERR\_CMD\_NOT\_SUPP | The SMP command received is not supported on this device. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0508 | 0x08 | BLE\_SM\_ERR\_UNSPECIFIED | Pairing failed due to an unspecified reason. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x0509 | 0x09 | BLE\_SM\_ERR\_REPEATED | Pairing or authentication procedure is disallowed because too little time has elapsed since last pairing request or security request. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x050a | 0x0a | BLE\_SM\_ERR\_INVAL | The Invalid Parameters error code indicates that the command length is invalid or that a parameter is outside of the specified range. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x050b | 0x0b | BLE\_SM\_ERR\_DHKEY | Indicates to the remote device that the DHKey Check value received doesn’t match the one calculated by the local device. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x050c | 0x0c | BLE\_SM\_ERR\_NUMCMP | Indicates that the confirm values in the numeric comparison protocol do not match. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x050d | 0x0d | BLE\_SM\_ERR\_ALREADY | Indicates that the pairing over the LE transport failed due to a Pairing Request sent over the BR/EDR transport in process. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +| 0x050e | 0x0e | BLE\_SM\_ERR\_CROSS\_TRANS | Indicates that the BR/EDR Link Key generated on the BR/EDR transport cannot be used to derive and distribute keys for the LE transport. | ++----------------+----------------+-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ diff --git a/src/libs/mynewt-nimble/docs/ble_sec.rst b/src/libs/mynewt-nimble/docs/ble_sec.rst new file mode 100644 index 00000000..0cc15e63 --- /dev/null +++ b/src/libs/mynewt-nimble/docs/ble_sec.rst @@ -0,0 +1,76 @@ +NimBLE Security +--------------- + +The Bluetooth Low Energy security model includes five distinct security +concepts as listed below. For detailed specifications, see BLUETOOTH +SPECIFICATION Version 4.2 [Vol 1, Part A]. + +- **Pairing**: The process for creating one or more shared secret keys. + In LE a single link key is generated by combining contributions from + each device into a link key used during pairing. + +- **Bonding**: The act of storing the keys created during pairing for + use in subsequent connections in order to form a trusted device pair. + +- **Device authentication**: Verification that the two devices have the + same keys (verify device identity) + +- **Encryption**: Keeps message confidential. Encryption in Bluetooth + LE uses AES-CCM cryptography and is performed in the *Controller*. + +- **Message integrity**: Protects against message forgeries. + +Bluetooth LE uses four association models depending on the I/O +capabilities of the devices. + +- **Just Works**: designed for scenarios where at least one of the + devices does not have a display capable of displaying a six digit + number nor does it have a keyboard capable of entering six decimal + digits. + +- **Numeric Comparison**: designed for scenarios where both devices are + capable of displaying a six digit number and both are capable of + having the user enter "yes" or "no". A good example of this model is + the cell phone / PC scenario. + +- **Out of Band**: designed for scenarios where an Out of Band + mechanism is used to both discover the devices as well as to exchange + or transfer cryptographic numbers used in the pairing process. + +- **Passkey Entry**: designed for the scenario where one device has + input capability but does not have the capability to display six + digits and the other device has output capabilities. A good example + of this model is the PC and keyboard scenario. + +Key Generation +~~~~~~~~~~~~~~ + +Key generation for all purposes in Bluetooth LE is performed by the +*Host* on each LE device independent of any other LE device. + +Privacy Feature +~~~~~~~~~~~~~~~ + +Bluetooth LE supports an optional feature during connection mode and +connection procedures that reduces the ability to track a LE device over +a period of time by changing the Bluetooth device address on a frequent +basis. + +There are two variants of the privacy feature. + +- In the first variant, private addresses are resolved and generated by + the *Host*. +- In the second variant, private addresses are resolved and generated + by the *Controller* without involving the Host after the Host + provides the Controller device identity information. The Host may + provide the Controller with a complete resolving list or a subset of + the resolving list. Device filtering becomes possible in the second + variant when address resolution is performed in the Controller + because the peer’s device identity address can be resolved prior to + checking whether it is in the white list. + +**Note**: When address resolution is performed exclusively in the Host, +a device may experience increased power consumption because device +filtering must be disabled. For more details on the privacy feature, +refer to BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part C] (Published +02 December 2014), Page 592. diff --git a/src/libs/mynewt-nimble/docs/ble_setup/ble_addr.rst b/src/libs/mynewt-nimble/docs/ble_setup/ble_addr.rst new file mode 100644 index 00000000..0a67a5f7 --- /dev/null +++ b/src/libs/mynewt-nimble/docs/ble_setup/ble_addr.rst @@ -0,0 +1,63 @@ +Configure device address +------------------------ + +A BLE device needs an address to do just about anything. For information +on the various types of Bluetooth addresses, see the `NimBLE Host +Identity Reference :doc:`<../ble_hs/ble_hs_id/ble_hs_id>`. + +There are several methods for assigning an address to a NimBLE device. +The available options are documented below: + +Method 1: Configure nRF hardware with a public address +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When Mynewt is running on a Nordic nRF platform, the NimBLE controller +will attempt to read a public address out of the board's FICR or UICR +registers. The controller uses the following logic while trying to read +an address from hardware: + +1. If the *DEVICEADDRTYPE* FICR register is written, read the address + programmed in the *DEVICEADDR[0]* and *DEVICEADDR[1]* FICR registers. +2. Else if the upper 16 bits of the *CUSTOMER[1]* UICR register are 0, + read the address programmed in the *CUSTOMER[0]* and *CUSTOMER[1]* + UCI registers. +3. Else, no address available. + +Method 2: Hardcode a public address in the Mynewt target +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The NimBLE controller package exports a +:doc:`syscfg <../../../os/modules/sysinitconfig/sysinitconfig>` setting +called ``BLE_PUBLIC_DEV_ADDR``. This setting can be overridden at the +application or target level to configure a public Bluetooth address. For +example, a target can assign the public address *11:22:33:44:55:66* as +follows: + +:: + + syscfg.vals: + BLE_PUBLIC_DEV_ADDR: '(uint8_t[6]){0x66, 0x55, 0x44, 0x33, 0x22, 0x11}' + +This setting takes the form of a C expression. Specifically, the value +is a designated initializer expressing a six-byte array. Also note that +the bytes are reversed, as an array is inherently little-endian, while +addresses are generally expressed in big-endian. + +Note: this method takes precedence over method 1. Whatever is written to +the ``BLE_PUBLIC_DEV_ADDR`` setting is the address that gets used. + +Method 3: Configure a random address at runtime +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Random addresses get configured through the NimBLE host. The following +two functions are used in random address configuration: + +- :doc:`ble_hs_id_gen_rnd <../ble_hs/ble_hs_id/functions/ble_hs_id_gen_rnd>`: + Generates a new random address. +- :doc:`ble_hs_id_set_rnd <../ble_hs/ble_hs_id/functions/ble_hs_id_set_rnd>`: + Sets the device's random address. + +For an example of how this is done, see the :doc:`<../../../os/tutorials/ibeacon>`. + +*Note:* A NimBLE device can be configured with multiple addresses; at +most one of each address type. diff --git a/src/libs/mynewt-nimble/docs/ble_setup/ble_lp_clock.rst b/src/libs/mynewt-nimble/docs/ble_setup/ble_lp_clock.rst new file mode 100644 index 00000000..34a967fe --- /dev/null +++ b/src/libs/mynewt-nimble/docs/ble_setup/ble_lp_clock.rst @@ -0,0 +1,67 @@ +Configure clock for controller +------------------------------ + +The NimBLE stack uses OS cputime for scheduling various events inside +controller. Since the code of controller is optimized to work with 32768 +Hz clock, the OS cputime has to be configured accordingly. + +To make things easier, controller package (``net/nimble/controller``) +defines new system configuration setting ``BLE_LP_CLOCK`` as sets it to +``1`` so other packages can be configured if necessary. The next section +describes configuration required for controller to work properly. + +System configuration +~~~~~~~~~~~~~~~~~~~~ + +**Note:** All BSPs based on nRF5x have below settings automatically +applied when ``BLE_LP_CLOCK`` is set, there is no need to configure this +in application. + +The following things need to be configured for NimBLE controller to work +properly: + +- OS cputime frequency shall be set to ``32768`` +- OS cputime timer source shall be set to 32768 Hz clock source +- Default 1 MHz clock source can be disabled if not used by application +- 32768 Hz clock source shall be enabled +- Crystal settling time shall be set to non-zero value (see below) + +For example, on nRF52 platform timer 5 can be used as source for 32768 +Hz clock. Also, timer 0 can be disabled since this is the default source +for OS cputime clock and is no longer used. The configuration will look +as below: + +:: + + syscfg.vals: + OS_CPUTIME_FREQ: 32768 + OS_CPUTIME_TIMER_NUM: 5 + TIMER_0: 0 + TIMER_5: 1 + BLE_XTAL_SETTLE_TIME: 1500 + +On nRF51 platform the only difference is to use timer 3 instead of timer +5. + +On platforms without 32768 Hz crystal available it usually can be +synthesized by setting ``XTAL_32768_SYNTH`` to ``1`` - this is also +already configured in existing BSPs. + +Crystal settle time configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The configuration variable ``BLE_XTAL_SETTLE_TIME`` is used by the +controller to turn on the necessary clock source(s) for the radio and +associated peripherals prior to Bluetooth events (advertising, scanning, +connections, etc). For the nRF5x platforms, the HFXO needs to be turned +on prior to using the radio and the ``BLE_XTAL_SETTLE_TIME`` must be set +to accommodate this time. The amount of time required is board +dependent, so users must characterize their hardware and set +``BLE_XTAL_SETTLE_TIME`` accordingly. The current value of 1500 +microseconds is a fairly long time and was intended to work for most, if +not all, platforms. + +Note that changing this time will impact battery life with the amount +depending on the application. The HFXO draws a fairly large amount of +current when running so keeping this time as small as possible will +reduce overall current drain. diff --git a/src/libs/mynewt-nimble/docs/ble_setup/ble_setup_intro.rst b/src/libs/mynewt-nimble/docs/ble_setup/ble_setup_intro.rst new file mode 100644 index 00000000..806817c6 --- /dev/null +++ b/src/libs/mynewt-nimble/docs/ble_setup/ble_setup_intro.rst @@ -0,0 +1,13 @@ +NimBLE Setup +------------ + +Most NimBLE initialization is done automatically by +:doc:`sysinit <../../../os/modules/sysinitconfig/sysinitconfig>`. This +section documents the few bits of initialization that an application +must perform manually. + +.. toctree:: + + ble_lp_clock + ble_addr + ble_sync_cb diff --git a/src/libs/mynewt-nimble/docs/ble_setup/ble_sync_cb.rst b/src/libs/mynewt-nimble/docs/ble_setup/ble_sync_cb.rst new file mode 100644 index 00000000..b14a3582 --- /dev/null +++ b/src/libs/mynewt-nimble/docs/ble_setup/ble_sync_cb.rst @@ -0,0 +1,80 @@ +Respond to *sync* and *reset* events +------------------------------------ + +sync +~~~~ + +The NimBLE stack is inoperable while the host and controller are out of +sync. In a combined host-controller app, the sync happens immediately at +startup. When the host and controller are separate, sync typically +occurs in under a second after the application starts. An application +learns when sync is achieved by configuring the host's *sync callback*: +``ble_hs_cfg.sync_cb``. The host calls the sync callback whenever sync +is acquired. The sync callback has the following form: + +.. code-block:: cpp + + typedef void ble_hs_sync_fn(void); + +Because the NimBLE stack begins in the unsynced state, the application +should delay all BLE operations until the sync callback has been called. + +reset +~~~~~ + +Another event indicated by the host is a *controller reset*. The NimBLE +stack resets itself when a catastrophic error occurs, such as loss of +communication between the host and controller. Upon resetting, the host +drops all BLE connections and loses sync with the controller. After a +reset, the application should refrain from using the host until sync is +again signaled via the sync callback. + +An application learns of a host reset by configuring the host's *reset +callback*: ``ble_hs_cfg.reset_cb``. This callback has the following +form: + +.. code-block:: cpp + + typedef void ble_hs_reset_fn(int reason); + +The ``reason`` parameter is a :doc:`NimBLE host return +code <../ble_hs/ble_hs_return_codes>`. + +Example +~~~~~~~ + +The following example demonstrates the configuration of the sync and +reset callbacks. + +.. code-block:: cpp + + #include "sysinit/sysinit.h" + #include "console/console.h" + #include "host/ble_hs.h" + + static void + on_sync(void) + { + /* Begin advertising, scanning for peripherals, etc. */ + } + + static void + on_reset(int reason) + { + console_printf("Resetting state; reason=%d\n", reason); + } + + int + main(void) + { + /* Initialize all packages. */ + sysinit(); + + ble_hs_cfg.sync_cb = on_sync; + ble_hs_cfg.reset_cb = on_reset; + + /* As the last thing, process events from default event queue. */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + } diff --git a/src/libs/mynewt-nimble/docs/btshell/btshell_GAP.rst b/src/libs/mynewt-nimble/docs/btshell/btshell_GAP.rst new file mode 100644 index 00000000..ce647555 --- /dev/null +++ b/src/libs/mynewt-nimble/docs/btshell/btshell_GAP.rst @@ -0,0 +1,660 @@ +GAP API for btshell +=================== + +Generic Access Profile (GAP) defines the generic procedures related to discovery of Bluetooth devices (idle mode +procedures) and link management aspects of connecting to Bluetooth devices (connecting mode procedures). It also defines +procedures related to use of different security levels. + +Several different modes and procedures may be performed simultaneously over an LE physical transport. The following +modes and procedures are defined for use over an LE physical transport: + +1. **Broadcast mode and observation procedure** + + - These allow two devices to communicate in a unidirectional connectionless manner using the advertising events. + +2. **Discovery modes and procedures** + + - All devices shall be in either non-discoverable mode or one of the discoverable modes. + - A device in the discoverable mode shall be in either the general discoverable mode or the limited discoverable mode. + - A device in non-discoverable mode will not be discovered by any device that is performing either the general + discovery procedure or the limited discovery procedure. + +3. **Connection modes and procedures** + + - allow a device to establish a connection to another device. + - allow updating of parameters of the connection + - allow termination of the connection + +4. **Bonding modes and procedures** + + - Bonding allows two connected devices to exchange and store security and identity information to create a trusted + relationship. + - Bonding can occur only between two devices in bondable mode. + +Available commands +~~~~~~~~~~~~~~~~~~ + +Parameters default values are marked red. + +Configuration +------------- + ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| **Command** | **Parmeters** | \*\* Possible values\*\* | **Description** | ++=====================+=================+============================+=========================================================================================================+ +| **set** | | | Set configuration options | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | addr | XX:XX:XX:XX:XX:XX | Local device address | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | addr\_type | ``public`` | Local device address type | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | | random | Use random address for scan requests | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | mtu | [23-UINT16\_MAX] | GATT Maximum Transmission Unit (MTU) | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | irk | XX:XX:XX... | Local Identity Resolving Key (16 byte | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| **set-priv-mode** | | | Set privacy mode for device | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | addr | XX:XX:XX:XX:XX:XX | Remote device address | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | addr\_type | ``public`` | Remote device public address type | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | | random | Remote device random address type | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | mode | [``0``-1] | 0 - use network privacy, 1 - use device privacy | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| **white-list** | | | Add devices to white list (this command accepts multiple instances of addr and addr\_type parameters) | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | addr | XX:XX:XX:XX:XX:XX | Remote device address | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | addr\_type | ``public`` | Remote device public address type | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ +| | | random | Remote device random address type | ++---------------------+-----------------+----------------------------+---------------------------------------------------------------------------------------------------------+ + +Device discovery and connection +------------------------------- + ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **Command** | **Parmeters** | \*\* Possible values\*\* | **Description** | ++==========================+================================+============================+============================================================================================================+ +| **scan** | | | Discover remote devices | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | cancel | | cancel ongoing scan procedure | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | extended | ``none`` | Start legacy scan | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | 1M | Start extended scan on 1M PHY | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | coded | Start extended scan on Coded PHY | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | both | Start extended scan on both PHYs | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | duration | [1-``INT32_MAX``], | Duration of scan in milliseconds | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | limited | [``0``-1] | Use limited discovery procedure | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | passive | [``0``-1] | Use passive scan | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | interval | [``0``-UINT16\_MAX] | Scan interval, if 0 use stack's default | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | window | [``0``-UINT16\_MAX] | Scan window, if 0 use stack's default | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | filter | ``no_wl`` | Scan filter policy - Accept all advertising packets | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | use\_wl | Accept only advertising packets from devices on White List | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | no\_wl\_inita | Accept all advertising packets (including directed RPA) | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | use\_wl\_inita | Accept only advertising packets from devices on White List (including directed RPA) | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | nodups | [``0``-1] | Disable duplicates filtering | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | own\_addr\_type | ``public`` | Use public address for scan requests | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | random | Use random address for scan requests | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | rpa\_pub | Use RPA address for scan requests (fallback to public if no IRK) | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | rpa\_rnd | Use RPA address for scan requests (fallback to random if no IRK) | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | extended\_duration | [``0``-UINT16\_MAX] | Duration of extended scan in 10 milliseconds | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | extended\_period | [``0``-UINT16\_MAX] | Periodic scan interval in 1.28 seconds (0 disabled) | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | longrange\_interval | [``0``-UINT16\_MAX] | Scan interval for Coded Scan , if 0 use stack's default | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | longrange\_window | [``0``-UINT16\_MAX] | Scan window for Coded Scan , if 0 use stack's default | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | longrange\_passive | [``0``-1] | Use passive scan for Coded Scan | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **connect** | | | Initiate connection to remote device | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | cancel | | Cancel ongoing connection procedure | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | extended | ``none`` | Use legacy connection procedure | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | 1M | Extended connect using 1M PHY scan parameters | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | coded | Extended connect using Coded PHY scan parameters | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | both | Extended connect using 1M and Coded PHYs scan parameters | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | all | Extended connect using 1M and Coded PHYs scan parameters (Provide also connection parameters for 2M PHY) | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | peer\_addr\_type | ``public`` | Remote device public address type | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | random | Remote device random address type | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | public\_id | Remote device public address type (Identity) | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | random\_id | Remote device random address type (Identity) | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | peer\_addr | XX:XX:XX:XX:XX:XX | Remote device address | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | own\_addr\_type | ``public`` | Use public address for scan requests | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | random | Use random address for scan requests | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | rpa\_pub | Use RPA address for scan requests (fallback to public if no IRK) | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | | rpa\_rnd | Use RPA address for scan requests (fallback to random if no IRK) | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | duration | [``0``-INT32\_MAX] | Connection attempt duration, if 0 use stack's default | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | scan\_interval | [0-UINT16\_MAX] | Scan interval, default: 0x0010 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | scan\_window | [0-UINT16\_MAX] | Scan window, default: 0x0010 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | interval\_min | [0-UINT16\_MAX] | Minimum connection interval, default: 30 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | interval\_max | [0-UINT16\_MAX] | Maximum connection interval, default: 50 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | latency | [UINT16] | Connection latency, default: 0 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | timeout | [UINT16] | Connection timeout, default: 0x0100 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | min\_conn\_event\_len | [UINT16] | Minimum length of connection event, default: 0x0010 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | max\_conn\_event\_len | [UINT16] | Maximum length of connection event, default: 0x0300 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | coded\_scan\_interval | [0-UINT16\_MAX] | Coded PHY Scan interval, default: 0x0010 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | coded\_scan\_window | [0-UINT16\_MAX] | Coded PHY Scan window, default: 0x0010 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | coded\_interval\_min | [0-UINT16\_MAX] | Coded PHY Minimum connection interval, default: 30 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | coded\_interval\_max | [0-UINT16\_MAX] | Coded PHY Maximum connection interval, default: 50 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | coded\_latency | [UINT16] | Coded PHY Connection latency, default: 0 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | coded\_timeout | [UINT16] | Coded PHY Connection timeout, default: 0x0100 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | coded\_min\_conn\_event\_len | [UINT16] | Coded PHY Minimum length of connection event, default: 0x0010 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | coded\_max\_conn\_event\_len | [UINT16] | Coded PHY Maximum length of connection event, default: 0x0300 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | 2M\_scan\_interval | [0-UINT16\_MAX] | 2M PHY Scan interval, default: 0x0010 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | 2M\_scan\_window | [0-UINT16\_MAX] | 2M PHY Scan window, default: 0x0010 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | 2M\_interval\_min | [0-UINT16\_MAX] | 2M PHY Minimum connection interval, default: 30 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | 2M\_interval\_max | [0-UINT16\_MAX] | 2M PHY Maximum connection interval, default: 50 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | 2M\_latency | [UINT16] | 2M PHY Connection latency, default: 0 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | 2M\_timeout | [UINT16] | 2M PHY Connection timeout, default: 0x0100 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | 2M\_min\_conn\_event\_len | [UINT16] | 2M PHY Minimum length of connection event, default: 0x0010 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | 2M\_max\_conn\_event\_len | [UINT16] | 2M PHY Maximum length of connection event, default: 0x0300 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **disconnect** | | | Disconnect exisiting connection | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | reason | [UINT8] | Disconnect reason | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **show-addr** | | | Show local public and random identity addresses | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **show-conn** | | | Show current connections | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **conn-rssi** | | | Obtain RSSI of specified connection | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **conn-update-params** | | | Update parameters of specified connection | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | interval\_min | [0-UINT16\_MAX] | Minimum connection interval, default: 30 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | interval\_max | [0-UINT16\_MAX] | Maximum connection interval, default: 50 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | latency | [UINT16] | Connection latency, default: 0 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | timeout | [UINT16] | Connection timeout, default: 0x0100 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | min\_conn\_event\_len | [UINT16] | Minimum length of connection event, default: 0x0010 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | max\_conn\_event\_len | [UINT16] | Maximum length of connection event, default: 0x0300 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **conn-datalen** | | | Set DLE parmaeters for connection | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | octets | [UINT16] | Maximum transmission packet size | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | time | [UINT16] | Maximum transmission packet time | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **phy-set** | | | Set prefered PHYs used for connection | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | tx\_phys\_mask | [UINT8] | Prefered PHYs on TX is mask of following bits0x00 - no preference0x01 - 1M, 0x02 - 2M, 0x04 - Coded | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | rx\_phys\_mask | [UINT8] | Prefered PHYs on RX is mask of following bits0x00 - no preference0x01 - 1M, 0x02 - 2M, 0x04 - Coded | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | phy\_opts | [UINT16] | Options for Coded PHY 0 - any coding, 1 - prefer S2, 2 - prefer S8 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **phy-set-default** | | | Set default prefered PHYs used for new connection | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | tx\_phys\_mask | [UINT8] | Prefered PHYs on TX is mask of following bits0x00 - no preference0x01 - 1M, 0x02 - 2M, 0x04 - Coded | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | rx\_phys\_mask | [UINT8] | Prefered PHYs on RX is mask of following bits0x00 - no preference0x01 - 1M, 0x02 - 2M, 0x04 - Coded | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **phy-read** | | | Read connection current PHY | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| **l2cap-update** | | | Update connection parameters | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | interval\_min | [0-UINT16\_MAX] | Minimum connection interval, default: 30 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | interval\_max | [0-UINT16\_MAX] | Maximum connection interval, default: 50 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | latency | [UINT16] | Connection latency, default: 0 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ +| | timeout | [UINT16] | Connection timeout, default: 0x0100 | ++--------------------------+--------------------------------+----------------------------+------------------------------------------------------------------------------------------------------------+ + +Security +-------- + ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| **Command** | **Parmeters** | \*\* Possible values\*\* | **Description** | ++===========================+====================+============================+============================================================================================================================+ +| **security-set-data** | | | Set security configuration | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | oob-flag | [``0``-1] | Set Out-Of-Band (OOB) flag in Security Manager | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | mitm-flag | [``0``-1] | Set Man-In-The-Middle (MITM) flag in Security Manager | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | io\_capabilities | 0 | Set Input-Output Capabilities to "DisplayOnly" | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | | 1 | Set Input-Output Capabilities to "DisplayYesNo" | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | | 2 | Set Input-Output Capabilities to "KeyboardOnly" | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | | 3 | Set Input-Output Capabilities to "NoInputNoOutput" | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | | 4 | Set Input-Output Capabilities to "KeyboardDisplay" | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | our\_key\_dist | [UINT8] | Set Local Keys Distribution, this is a bit field of possible values: LTK (0x01), IRK (0x02), CSRK (0x04), LTK\_SC(0x08) | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | their\_key\_dist | [UINT8] | Set Remote Keys Distribution, this is a bit field of possible values: LTK (0x01), IRK (0x02), CSRK (0x04), LTK\_SC(0x08) | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | bonding-flag | [``0``-1] | Set Bonding flag in Security Manager | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | sc-flag | [``0``-1] | Set Secure Connections flag in Security Manager | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| **security-pair** | | | Start pairing procedure | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| **security-encryption** | | | Start encryption procedure | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | ediv | [UINT16] | EDIV for LTK to use (use storage if not provided) | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | rand | [UINT64] | Rand for LTK | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | ltk | XX:XX:XX... | LTK (16 bytes) | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| **security-start** | | | Start security procedure (This starts either pairing or encryption depending if keys are stored) | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| **auth-passkey** | | | Reply to Passkey request | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | action | [UINT16] | Action to reply (as received in event) | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | key | [0-999999] | Passkey to reply (Input or Display action) | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | oob | XX:XX:XX:... | Out-Of-Band secret (16 bytes) (OOB action) | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ +| | yesno | Yy-Ny | Confirm passkey (for Passkey Confirm action) | ++---------------------------+--------------------+----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + +Advertising with Extended Advertising enabled +--------------------------------------------- + ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| **Command** | **Parmeters** | \*\* Possible values\*\* | **Description** | ++==============================+==========================+============================+=====================================================================================+ +| **advertise-configure** | | | Configure new advertising instance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | instance | [``0``-UINT8\_MAX] | Advertising instance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | connectable | [``0``-1] | Use connectable advertising | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | scannable | [``0``-1] | Use scannable advertising | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | peer\_addr\_type | ``public`` | Remote device public address type | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | random | Remote device random address type | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | public\_id | Remote device public address type (Identity) | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | random\_id | Remote device random address type (Identity) | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | peer\_addr | XX:XX:XX:XX:XX:XX | Remote device address - if provided perform directed advertising | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | own\_addr\_type | ``public`` | Use public address for scan requests | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | random | Use random address for scan requests | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | rpa\_pub | Use RPA address for scan requests (fallback to public if no IRK) | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | rpa\_rnd | Use RPA address for scan requests (fallback to random if no IRK) | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | channel\_map | [``0``-UINT8\_MAX} | Primary advertising channels map. If 0 use all channels. | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | filter | ``none`` | Advertising filter policy - no filtering, no whitelist used | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | scan | process all connection requests but only scans from white list | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | conn | process all scan request but only connection requests from white list | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | both | ignore all scan and connection requests unless in white list | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | interval\_min | [``0``-UINT32\_MAX] | Minimum advertising interval in 0.625 miliseconds If 0 use stack default. | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | interval\_max | [``0``-UINT32\_MAX] | Maximum advertising interval in 0.625 miliseconds If 0 use stack default. | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | rx\_power | [-127 - ``127``] | Advertising TX power in dBm | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | primary\_phy | ``1M`` | Use 1M PHY on primary advertising channels | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | ``coded`` | Use Coded PHY on primary advertising channels | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | secondary\_phy | ``1M`` | Use 1M PHY on secondary advertising channels | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | ``coded`` | Use coded PHY on primary advertising channels | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | ``2M`` | Use 2M PHY on primary advertising channels | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | sid | [``0``-16] | Adsertising instance SID | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | high\_duty | [``0``-1] | Use high\_duty advertising | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | anonymous | [``0``-1] | Use anonymous advertising | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | legacy | [``0``-1] | Use legacy PDUs for advertising | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | include\_tx\_power | [``0``-1] | Include TX power information in advertising PDUs | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | scan\_req\_notif | [``0``-1] | Enable SCAN\_REQ notifications | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| **advertise-set-addr** | | | Configure *random* adress for instance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | instance | [``0``-UINT8\_MAX] | Advertising instance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | addr | XX:XX:XX:XX:XX:XX | Random address | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| **advertise-set-adv-data** | | | Configure advertising instance ADV\_DATA. This allow to configure following TLVs: | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| **advertise-set-scan-rsp** | | | Configure advertising instance SCAN\_RSP. This allow to configure following TLVs: | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | instance | [``0``-UINT8\_MAX] | Advertising instance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | flags | [``0``-UINT8\_MAX] | Flags value | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid16 | [UINT16] | 16-bit UUID value (can be passed multiple times) | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid16\_is\_complete | [``0``-1] | I 16-bit UUID list is complete | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid32 | [UINT32] | 32-bit UUID value (can be passed multiple times) | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid32\_is\_complete | [``0``-1] | I 32-bit UUID list is complete | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid128 | XX:XX:XX:... | 128-bit UUID value (16 bytes) (can be passed multiple times) | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid128\_is\_complete | [``0``-1] | I 128-bit UUID list is complete | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | tx\_power\_level | [-127 - 127] | TX Power level to include | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | appearance | [UINT16] | Appearance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | name | string | Name | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | advertising\_interval | [UINT16] | Advertising interval | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | service\_data\_uuid32 | XX:XX:XX:... | 32-bit UUID service data | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | service\_data\_uuid128 | XX:XX:XX:... | 128-bit UUID service data | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uri | XX:XX:XX:... | URI | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | msg\_data | XX:XX:XX:... | Manufacturer data | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | eddystone\_url | string | Eddystone with specified URL | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| **advertise-start** | | | Start advertising with configured instance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | instance | [``0``-UINT8\_MAX] | Advertising instance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | duration | [``0``-UINT16\_MAX] | Advertising duration in 10ms units. 0 - forver | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | max\_events | [``0``-UINT8\_MAX] | Maximum number of advertising events. 0 - no limit | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| **advertise-stop** | | | Stop advertising | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | instance | [``0``-UINT8\_MAX] | Advertising instance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| **advertise-remove** | | | Remove configured advertising instance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | instance | [``0``-UINT8\_MAX] | Advertising instance | ++------------------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ + +Legacy Advertising with Extended Advertising disabled +----------------------------------------------------- + ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| **Command** | **Parmeters** | \*\* Possible values\*\* | **Description** | ++====================+==========================+============================+=====================================================================================+ +| **advertise** | | | Enable advertising | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | stop | | Stop enabled advertising | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | conn | ``und`` | Connectable mode: undirected | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | non | non-connectable | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | dir | directed | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | discov | ``gen`` | Discoverable mode: general discoverable | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | ltd | limited discoverable | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | non | non-discoverable | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | scannable | [``0``-1] | Use scannable advertising | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | peer\_addr\_type | ``public`` | Remote device public address type | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | random | Remote device random address type | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | public\_id | Remote device public address type (Identity) | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | random\_id | Remote device random address type (Identity) | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | peer\_addr | XX:XX:XX:XX:XX:XX | Remote device address - if provided perform directed advertising | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | own\_addr\_type | ``public`` | Use public address for scan requests | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | random | Use random address for scan requests | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | rpa\_pub | Use RPA address for scan requests (fallback to public if no IRK) | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | rpa\_rnd | Use RPA address for scan requests (fallback to random if no IRK) | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | channel\_map | [``0``-UINT8\_MAX} | Primary advertising channels map. If 0 use all channels. | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | filter | ``none`` | Advertising filter policy - no filtering, no whitelist used | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | scan | process all connection requests but only scans from white list | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | conn | process all scan request but only connection requests from white list | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | | both | ignore all scan and connection requests unless in white list | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | interval\_min | [``0``-UINT32\_MAX] | Minimum advertising interval in 0.625 miliseconds If 0 use stack default. | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | interval\_max | [``0``-UINT32\_MAX] | Maximum advertising interval in 0.625 miliseconds If 0 use stack default. | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | high\_duty | [``0``-1] | Use high\_duty advertising | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | duration | [``1``-INT32\_MAX] | Advertising duration in ms | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| **set-adv-data** | | | Configure advertising instance ADV\_DATA. This allow to configure following TLVs: | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| **set-scan-rsp** | | | Configure advertising instance SCAN\_RSP. This allow to configure following TLVs: | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | flags | [``0``-UINT8\_MAX] | Flags value | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid16 | [UINT16] | 16-bit UUID value (can be passed multiple times) | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid16\_is\_complete | [``0``-1] | I 16-bit UUID list is complete | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid32 | [UINT32] | 32-bit UUID value (can be passed multiple times) | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid32\_is\_complete | [``0``-1] | I 32-bit UUID list is complete | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid128 | XX:XX:XX:... | 128-bit UUID value (16 bytes) (can be passed multiple times) | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uuid128\_is\_complete | [``0``-1] | I 128-bit UUID list is complete | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | tx\_power\_level | [-127 - 127] | TX Power level to include | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | appearance | [UINT16] | Appearance | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | name | string | Name | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | advertising\_interval | [UINT16] | Advertising interval | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | service\_data\_uuid32 | XX:XX:XX:... | 32-bit UUID service data | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | service\_data\_uuid128 | XX:XX:XX:... | 128-bit UUID service data | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | uri | XX:XX:XX:... | URI | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | msg\_data | XX:XX:XX:... | Manufacturer data | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ +| | eddystone\_url | string | Eddystone with specified URL | ++--------------------+--------------------------+----------------------------+-------------------------------------------------------------------------------------+ + +L2CAP Connection Oriented Channels +---------------------------------- + ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| **Command** | **Parmeters** | \*\* Possible values\*\* | **Description** | ++===========================+=================+============================+====================================================+ +| **l2cap-create-server** | | | Create L2CAP server | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| | psm | [UINT16] | PSM | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| **l2cap-connect** | | | Connect to remote L2CAP server | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| | psm | [UINT16] | PSM | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| **l2cap-disconnect** | | | Disconnec from L2CAP server | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| | idx | [UINT16] | L2CAP connection oriented channel identifier | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| **l2cap-send** | | | Send data over connected L2CAP channel | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| | idx | [UINT16] | L2CAP connection oriented channel identifier | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| | bytes | [UINT16] | Number of bytes to send (hardcoded data pattern) | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ +| **l2cap-show-coc** | | | Show connected L2CAP channels | ++---------------------------+-----------------+----------------------------+----------------------------------------------------+ + +Keys storage +------------ + ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| **Command** | **Parmeters** | \*\* Possible values\*\* | **Description** | ++=====================+=================+============================+====================================================+ +| **keystore-add** | | | Add keys to storage | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | type | msec | Master Key | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | | ssec | Slave Key | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | | cccd | Client Characteristic Configuration Descriptor | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | addr | XX:XX:XX:XX:XX:XX | Device address | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | addr\_type | ``public`` | Device address type | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | | random | Use random address for scan requests | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | ediv | [UINT16] | EDIV for LTK to add | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | rand | [UINT64] | Rand for LTK | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | ltk | XX:XX:XX... | LTK (16 bytes) | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | irk | XX:XX:XX... | Identity Resolving Key (16 bytes) | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | csrk | XX:XX:XX... | Connection Signature Resolving Key (16 bytes) | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| **keystore-del** | | | Delete keys from storage | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | type | msec | Master Key | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | | ssec | Slave Key | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | | cccd | Client Characteristic Configuration Descriptor | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | addr | XX:XX:XX:XX:XX:XX | Device address | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | addr\_type | ``public`` | Device address type | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | | random | Use random address for scan requests | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | ediv | [UINT16] | EDIV for LTK to remove | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | rand | [UINT64] | Rand for LTK | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| **keystore-show** | | | Show stored keys | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | type | msec | Master Keys | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | | ssec | Slave Keys | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ +| | | cccd | Client Characteristic Configuration Descriptor s | ++---------------------+-----------------+----------------------------+----------------------------------------------------+ diff --git a/src/libs/mynewt-nimble/docs/btshell/btshell_GATT.rst b/src/libs/mynewt-nimble/docs/btshell/btshell_GATT.rst new file mode 100644 index 00000000..0fe465fe --- /dev/null +++ b/src/libs/mynewt-nimble/docs/btshell/btshell_GATT.rst @@ -0,0 +1,108 @@ +GATT feature API for btshell +============================ + +GATT(GENERIC ATTRIBUTE PROFILE) describes a service framework using the Attribute Protocol for discovering services, +and for reading and writing characteristic values on a peer device. There are 11 features defined in the GATT Profile, +and each of the features is mapped to procedures and sub-procedures: + +Available commands +~~~~~~~~~~~~~~~~~~ + +Parameters default values (if applicable) are marked red. + +Configuration +------------- + ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **Command** | **Parmeters** | \*\* Possible values\*\* | **Description** | ++====================================+=================+============================+===========================================================+ +| **gatt-discover-characteristic** | | | Discover GATT characteristics | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | uuid | [UINT16] | Characteristic UUID | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | start | [UINT16] | Discovery start handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | end | [UINT16] | Discovery end handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-discover-descriptor** | | | Discover GATT descriptors | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | start | [UINT16] | Discovery start handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | end | [UINT16] | Discovery end handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-discover-service** | | | Discover services | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | uuid16 | [UINT16] | Service UUID | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-discover-full** | | | Discover services, characteristic and descriptors | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-find-included-services** | | | Find included services | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | start | [UINT16] | Discovery start handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | end | [UINT16] | Discovery end handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-exchange-mtu** | | | Initiate ATT MTU exchange procedure | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-read** | | | Read attribute | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | long | [``0``-1] | Long read | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | attr | [UINT16] | Attribute handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | offset | [UINT16] | Long read offset value | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | uuid | [UINT16] | Characteristic UUID | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | start | [UINT16] | Discovery start handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | end | [UINT16] | Discovery end handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-notify** | | | Send notification or indication to all subscribed peers | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | attr | [UINT16] | Attribute handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-service-changed** | | | Send Services Changed notification | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | start | [UINT16] | Start handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | end | [UINT16] | End handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-service-visibility** | | | Set service visibility | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | handle | [UINT16] | Service handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | visibility | [``0``-1] | Service visibility | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-show** | | | Show remote devices discovered databases structure | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-show-local** | | | Show local database structure | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| **gatt-write** | | | Write attribute | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | conn | [UINT16] | Connection handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | no\_rsp | [``0``-1] | Use Write Without Response | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | long | [``0``-1] | Use Long Write procedure | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | attr | [UINT16] | Attribute handle | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | offset | [UINT16] | Long write offset value | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ +| | value | XX:XX:XX... | Data to write | ++------------------------------------+-----------------+----------------------------+-----------------------------------------------------------+ diff --git a/src/libs/mynewt-nimble/docs/btshell/btshell_advdata.rst b/src/libs/mynewt-nimble/docs/btshell/btshell_advdata.rst new file mode 100644 index 00000000..eabfcb3b --- /dev/null +++ b/src/libs/mynewt-nimble/docs/btshell/btshell_advdata.rst @@ -0,0 +1,47 @@ +Advertisement Data Fields +------------------------- + +This part defines the advertisement data fields used in the ``btshell`` app. For a complete list of all data types and +formats used for Extended Inquiry Response (EIR), Advertising Data (AD), and OOB data blocks, refer to the Supplement +to the Bluetooth Core Specification, CSSv6, available for download +`here `__. + ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| **Name** | **Definition** | **Details** | **btshell Notes** | ++===========================+=====================================================+===========================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================+==============================================+ +| flags | Indicates basic information about the advertiser. | Flags used over the LE physical channel are: \* Limited Discoverable Mode \* General Discoverable Mode \* BR/EDR Not Supported \* Simultaneous LE and BR/EDR to Same Device Capable (Controller) \* Simultaneous LE and BR/EDR to Same Device Capable (Host) | NimBLE will auto-calculate if set to 0. | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| uuid16 | 16-bit Bluetooth Service UUIDs | Indicates the Service UUID list is incomplete i.e. more 16-bit Service UUIDs available. 16 bit UUIDs shall only be used if they are assigned by the Bluetooth SIG. | Set repeatedly for multiple service UUIDs. | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| uuid16\_is\_complete | 16-bit Bluetooth Service UUIDs | Indicates the Service UUID list is complete. 16 bit UUIDs shall only be used if they are assigned by the Bluetooth SIG. | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| uuid32 | 32-bit Bluetooth Service UUIDs | Indicates the Service UUID list is incomplete i.e. more 32-bit Service UUIDs available. 32 bit UUIDs shall only be used if they are assigned by the Bluetooth SIG. | Set repeatedly for multiple service UUIDs. | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| uuid32\_is\_complete | 32-bit Bluetooth Service UUIDs | Indicates the Service UUID list is complete. 32 bit UUIDs shall only be used if they are assigned by the Bluetooth SIG. | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| uuid128 | Global 128-bit Service UUIDs | More 128-bit Service UUIDs available. | Set repeatedly for multiple service UUIDs. | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| uuid128\_is\_complete | Global 128-bit Service UUIDs | Complete list of 128-bit Service UUIDs | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| tx\_power\_level | TX Power Level | Indicates the transmitted power level of the packet containing the data type. The TX Power Level data type may be used to calculate path loss on a received packet using the following equation: pathloss = Tx Power Level – RSSI where “RSSI” is the received signal strength, in dBm, of the packet received. | NimBLE will auto-calculate if set to -128. | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| slave\_interval\_range | Slave Connection Interval Range | Contains the Peripheral’s preferred connection interval range, for all logical connections. Size: 4 Octets . The first 2 octets defines the minimum value for the connection interval in the following manner: connIntervalmin = Conn\_Interval\_Min \* 1.25 ms Conn\_Interval\_Min range: 0x0006 to 0x0C80 Value of 0xFFFF indicates no specific minimum. The other 2 octets defines the maximum value for the connection interval in the following manner: connIntervalmax = Conn\_Interval\_Max \* 1.25 ms Conn\_Interval\_Max range: 0x0006 to 0x0C80 Conn\_Interval\_Max shall be equal to or greater than the Conn\_Interval\_Min. Value of 0xFFFF indicates no specific maximum. | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| service\_data\_uuid16 | Service Data - 16 bit UUID | Size: 2 or more octets The first 2 octets contain the 16 bit Service UUID followed by additional service data | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| public\_target\_address | Public Target Address | Defines the address of one or more intended recipients of an advertisement when one or more devices were bonded using a public address. This data type shall exist only once. It may be sent in either the Advertising or Scan Response data, but not both. | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| appearance | Appearance | Defines the external appearance of the device. The Appearance data type shall exist only once. It may be sent in either the Advertising or Scan Response data, but not both. | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| advertising\_interval | Advertising Interval | Contains the advInterval value as defined in the Core specification, Volume 6, Part B, Section 4.4.2.2. | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| service\_data\_uuid32 | Service Data - 32 bit UUID | Size: 4 or more octets The first 4 octets contain the 32 bit Service UUID followed by additional service data | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| service\_data\_uuid128 | Service Data - 128 bit UUID | Size: 16 or more octets The first 16 octets contain the 128 bit Service UUID followed by additional service data | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| uri | Uniform Resource Identifier (URI) | Scheme name string and URI as a UTF-8 string | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| mfg\_data | Manufacturer Specific data | Size: 2 or more octets The first 2 octets contain the Company Identifier Code followed by additional manufacturer specific data | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ +| eddystone\_url | | | | ++---------------------------+-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+ diff --git a/src/libs/mynewt-nimble/docs/btshell/btshell_api.rst b/src/libs/mynewt-nimble/docs/btshell/btshell_api.rst new file mode 100644 index 00000000..49605bf4 --- /dev/null +++ b/src/libs/mynewt-nimble/docs/btshell/btshell_api.rst @@ -0,0 +1,153 @@ +API for btshell app +------------------- + +"btshell" is one of the sample applications that come with Mynewt. It is a shell application which provides a basic +interface to the host-side of the BLE stack. "btshell" includes all the possible roles (Central/Peripheral) and they may +be run simultaneously. You can run btshell on a board and issue commands that make it behave as a central or a peripheral +with different peers. + +**btshell** is a new application that uses shell subsystem introduced in Mynewt 1.1 and has updated commands and +parameters names. Thanks to support for tab completion commands names are more descriptive and self-explanatory +without requiring extensive typing. + +Highlighted below are some of the ways you can use the API to establish connections and discover services and +characteristics from peer devices. For descriptions of the full API, go to the next sections on +:doc:`btshell_GAP` and :doc:`btshell_GATT`. + +.. contents:: + :local: + :depth: 2 + +.. toctree:: + :hidden: + :titlesonly: + + GAP + GATT + btshell_advdata + +Set device address. +~~~~~~~~~~~~~~~~~~~ + +On startup, btshell has the following identity address configuration: + +- Public address: None +- Random address: None + +The below ``set`` commands can be used to change the address configuration: + +:: + + set addr_type=public addr= + set addr_type=random addr= + +For example: + +:: + + set addr_type=public addr=01:02:03:04:05:06 + set addr_type=random addr=c1:aa:bb:cc:dd:ee + +The address configuration can be viewed with the ``gatt-show-addr`` command, as follows: + +:: + + gatt-show-addr + public_id_addr=01:02:03:04:05:06 random_id_addr=c1:aa:bb:cc:dd:ee + +Initiate a direct connection to a device +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In this case, your board is acting as a central and initiating a connection with another BLE device. The example +assumes you know the address of the peer, either by scanning for available peers or because you have set up the peer +yourself. + +.. code-block:: none + :emphasize-lines: 1 + + connect peer_addr=d4:f5:13:53:d2:43 + connection established; handle=1 our_ota_addr_type=0 our_ota_addr=0a:0b:0c:0d:0e:0f out_id_addr_type=0 our_id_addr=0a:0b:0c:0d:0e:0f peer_addr_type=0 peer_addr=43:d2:53:13:f5:d4 conn_itvl=40 conn_latency=0 supervision_timeout=256 encrypted=0 authenticated=0 bonded=0 + +The ``handle=1`` in the output indicates that it is connection-1. + +Configure advertisements to include device name +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In this case, your board is acting as a peripheral. + +With Extended Advertising enabled (should be executed after advertise-configure): + +:: + + advertise-set-adv-data name= + +With Extended Advertising disabled: + +:: + + set-adv-data name= + +Begin sending undirected general advertisements +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In this case, your board is acting as a peripheral. + +With Extended Advertising enabled: + +:: + + advertise-configure connectable=1 legacy=1 scannable=1 + advertise-start + +With Extended Advertising disabled: + +:: + + advertise conn=und discov=gen + +Show established connections. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + gatt-show-conn + +Discover and display peer's services, characteristics, and descriptors. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This is how you discover and then display the services of the peer you established earlier across connection-1. + +.. code-block:: none + :emphasize-lines: 1,2 + + gatt-discover-full conn=1 + gatt-show + [ts=132425ssb, mod=64 level=2] CONNECTION: handle=1 addr=d4:f5:13:53:d2:43 + [ts=132428ssb, mod=64 level=2] start=1 end=5 uuid=0x1800 + [ts=132433ssb, mod=64 level=2] start=6 end=16 uuid=0x1808 + [ts=132437ssb, mod=64 level=2] start=17 end=31 uuid=0x180a + [ts=132441ssb, mod=64 level=2] start=32 end=65535 uuid=00000000-0000-1000-1000000000000000 + + +Read an attribute belonging to the peer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + gatt-read conn=1 attr=21 + +Write to an attribute belonging to the peer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + gatt-write conn=1 attr=3 value=0x01:0x02:0x03 + +Perform a passive scan +~~~~~~~~~~~~~~~~~~~~~~ + +This is how you tell your board to listen to all advertisements around it. The duration is specified in ms. + +:: + + scan duration=1000 passive=1 filter=no_wl diff --git a/src/libs/mynewt-nimble/docs/conf.py b/src/libs/mynewt-nimble/docs/conf.py new file mode 100644 index 00000000..0aaf5c83 --- /dev/null +++ b/src/libs/mynewt-nimble/docs/conf.py @@ -0,0 +1,177 @@ +# -*- coding: utf-8 -*- +# +# Mynewt documentation build configuration file, created by +# sphinx-quickstart on Tue Jan 10 11:33:44 2017. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys +sys.path.insert(0, os.path.abspath('_ext')) + + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', 'breathe', 'sphinx.ext.todo', + 'sphinx.ext.extlinks' +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = [] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'NimBLE Bluetooth Stack' +copyright = u'Copyright © 2018 The Apache Software Foundation, Licensed under the Apache License, Version 2.0 Apache and the Apache feather logo are trademarks of The Apache Software Foundation.' +author = u'The Apache Software Foundation' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = u'1.0' +# The full version, including alpha/beta/rc tags. +release = u'1.0.0-b1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'README.rst', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +highlight_language = 'none' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# + +html_theme = 'alabaster' +html_theme_path = [] +html_sidebars = { + '**': [ + 'about.html', + 'navigation.html', + 'relations.html', + 'searchbox.html', + 'donate.html', + ] +} + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +html_theme_options = { +} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = [] + + +# -- Options for HTMLHelp output ------------------------------------------ + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Mynewtdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'Mynewt.tex', u'NimBLE Bluetooth Stack', + u'The Apache Software Foundation', 'manual'), +] + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'mynewt', u'Mynewt Documentation', + [author], 1) +] + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'Mynewt', u'NimBLE Bluetooth Stack', + author, 'Mynewt', 'One line description of project.', + 'Miscellaneous'), +] + +breathe_projects = { + "mynewt": "_build/xml" +} +breathe_default_project = "mynewt" +breathe_domain_by_extension = { + "h" : "c", +} diff --git a/src/libs/mynewt-nimble/docs/doxygen.xml b/src/libs/mynewt-nimble/docs/doxygen.xml new file mode 100644 index 00000000..fb000de3 --- /dev/null +++ b/src/libs/mynewt-nimble/docs/doxygen.xml @@ -0,0 +1,2433 @@ +# Doxyfile 1.8.11 + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "Apache Mynewt" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = docs/ + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = YES + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = nimble/host/include/host \ + nimble/host/mesh/include/mesh + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl, +# *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js. + +FILE_PATTERNS = *.h + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = docs + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = *bin/* \ + *src/ext* \ + *lwip_base* \ + *mbedtls* \ + *.md \ + *.yml + + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = __* + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = YES + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = _build/html/api + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /