Selaa lähdekoodia

【增加】添加基于RT-Thread的开源BLE5.0协议栈:NimBLE

EvalZero 7 vuotta sitten
vanhempi
sitoutus
ebf4924934
100 muutettua tiedostoa jossa 23097 lisäystä ja 4 poistoa
  1. 3 0
      .gitignore
  2. 28 0
      .rat-excludes
  3. 131 0
      .travis.yml
  4. 267 0
      CODING_STANDARDS.md
  5. 158 0
      Kconfig
  6. 18 2
      LICENSE
  7. 8 0
      NOTICE
  8. 3 2
      README.md
  9. 166 0
      SConscript
  10. 37 0
      apps/blecent/pkg.yml
  11. 111 0
      apps/blecent/src/blecent.h
  12. 521 0
      apps/blecent/src/main.c
  13. 209 0
      apps/blecent/src/misc.c
  14. 807 0
      apps/blecent/src/peer.c
  15. 24 0
      apps/blecent/syscfg.yml
  16. 9 0
      apps/blecsc/README.md
  17. 41 0
      apps/blecsc/pkg.yml
  18. 105 0
      apps/blecsc/src/blecsc_sens.h
  19. 385 0
      apps/blecsc/src/gatt_svr.c
  20. 310 0
      apps/blecsc/src/main.c
  21. 38 0
      apps/blecsc/syscfg.yml
  22. 30 0
      apps/blehci/pkg.yml
  23. 33 0
      apps/blehci/src/main.c
  24. 21 0
      apps/blehci/syscfg.yml
  25. 9 0
      apps/blehr/README.md
  26. 41 0
      apps/blehr/pkg.yml
  27. 245 0
      apps/blehr/src/blehr.c
  28. 46 0
      apps/blehr/src/blehr_sens.h
  29. 177 0
      apps/blehr/src/gatt_svr.c
  30. 35 0
      apps/blehr/syscfg.yml
  31. 37 0
      apps/blemesh/pkg.yml
  32. 470 0
      apps/blemesh/src/main.c
  33. 52 0
      apps/blemesh/syscfg.yml
  34. 37 0
      apps/blemesh_light/pkg.yml
  35. 242 0
      apps/blemesh_light/src/light_model.c
  36. 37 0
      apps/blemesh_light/src/light_model.h
  37. 128 0
      apps/blemesh_light/src/main.c
  38. 133 0
      apps/blemesh_light/src/ws2812.c
  39. 42 0
      apps/blemesh_light/src/ws2812.h
  40. 63 0
      apps/blemesh_light/syscfg.yml
  41. 79 0
      apps/blemesh_models_example_1/README.md
  42. 34 0
      apps/blemesh_models_example_1/pkg.yml
  43. 690 0
      apps/blemesh_models_example_1/src/main.c
  44. 51 0
      apps/blemesh_models_example_1/syscfg.yml
  45. 101 0
      apps/blemesh_models_example_2/README.md
  46. 38 0
      apps/blemesh_models_example_2/pkg.yml
  47. 93 0
      apps/blemesh_models_example_2/src/ble_mesh.c
  48. 34 0
      apps/blemesh_models_example_2/src/ble_mesh.h
  49. 16 0
      apps/blemesh_models_example_2/src/common.h
  50. 1944 0
      apps/blemesh_models_example_2/src/device_composition.c
  51. 182 0
      apps/blemesh_models_example_2/src/device_composition.h
  52. 198 0
      apps/blemesh_models_example_2/src/main.c
  53. 241 0
      apps/blemesh_models_example_2/src/publisher.c
  54. 27 0
      apps/blemesh_models_example_2/src/publisher.h
  55. 183 0
      apps/blemesh_models_example_2/src/state_binding.c
  56. 13 0
      apps/blemesh_models_example_2/src/state_binding.h
  57. 678 0
      apps/blemesh_models_example_2/src/transition.c
  58. 58 0
      apps/blemesh_models_example_2/src/transition.h
  59. 54 0
      apps/blemesh_models_example_2/syscfg.yml
  60. 37 0
      apps/blemesh_shell/pkg.yml
  61. 112 0
      apps/blemesh_shell/src/main.c
  62. 68 0
      apps/blemesh_shell/syscfg.yml
  63. 44 0
      apps/bleprph/pkg.yml
  64. 61 0
      apps/bleprph/src/bleprph.h
  65. 204 0
      apps/bleprph/src/gatt_svr.c
  66. 347 0
      apps/bleprph/src/main.c
  67. 43 0
      apps/bleprph/src/misc.c
  68. 128 0
      apps/bleprph/src/phy.c
  69. 63 0
      apps/bleprph/syscfg.yml
  70. 38 0
      apps/bletest/pkg.yml
  71. 782 0
      apps/bletest/src/bletest_hci.c
  72. 86 0
      apps/bletest/src/bletest_priv.h
  73. 1323 0
      apps/bletest/src/main.c
  74. 33 0
      apps/bletest/syscfg.yml
  75. 39 0
      apps/btshell/pkg.yml
  76. 201 0
      apps/btshell/src/btshell.h
  77. 3934 0
      apps/btshell/src/cmd.c
  78. 64 0
      apps/btshell/src/cmd.h
  79. 587 0
      apps/btshell/src/cmd_gatt.c
  80. 39 0
      apps/btshell/src/cmd_gatt.h
  81. 261 0
      apps/btshell/src/cmd_l2cap.c
  82. 32 0
      apps/btshell/src/cmd_l2cap.h
  83. 595 0
      apps/btshell/src/gatt_svr.c
  84. 2251 0
      apps/btshell/src/main.c
  85. 162 0
      apps/btshell/src/misc.c
  86. 587 0
      apps/btshell/src/parse.c
  87. 42 0
      apps/btshell/syscfg.yml
  88. 41 0
      apps/ext_advertiser/pkg.yml
  89. 464 0
      apps/ext_advertiser/src/main.c
  90. 186 0
      apps/ext_advertiser/src/patterns.h
  91. 45 0
      apps/ext_advertiser/syscfg.yml
  92. 71 0
      apps/ibeacon/src/ibeacon.c
  93. 5 0
      docs/.gitignore
  94. 25 0
      docs/Makefile
  95. 33 0
      docs/README.rst
  96. 22 0
      docs/ble_hs/ble_att.rst
  97. 14 0
      docs/ble_hs/ble_gap.rst
  98. 15 0
      docs/ble_hs/ble_gattc.rst
  99. 15 0
      docs/ble_hs/ble_gatts.rst
  100. 27 0
      docs/ble_hs/ble_hs.rst

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+# Dummy NPL build
+*.o
+/porting/examples/dummy/dummy

+ 28 - 0
.rat-excludes

@@ -0,0 +1,28 @@
+# 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
+
+# tinycrypt - BSD License.
+tinycrypt
+
+# Bluetooth Mesh - Apache 2.0 License
+mesh
+
+# Queue implementation - BSD License
+queue.h
+
+# mbuf implementation - BSD License
+os_mbuf.c

+ 131 - 0
.travis.yml

@@ -0,0 +1,131 @@
+# 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.10"
+
+git:
+  depth: false
+
+matrix:
+  include:
+    # newt build <targets>
+    - 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
+
+before_install:
+  - printenv
+  - export GOPATH=$HOME/gopath
+  - go version
+
+install:
+  - git clone https://github.com/runtimeco/mynewt-travis-ci $HOME/ci
+  - chmod +x $HOME/ci/*.sh
+  - $HOME/ci/${TRAVIS_OS_NAME}_travis_install.sh
+
+before_script:
+  - 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
+  - cp -R $HOME/ci/mynewt-nimble-targets targets
+  - $HOME/ci/prepare_test.sh $VM_AMOUNT
+  - mkdir -p repos && pushd repos/
+  - git clone https://github.com/apache/mynewt-core apache-mynewt-core
+  - popd
+
+script:
+  - $HOME/ci/run_test.sh
+
+cache:
+  directories:
+  - $HOME/TOOLCHAIN
+  - $HOME/Library/Caches/Homebrew

+ 267 - 0
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.
+

+ 158 - 0
Kconfig

@@ -0,0 +1,158 @@
+# Kconfig - Bluetooth configuration options
+#
+# Copyright (c) 2016 Intel Corporation
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+menu "Nimble Bluetooth stack"
+
+config PKG_USING_NIMBLE
+    bool "Enable NimBLE Bluetooth stack"
+    help
+        This option enables NimBLE Bluetooth stack.
+
+    if PKG_USING_NIMBLE
+    
+        config NIMBT_ROLE_PERIPHERAL
+            bool "Peripheral Role support"
+            select NIMBT_ROLE_BROADCASTER
+            default n
+            help
+              Select this for NimBLE Peripheral role support.
+
+        config NIMBT_ROLE_CENTRAL
+            bool "Central Role support"
+            select NIMBLE_ROLE_OBSERVER
+            default n
+            help
+              Select this for NimBLE Central role support.
+
+        menu "Broadcaster"
+            visible if !NIMBT_ROLE_PERIPHERAL
+
+        config NIMBT_ROLE_BROADCASTER
+            bool "Broadcaster Role support"
+            default y if !NIMBLE_ROLE_OBSERVER
+            default n
+            help
+              Select this for NimBLE Broadcaster role support.
+
+        endmenu
+
+        menu "Observer"
+            visible if !NIMBT_ROLE_CENTRAL
+
+        config NIMBT_ROLE_OBSERVER
+            bool "Observer Role support"
+            default n
+            help
+              Select this for NimBLE Observer role support.
+
+        endmenu  
+        
+        
+        menu "Common configuration"
+            
+        endmenu 
+        
+        
+        menu "Host stack configuration"
+        
+            config NIMBLE_HOST
+                bool "Enable Host support"
+                default y
+                help
+                    Enable support for Host Stack.
+                
+        endmenu
+        
+        
+        menu "Controller configuretion"
+            
+            config NIMBLE_CTLR
+                bool "Enabel Controller support"
+                default n
+                help
+                  Enable support for native controller implementations.
+                
+                if NIMBLE_CTLR
+                
+                    choice
+                    
+                        prompt "BLE device support"
+                        default NIMBLE_BSP_NRF52
+                        help
+                            Select the device controller layer support.
+                    
+                        config NIMBLE_BSP_NRF52
+                            bool "NRF52xxx"
+                    
+                    endchoice
+                
+                endif 
+            
+        endmenu
+    
+    
+        choice
+            prompt "Log level"
+            default NIMBLE_DEBUG_LEVEL_I
+            help
+                select the NIMBLE log level
+            
+            config NIMBLE_DEBUG_LEVEL_O
+                bool "OFF"
+
+            config NIMBLE_DEBUG_LEVEL_E
+                bool "ERROR"
+            
+            config NIMBLE_DEBUG_LEVEL_W
+                bool "WARING"
+            
+            config NIMBLE_DEBUG_LEVEL_I
+                bool "INFO"
+                
+            config NIMBLE_DEBUG_LEVEL_D
+                bool "DEBUG"
+        
+        endchoice
+        
+        config NIMBLE_DEBUG_LEVEL
+            # Virtual/hidden option
+            int
+            default -1 if NIMBLE_DEBUG_LEVEL_O
+            default 0 if NIMBLE_DEBUG_LEVEL_E
+            default 1 if NIMBLE_DEBUG_LEVEL_W
+            default 2 if NIMBLE_DEBUG_LEVEL_I
+            default 3 if NIMBLE_DEBUG_LEVEL_D
+            default 2
+    
+        choice
+            prompt "Samples"
+            default NIMBLE_SAMPLE_DISABLE
+            help
+                select the NimBLE samples
+            
+            config NIMBLE_SAMPLE_DISABLE
+                bool "Not enable sample"
+            
+            config NIMBLE_SAMPLE_PER_HR
+                bool "peripheral heart-rate"
+                select NIMBT_ROLE_PERIPHERAL
+                select NIMBLE_CTLR
+            
+            config NIMBLE_SAMPLE_PER_LED
+                bool "peripheral led central"
+                select NIMBT_ROLE_PERIPHERAL
+                select NIMBLE_CTLR
+            
+            config NIMBLE_SAMPLE_BEACON
+                bool "beacon"
+                select NIMBT_ROLE_BROADCASTER
+                
+        endchoice
+    
+    endif
+
+endmenu

+ 18 - 2
LICENSE

@@ -178,7 +178,7 @@
    APPENDIX: How to apply the Apache License to your work.
    APPENDIX: How to apply the Apache License to your work.
 
 
       To apply the Apache License to your work, attach the following
       To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
+      boilerplate notice, with the fields enclosed by brackets "{}"
       replaced with your own identifying information. (Don't include
       replaced with your own identifying information. (Don't include
       the brackets!)  The text should be enclosed in the appropriate
       the brackets!)  The text should be enclosed in the appropriate
       comment syntax for the file format. We also recommend that a
       comment syntax for the file format. We also recommend that a
@@ -186,7 +186,7 @@
       same "printed page" as the copyright notice for easier
       same "printed page" as the copyright notice for easier
       identification within third-party archives.
       identification within third-party archives.
 
 
-   Copyright [yyyy] [name of copyright owner]
+   Copyright {yyyy} {name of copyright owner}
 
 
    Licensed under the Apache License, Version 2.0 (the "License");
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    you may not use this file except in compliance with the License.
@@ -199,3 +199,19 @@
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    See the License for the specific language governing permissions and
    limitations under the License.
    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

+ 8 - 0
NOTICE

@@ -0,0 +1,8 @@
+Apache Mynewt NimBLE
+Copyright 2015-2018 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.

+ 3 - 2
README.md

@@ -1,2 +1,3 @@
-# nimble
-an Apache open-source Bluetooth 5.0 stack porting on RT-Thread
+# NimBLE
+
+Apache NimBLE is an open-source Bluetooth 5.0 stack (both Host & Controller) porting

+ 166 - 0
SConscript

@@ -0,0 +1,166 @@
+from building import *
+import rtconfig
+
+cwd = GetCurrentDir()
+path = [cwd]
+src = []
+
+path += [
+    cwd + '/nimble/include',
+    cwd + '/nimble/host/include',
+    cwd + '/nimble/host/src',
+    cwd + '/nimble/host/services/ans/include',
+    cwd + '/nimble/host/services/bas/include',
+    cwd + '/nimble/host/services/bleuart/include',
+    cwd + '/nimble/host/services/gap/include',
+    cwd + '/nimble/host/services/gatt/include',
+    cwd + '/nimble/host/services/ias/include',
+    cwd + '/nimble/host/services/lls/include',
+    cwd + '/nimble/host/services/tps/include',
+    cwd + '/nimble/host/store/ram/include',
+    cwd + '/nimble/host/util/include',
+    cwd + '/porting/nimble/include',
+    cwd + '/porting/npl/rtthread/include']
+
+src += Split('''
+    nimble/host/src/ble_att.c
+    nimble/host/src/ble_att_clt.c
+    nimble/host/src/ble_att_cmd.c
+    nimble/host/src/ble_att_svr.c
+    nimble/host/src/ble_eddystone.c
+    nimble/host/src/ble_gap.c
+    nimble/host/src/ble_gattc.c
+    nimble/host/src/ble_gatts.c
+    nimble/host/src/ble_hs_adv.c
+    nimble/host/src/ble_hs_atomic.c
+    nimble/host/src/ble_hs.c
+    nimble/host/src/ble_hs_cfg.c
+    nimble/host/src/ble_hs_conn.c
+    nimble/host/src/ble_hs_dbg.c
+    nimble/host/src/ble_hs_flow.c
+    nimble/host/src/ble_hs_hci.c
+    nimble/host/src/ble_hs_hci_cmd.c
+    nimble/host/src/ble_hs_hci_evt.c
+    nimble/host/src/ble_hs_hci_util.c
+    nimble/host/src/ble_hs_id.c
+    nimble/host/src/ble_hs_log.c
+    nimble/host/src/ble_hs_mbuf.c
+    nimble/host/src/ble_hs_mqueue.c
+    nimble/host/src/ble_hs_misc.c
+    nimble/host/src/ble_hs_pvcy.c
+    nimble/host/src/ble_hs_startup.c
+    nimble/host/src/ble_hs_stop.c
+    nimble/host/src/ble_ibeacon.c
+    nimble/host/src/ble_l2cap.c
+    nimble/host/src/ble_l2cap_coc.c
+    nimble/host/src/ble_l2cap_sig.c
+    nimble/host/src/ble_l2cap_sig_cmd.c
+    nimble/host/src/ble_monitor.c
+    nimble/host/src/ble_sm_alg.c
+    nimble/host/src/ble_sm.c
+    nimble/host/src/ble_sm_cmd.c
+    nimble/host/src/ble_sm_lgcy.c
+    nimble/host/src/ble_sm_sc.c
+    nimble/host/src/ble_store.c
+    nimble/host/src/ble_store_util.c
+    nimble/host/src/ble_uuid.c
+    nimble/host/services/ans/src/ble_svc_ans.c
+    nimble/host/services/bas/src/ble_svc_bas.c
+    nimble/host/services/gap/src/ble_svc_gap.c
+    nimble/host/services/gatt/src/ble_svc_gatt.c
+    nimble/host/services/ias/src/ble_svc_ias.c
+    nimble/host/services/lls/src/ble_svc_lls.c
+    nimble/host/services/tps/src/ble_svc_tps.c
+    nimble/host/store/ram/src/ble_store_ram.c
+    nimble/host/util/src/addr.c
+    nimble/src/ble_util.c
+    ''')
+
+# Few utils and data structures copied from Mynewt
+src += Split('''
+    porting/nimble/src/nimble_port.c''')
+
+# Few utils and data structures copied from Mynewt
+src += Split('''
+    porting/nimble/src/endian.c
+    porting/nimble/src/mem.c
+    porting/nimble/src/os_mbuf.c
+    porting/nimble/src/os_mempool.c
+    porting/nimble/src/os_msys_init.c
+    porting/npl/rtthread/src/modlog.c
+    porting/npl/rtthread/src/nimble_port_rtthread.c
+    porting/npl/rtthread/src/npl_os_rtthread.c
+    ''')
+
+if GetDepend(['NIMBLE_BSP_NRF52']):
+    src += Glob('porting/npl/rtthread/src/nrf5x_isr.c')
+
+# tinycrypt
+path += [cwd + '/ext/tinycrypt/include']
+
+src += Split('''
+    ext/tinycrypt/src/aes_decrypt.c
+    ext/tinycrypt/src/aes_encrypt.c
+    ext/tinycrypt/src/cmac_mode.c
+    ext/tinycrypt/src/ecc.c
+    ext/tinycrypt/src/ecc_dh.c
+    ext/tinycrypt/src/utils.c
+    ''')
+
+# controller
+if GetDepend(['NIMBLE_CTLR']):
+    CPPDEFINES = ['NIMBLE_CFG_CONTROLLER=1']
+else:
+    CPPDEFINES = ['']
+
+if GetDepend(['NIMBLE_CTLR']):
+    path += [
+        cwd + '/nimble/transport/ram/include',
+        cwd + '/nimble/controller/include',
+        cwd + '/nimble/drivers/nrf52/include']
+
+    src += Split("""
+        nimble/transport/ram/src/ble_hci_ram.c
+        nimble/controller/src/ble_ll_sched.c
+        nimble/controller/src/ble_ll_xcvr.c
+        nimble/controller/src/ble_ll_whitelist.c
+        nimble/controller/src/ble_ll_ctrl.c
+        nimble/controller/src/ble_ll_hci.c
+        nimble/controller/src/ble_ll_supp_cmd.c
+        nimble/controller/src/ble_ll_adv.c
+        nimble/controller/src/ble_ll_conn.c
+        nimble/controller/src/ble_ll_resolv.c
+        nimble/controller/src/ble_ll_conn_hci.c
+        nimble/controller/src/ble_ll_rand.c
+        nimble/controller/src/ble_ll.c
+        nimble/controller/src/ble_ll_scan.c
+        nimble/controller/src/ble_ll_dtm.c
+        nimble/controller/src/ble_ll_hci_ev.c
+        nimble/drivers/nrf52/src/ble_hw.c
+        nimble/drivers/nrf52/src/ble_phy.c
+        porting/nimble/src/os_cputime.c
+        porting/nimble/src/os_cputime_pwr2.c
+        porting/nimble/src/hal_timer.c
+        """)
+
+# peripheral heart-rate sample
+if GetDepend(['NIMBLE_SAMPLE_PER_HR']):
+    path += [cwd + '/apps/blehr/src']
+    
+    src += Split("""
+        apps/blehr/src/blehr.c
+        apps/blehr/src/gatt_svr.c
+        """)
+
+# beacon sample
+if GetDepend(['NIMBLE_SAMPLE_BEACON']):
+    src += Glob('apps/ibeacon/src/ibeacon.c')
+
+LOCAL_CCFLAGS = ''
+
+if rtconfig.CROSS_TOOL == 'keil':
+    LOCAL_CCFLAGS += ' --gnu --diag_suppress=111'
+    
+group = DefineGroup('nimble', src, depend = ['PKG_USING_NIMBLE'], CPPPATH = path, CPPDEFINES = CPPDEFINES, LOCAL_CCFLAGS = LOCAL_CCFLAGS)
+
+Return('group')

+ 37 - 0
apps/blecent/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/blecent
+pkg.type: app
+pkg.description: Simple BLE central application.
+pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
+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/controller
+    - nimble/host
+    - nimble/host/util
+    - nimble/host/services/gap
+    - nimble/host/services/gatt
+    - nimble/host/store/ram
+    - nimble/transport/ram

+ 111 - 0
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

+ 521 - 0
apps/blecent/src/main.c

@@ -0,0 +1,521 @@
+/*
+ * 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 <assert.h>
+#include <string.h>
+#include "os/mynewt.h"
+#include "bsp/bsp.h"
+
+/* BLE */
+#include "nimble/ble.h"
+#include "controller/ble_ll.h"
+#include "host/ble_hs.h"
+#include "host/util/util.h"
+
+/* RAM HCI transport. */
+#include "transport/ram/ble_hci_ram.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)
+{
+    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;
+    }
+
+    /* Try to connect the the advertiser.  Allow 30 seconds (30000 ms) for
+     * timeout.
+     */
+    rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &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",
+                    disc->addr.type, addr_str(disc->addr.val));
+        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;
+}

+ 209 - 0
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 <assert.h>
+#include <stdio.h>
+#include <string.h>
+#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");
+    }
+}

+ 807 - 0
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 <assert.h>
+#include <string.h>
+#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_def_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;
+}

+ 24 - 0
apps/blecent/syscfg.yml

@@ -0,0 +1,24 @@
+# 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

+ 9 - 0
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.
+
+

+ 41 - 0
apps/blecsc/pkg.yml

@@ -0,0 +1,41 @@
+# 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/boot/bootutil"
+    - "@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

+ 105 - 0
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

+ 385 - 0
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 <assert.h>
+#include <stdio.h>
+#include <string.h>
+#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;
+}
+

+ 310 - 0
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 <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#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()
+{
+    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;
+}
+

+ 38 - 0
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

+ 30 - 0
apps/blehci/pkg.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.
+#
+pkg.name: apps/blehci
+pkg.type: app
+pkg.description: BLE controller application exposing HCI over UART
+pkg.author: "Johan Hedberg <johan.hedberg@intel.com>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps:
+    - "@apache-mynewt-core/sys/console/stub"
+    - "@apache-mynewt-core/sys/stats/full"
+    - "@apache-mynewt-core/kernel/os"
+    - nimble/controller
+    - nimble/transport/uart

+ 33 - 0
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 <assert.h>
+#include "os/mynewt.h"
+
+int
+main(void)
+{
+    /* Initialize OS */
+    sysinit();
+
+    while (1) {
+        os_eventq_run(os_eventq_dflt_get());
+    }
+    return 0;
+}

+ 21 - 0
apps/blehci/syscfg.yml

@@ -0,0 +1,21 @@
+# 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

+ 9 - 0
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.
+
+

+ 41 - 0
apps/blehr/pkg.yml

@@ -0,0 +1,41 @@
+# 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/boot/bootutil"
+    - "@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

+ 245 - 0
apps/blehr/src/blehr.c

@@ -0,0 +1,245 @@
+/*
+ * 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 <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <rtthread.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;
+static uint16_t notify_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 ble_npl_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)
+{
+    ble_npl_callout_stop(&blehr_tx_timer);
+}
+
+/* Reset heartrate measurment */
+static void
+blehr_tx_hrate_reset(void)
+{
+    int rc;
+
+    rc = ble_npl_callout_reset(&blehr_tx_timer, RT_TICK_PER_SECOND);
+    assert(rc == 0);
+}
+
+/* This functions simulates heart beat and notifies it to the client */
+static void
+blehr_tx_hrate(struct ble_npl_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(notify_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();
+        }
+        break;
+
+    case BLE_GAP_EVENT_DISCONNECT:
+        MODLOG_DFLT(INFO, "disconnect; reason=%d\n", event->disconnect.reason);
+
+        /* 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;
+            notify_conn_handle = event->subscribe.conn_handle;
+            blehr_tx_hrate_reset();
+        } else if (event->subscribe.attr_handle != hrs_hrm_handle) {
+            notify_state = event->subscribe.cur_notify;
+            notify_conn_handle = 0;
+            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();
+}
+
+extern int nimble_ble_enable(void);
+extern struct ble_npl_eventq *nimble_port_get_dflt_eventq(void);
+static int ble_hr(void)
+{
+    int rc;
+    static int init_flag = 0;
+
+    if (init_flag)
+        return 0;
+    init_flag = 1;
+
+    /* Initialize the NimBLE host configuration */
+    ble_hs_cfg.sync_cb = blehr_on_sync;
+
+    ble_npl_callout_init(&blehr_tx_timer, nimble_port_get_dflt_eventq(),
+                    blehr_tx_hrate, NULL);
+
+    rc = gatt_svr_init();
+
+    /* Set the default device name */
+    rc = ble_svc_gap_device_name_set(device_name);
+    RT_ASSERT(rc == 0);
+
+    nimble_ble_enable();
+    
+    return 0;
+}
+MSH_CMD_EXPORT_ALIAS(ble_hr, ble_hr, "bluetoooth peripheral heartrate");

+ 46 - 0
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

+ 177 - 0
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 <assert.h>
+#include <stdio.h>
+#include <string.h>
+#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;
+}
+

+ 35 - 0
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})

+ 37 - 0
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 <lukasz.rymanowski@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/shell"
+    - nimble/controller
+    - nimble/host
+    - nimble/host/services/gap
+    - nimble/host/services/gatt
+    - nimble/host/store/ram
+    - nimble/transport/ram

+ 470 - 0
apps/blemesh/src/main.c

@@ -0,0 +1,470 @@
+/*
+ * 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 <assert.h>
+#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"
+
+#define BT_DBG_ENABLED (MYNEWT_VAL(BLE_MESH_DEBUG))
+
+/* 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;
+}

+ 52 - 0
apps/blemesh/syscfg.yml

@@ -0,0 +1,52 @@
+# 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
+
+    # Newtmgr is not supported in this app, so disable newtmgr-over-shell.
+    SHELL_NEWTMGR: 0
+
+    BLE_MESH: 1
+    MSYS_1_BLOCK_COUNT: 48
+
+    BLE_MESH_ADV_BUF_COUNT: 20
+    BLE_MESH_TX_SEG_MAX: 6
+
+    BLE_MESH_DEBUG: 1
+    BLE_MESH_DEBUG_NET: 1
+    BLE_MESH_DEBUG_TRANS: 1
+    BLE_MESH_DEBUG_BEACON: 1
+    BLE_MESH_DEBUG_CRYPTO: 1
+    BLE_MESH_DEBUG_PROV: 1
+    BLE_MESH_DEBUG_ACCESS: 1
+    BLE_MESH_DEBUG_MODEL: 1
+    BLE_MESH_DEBUG_ADV: 1
+    BLE_MESH_DEBUG_LOW_POWER: 1
+    BLE_MESH_DEBUG_FRIEND: 1
+    BLE_MESH_DEBUG_PROXY: 1
+
+    BLE_MESH_SETTINGS: 0
+    CONFIG_NFFS: 0

+ 37 - 0
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 <michal.narajowski@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/shell"
+    - nimble/controller
+    - nimble/host
+    - nimble/host/services/gap
+    - nimble/host/services/gatt
+    - nimble/host/store/ram
+    - nimble/transport/ram

+ 242 - 0
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
+}
+

+ 37 - 0
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

+ 128 - 0
apps/blemesh_light/src/main.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 <assert.h>
+#include "sysinit/sysinit.h"
+#include "os/os.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"
+#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 struct bt_mesh_gen_onoff_srv_cb gen_onoff_srv_cb = {
+        .get = light_model_gen_onoff_get,
+        .set = light_model_gen_onoff_set,
+};
+static struct bt_mesh_gen_level_srv_cb gen_level_srv_cb = {
+        .get = light_model_gen_level_get,
+        .set = light_model_gen_level_set,
+};
+static struct bt_mesh_light_lightness_srv_cb light_lightness_srv_cb = {
+        .get = light_model_light_lightness_get,
+        .set = light_model_light_lightness_set,
+};
+
+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(&gen_onoff_srv_cb);
+    bt_mesh_set_gen_level_srv_cb(&gen_level_srv_cb);
+    bt_mesh_set_light_lightness_srv_cb(&light_lightness_srv_cb);
+
+    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;
+}

+ 133 - 0
apps/blemesh_light/src/ws2812.c

@@ -0,0 +1,133 @@
+/**
+ * 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 <assert.h>
+#include <string.h>
+#include <stdio.h>
+#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;
+}

+ 42 - 0
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 <stddef.h>
+#include <stdint.h>
+
+#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__ */

+ 63 - 0
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
+
+    # Newtmgr is not supported in this app, so disable newtmgr-over-shell.
+    SHELL_NEWTMGR: 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
+

+ 79 - 0
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 <discovered UUID>
+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.

+ 34 - 0
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 <michal.narajowski@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/stats/full"
+    - nimble/controller
+    - nimble/host
+    - nimble/host/services/gap
+    - nimble/host/services/gatt
+    - nimble/transport/ram

+ 690 - 0
apps/blemesh_models_example_1/src/main.c

@@ -0,0 +1,690 @@
+/* main.c - Application main entry point */
+
+/*
+ * 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 ble_npl_callout button_work;
+    struct k_delayed_work 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) {
+    	k_delayed_work_submit(&sw.button_timer, 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 ble_npl_event *work)
+{
+    struct sw *button_sw = work->ev.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;
+    k_work_submit(&sw.button_work);
+}
+
+/*
+ * Button Pressed Worker Task
+ */
+
+static void button_pressed_worker(struct ble_npl_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.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*/
+    k_work_init(&sw.button_work, button_pressed_worker);
+    k_work_add_arg(&sw.button_work, &sw);
+
+    /* Initialize button count timer */
+    k_delayed_work_init(&sw.button_timer, button_cnt_timer);
+    k_delayed_work_add_arg(&sw.button_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;
+}

+ 51 - 0
apps/blemesh_models_example_1/syscfg.yml

@@ -0,0 +1,51 @@
+# 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
+
+    # Newtmgr is not supported in this app, so disable newtmgr-over-shell.
+    SHELL_NEWTMGR: 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
+
+    BLE_MESH_DEBUG: 0
+    BLE_MESH_DEBUG_NET: 0
+    BLE_MESH_DEBUG_TRANS: 0
+    BLE_MESH_DEBUG_BEACON: 0
+    BLE_MESH_DEBUG_CRYPTO: 0
+    BLE_MESH_DEBUG_PROV: 0
+    BLE_MESH_DEBUG_ACCESS: 0
+    BLE_MESH_DEBUG_MODEL: 0
+    BLE_MESH_DEBUG_ADV: 0
+    BLE_MESH_DEBUG_LOW_POWER: 0
+    BLE_MESH_DEBUG_FRIEND: 0
+    BLE_MESH_DEBUG_PROXY: 0

+ 101 - 0
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 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 OnOff Server
+- Generic OnOff Client
+- 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 <discovered UUID>
+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.
+

+ 38 - 0
apps/blemesh_models_example_2/pkg.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.
+#
+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 <michal.narajowski@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/stats/full"
+    - nimble/controller
+    - nimble/host
+    - nimble/host/services/gap
+    - nimble/host/services/gatt
+    - nimble/transport/ram
+
+pkg.lflags:
+    - -DFLOAT_SUPPORT
+    - -lm

+ 93 - 0
apps/blemesh_models_example_2/src/ble_mesh.c

@@ -0,0 +1,93 @@
+/* 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 "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)
+{
+}
+
+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");
+}

+ 34 - 0
apps/blemesh_models_example_2/src/ble_mesh.h

@@ -0,0 +1,34 @@
+/* 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)
+
+void blemesh_on_reset(int reason);
+void blemesh_on_sync(void);
+void init_pub(void);
+
+#endif
+

+ 16 - 0
apps/blemesh_models_example_2/src/common.h

@@ -0,0 +1,16 @@
+/* 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
+
+extern int button_device[];
+extern int led_device[];
+
+void update_light_state(void);
+
+#endif

+ 1944 - 0
apps/blemesh_models_example_2/src/device_composition.c

@@ -0,0 +1,1944 @@
+/* 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 "common.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_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),
+};
+
+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_onoff_srv_pub_s0;
+static struct bt_mesh_model_pub gen_onoff_cli_pub_s0;
+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_onoff_srv_pub_s0;
+static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_cli_pub_s0;
+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_onoff_srv_pub_s0		= NET_BUF_SIMPLE(2 + 3);
+	bt_mesh_pub_msg_gen_onoff_cli_pub_s0		= NET_BUF_SIMPLE(2 + 4);
+	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_onoff_srv_pub_s0.msg		= bt_mesh_pub_msg_gen_onoff_srv_pub_s0;
+	gen_onoff_cli_pub_s0.msg		= bt_mesh_pub_msg_gen_onoff_cli_pub_s0;
+	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;
+
+struct generic_level_state gen_level_srv_root_user_data;
+
+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;
+
+struct light_ctl_state light_ctl_srv_user_data;
+
+struct vendor_state vnd_user_data;
+
+struct generic_onoff_state gen_onoff_srv_s0_user_data;
+
+struct generic_level_state gen_level_srv_s0_user_data;
+/* 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->is_optional_para_available) {
+		net_buf_simple_add_u8(msg, state->target_onoff);
+		net_buf_simple_add_u8(msg, state->tt);
+	}
+
+	state->is_optional_para_available = 0x00;
+
+	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+		printk("Unable to send GEN_ONOFF_SRV Status response\n");
+	}
+}
+
+static bool gen_onoff_setunack(struct bt_mesh_model *model,
+			    struct bt_mesh_msg_ctx *ctx,
+			    struct os_mbuf *buf)
+{
+	u8_t tid, onoff;
+	s64_t now;
+	struct os_mbuf *msg = model->pub->msg;
+	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 false;
+	}
+
+	now = k_uptime_get();
+	if (state->last_tid == tid && state->last_tx_addr == ctx->addr &&
+	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+		return true;
+	}
+
+	state->is_optional_para_available = 0x01;
+
+	switch (buf->om_len) {
+	case 0x00:	/* No optional fields are available */
+		state->tt = default_tt;
+		state->delay = 0;
+		state->is_optional_para_available = 0x00;
+		break;
+	case 0x02:	/* Optional fields are available */
+		state->tt = net_buf_simple_pull_u8(buf);
+		state->delay = net_buf_simple_pull_u8(buf);
+		break;
+	default:
+		return false;
+	}
+
+	enable_transition = DISABLE_TRANSITION;
+
+	state->last_tid = tid;
+	state->last_tx_addr = ctx->addr;
+	state->last_msg_timestamp = now;
+	state->target_onoff = onoff;
+
+	onoff_tt_values(state);
+
+	if (state->tt_counter == 0) {
+		state->onoff = onoff;
+	}
+
+	if (bt_mesh_model_elem(model)->addr == elements[0].addr) {
+		/* Root element */
+		onoff_handler(state);
+	} else if (bt_mesh_model_elem(model)->addr == elements[1].addr) {
+		/* Secondary element */
+		printk("Hello World\n");
+	}
+
+	if (model->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
+		int err;
+
+		bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_ONOFF_STATUS);
+		net_buf_simple_add_u8(msg, state->onoff);
+
+		err = bt_mesh_model_publish(model);
+		if (err) {
+			printk("bt_mesh_model_publish err %d\n", err);
+		}
+	}
+
+	return true;
+}
+
+static void gen_onoff_set_unack(struct bt_mesh_model *model,
+				struct bt_mesh_msg_ctx *ctx,
+				struct os_mbuf *buf)
+{
+	gen_onoff_setunack(model, ctx, buf);
+}
+
+static void gen_onoff_set(struct bt_mesh_model *model,
+			  struct bt_mesh_msg_ctx *ctx,
+			  struct os_mbuf *buf)
+{
+	if (gen_onoff_setunack(model, ctx, buf) == true) {
+		gen_onoff_get(model, ctx, buf);
+	}
+}
+
+/* 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->is_optional_para_available == 0x01) {
+		net_buf_simple_add_le16(msg, state->target_level);
+		net_buf_simple_add_u8(msg, state->tt);
+	} else if (state->is_optional_para_available == 0x02) {
+
+		if (state->last_delta < 0) {
+			net_buf_simple_add_le16(msg, INT16_MIN);
+		} else if (state->last_delta > 0) {
+			net_buf_simple_add_le16(msg, INT16_MAX);
+		}
+
+		net_buf_simple_add_u8(msg, state->tt);
+	}
+
+	state->is_optional_para_available = 0x00;
+
+	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+		printk("Unable to send GEN_LEVEL_SRV Status response\n");
+	}
+}
+
+static void gen_level_set_unack(struct bt_mesh_model *model,
+				struct bt_mesh_msg_ctx *ctx,
+				struct os_mbuf *buf)
+{
+	u8_t tid;
+	s16_t level;
+	s64_t now;
+	struct os_mbuf *msg = model->pub->msg;
+	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_tx_addr == ctx->addr &&
+	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+		return;
+	}
+
+	state->is_optional_para_available = 0x01;
+
+	switch (buf->om_len) {
+	case 0x00:	/* No optional fields are available */
+		state->tt = default_tt;
+		state->delay = 0;
+		state->is_optional_para_available = 0x00;
+		break;
+	case 0x02:	/* Optional fields are available */
+		state->tt = net_buf_simple_pull_u8(buf);
+		state->delay = net_buf_simple_pull_u8(buf);
+		break;
+	default:
+		return;
+	}
+
+	enable_transition = DISABLE_TRANSITION;
+
+	state->last_tid = tid;
+	state->last_tx_addr = ctx->addr;
+	state->last_msg_timestamp = now;
+	state->target_level = level;
+
+	level_tt_values(state);
+
+	if (state->tt_counter == 0) {
+		state->level = level;
+	}
+
+	if (bt_mesh_model_elem(model)->addr == elements[0].addr) {
+		/* Root element */
+		enable_transition = LEVEL_TT;
+		level_lightness_handler(state);
+	} else if (bt_mesh_model_elem(model)->addr == elements[1].addr) {
+		/* Secondary element */
+		enable_transition = LEVEL_TEMP_TT;
+		level_temp_handler(state);
+	}
+
+	if (model->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
+		int err;
+
+		bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS);
+		net_buf_simple_add_le16(msg, state->level);
+
+		err = bt_mesh_model_publish(model);
+		if (err) {
+			printk("bt_mesh_model_publish err %d\n", err);
+		}
+	}
+}
+
+static void gen_level_set(struct bt_mesh_model *model,
+			  struct bt_mesh_msg_ctx *ctx,
+			  struct os_mbuf *buf)
+{
+	gen_level_set_unack(model, ctx, buf);
+	gen_level_get(model, ctx, buf);
+}
+
+static void gen_delta_set_unack(struct bt_mesh_model *model,
+				struct bt_mesh_msg_ctx *ctx,
+				struct os_mbuf *buf)
+{
+	u8_t tid;
+	s32_t tmp32, delta;
+	s64_t now;
+	struct os_mbuf *msg = model->pub->msg;
+	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_tx_addr == ctx->addr) {
+
+		if (now - state->last_msg_timestamp <= K_SECONDS(6)) {
+			if (state->last_delta == delta) {
+				return;
+			}
+			tmp32 = state->last_level + delta;
+		} else {
+			return;
+		}
+
+	} else {
+		state->last_level = state->level;
+		tmp32 = state->level + delta;
+	}
+
+	state->is_optional_para_available = 0x01;
+
+	switch (buf->om_len) {
+	case 0x00:	/* No optional fields are available */
+		state->tt = default_tt;
+		state->delay = 0;
+		state->is_optional_para_available = 0x00;
+		break;
+	case 0x02:	/* Optional fields are available */
+		state->tt = net_buf_simple_pull_u8(buf);
+		state->delay = net_buf_simple_pull_u8(buf);
+		break;
+	default:
+		return;
+	}
+
+	enable_transition = DISABLE_TRANSITION;
+
+	state->last_delta = delta;
+	state->last_tid = tid;
+	state->last_tx_addr = ctx->addr;
+	state->last_msg_timestamp = now;
+
+	if (tmp32 < INT16_MIN) {
+		tmp32 = INT16_MIN;
+	} else if (tmp32 > INT16_MAX) {
+		tmp32 = INT16_MAX;
+	}
+
+	state->target_level = tmp32;
+
+	delta_level_tt_values(state);
+
+	if (state->tt_counter_delta == 0) {
+		state->level = tmp32;
+	}
+
+	if (bt_mesh_model_elem(model)->addr == elements[0].addr) {
+		/* Root element */
+		enable_transition = LEVEL_TT_DELTA;
+		level_lightness_handler(state);
+	} else if (bt_mesh_model_elem(model)->addr == elements[1].addr) {
+		/* Secondary element */
+		enable_transition = LEVEL_TEMP_TT_DELTA;
+		level_temp_handler(state);
+	}
+
+	if (model->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
+		int err;
+
+		bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS);
+		net_buf_simple_add_le16(msg, state->level);
+
+		err = bt_mesh_model_publish(model);
+		if (err) {
+			printk("bt_mesh_model_publish err %d\n", err);
+		}
+	}
+}
+
+static void gen_delta_set(struct bt_mesh_model *model,
+			  struct bt_mesh_msg_ctx *ctx,
+			  struct os_mbuf *buf)
+{
+	gen_delta_set_unack(model, ctx, buf);
+	gen_level_get(model, ctx, buf);
+}
+
+static void gen_move_set_unack(struct bt_mesh_model *model,
+			       struct bt_mesh_msg_ctx *ctx,
+			       struct os_mbuf *buf)
+{
+	u8_t tid;
+	s16_t delta;
+	s32_t tmp32;
+	s64_t now;
+	struct os_mbuf *msg = model->pub->msg;
+	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_tx_addr == ctx->addr &&
+	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+		return;
+	}
+
+	state->is_optional_para_available = 0x02;
+
+	switch (buf->om_len) {
+	case 0x00:	/* No optional fields are available */
+		state->tt = default_tt;
+		state->delay = 0;
+		state->is_optional_para_available = 0x00;
+		break;
+	case 0x02:	/* Optional fields are available */
+		state->tt = net_buf_simple_pull_u8(buf);
+		state->delay = net_buf_simple_pull_u8(buf);
+		break;
+	default:
+		return;
+	}
+
+	enable_transition = DISABLE_TRANSITION;
+
+	state->last_delta = delta;
+	state->last_tid = tid;
+	state->last_tx_addr = ctx->addr;
+	state->last_msg_timestamp = now;
+
+	tmp32 = state->level + delta;
+	if (tmp32 < INT16_MIN) {
+		tmp32 = INT16_MIN;
+	} else if (tmp32 > INT16_MAX) {
+		tmp32 = INT16_MAX;
+	}
+
+	state->target_level = tmp32;
+
+	move_level_tt_values(state);
+
+	if (bt_mesh_model_elem(model)->addr == elements[0].addr) {
+		/* Root element */
+		if (delta == 0) {
+			ble_npl_callout_stop(&level_lightness_transition_timer);
+		} else {
+			enable_transition = LEVEL_TT_MOVE;
+			level_lightness_handler(state);
+		}
+	} else if (bt_mesh_model_elem(model)->addr == elements[1].addr) {
+		/* Secondary element */
+		if (delta == 0) {
+			ble_npl_callout_stop(&level_temp_transition_timer);
+		} else {
+			enable_transition = LEVEL_TEMP_TT_MOVE;
+			level_temp_handler(state);
+		}
+	}
+
+	if (model->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
+		int err;
+
+		bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_LEVEL_STATUS);
+		net_buf_simple_add_le16(msg, state->level);
+
+		err = bt_mesh_model_publish(model);
+		if (err) {
+			printk("bt_mesh_model_publish err %d\n", err);
+		}
+	}
+}
+
+static void gen_move_set(struct bt_mesh_model *model,
+			 struct bt_mesh_msg_ctx *ctx,
+			 struct os_mbuf *buf)
+{
+	gen_move_set_unack(model, ctx, buf);
+	gen_level_get(model, ctx, buf);
+}
+
+/* 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_OP_2(0x82, 0x10));
+	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");
+	}
+}
+
+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 os_mbuf *msg = model->pub->msg;
+	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;
+	}
+
+	state->tt = tt;
+	default_tt = tt;
+
+	/* Do some work here to save value of state->tt on SoC flash */
+
+	if (model->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
+		int err;
+
+		bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x10));
+		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);
+		}
+	}
+
+	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)
+{
+	gen_def_trans_time_setunack(model, ctx, buf);
+}
+
+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);
+	}
+}
+
+/* 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_OP_2(0x82, 0x12));
+	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");
+	}
+}
+
+/* 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 bool gen_onpowerup_setunack(struct bt_mesh_model *model,
+				struct bt_mesh_msg_ctx *ctx,
+				struct os_mbuf *buf)
+{
+	u8_t onpowerup;
+	struct os_mbuf *msg = model->pub->msg;
+	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;
+	}
+	state->onpowerup = onpowerup;
+
+	/* Do some work here to save value of state->onpowerup on SoC flash */
+
+	if (model->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
+		int err;
+
+		bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x12));
+		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);
+		}
+	}
+
+	return true;
+}
+
+static void gen_onpowerup_set_unack(struct bt_mesh_model *model,
+				    struct bt_mesh_msg_ctx *ctx,
+				    struct os_mbuf *buf)
+{
+	gen_onpowerup_setunack(model, ctx, buf);
+}
+
+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);
+	}
+}
+
+/* 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;
+
+	bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_3(0x03, 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");
+	}
+}
+
+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_tx_addr == ctx->addr &&
+	    (now - state->last_msg_timestamp <= 6000)) {
+		return;
+	}
+
+	state->last_tid = tid;
+	state->last_tx_addr = ctx->addr;
+	state->last_msg_timestamp = now;
+	state->current = current;
+
+	/* This is dummy response for demo purpose */
+	state->response = 0xA578FEB3;
+
+	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_OP_2(0x82, 0x4E));
+	net_buf_simple_add_le16(msg, state->actual);
+
+	if (state->is_optional_para_available) {
+		net_buf_simple_add_le16(msg, state->target_actual);
+		net_buf_simple_add_u8(msg, state->tt);
+	}
+
+	state->is_optional_para_available = 0x00;
+
+	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+		printk("Unable to send LightLightnessAct Status response\n");
+	}
+}
+
+static void light_lightness_set_unack(struct bt_mesh_model *model,
+				      struct bt_mesh_msg_ctx *ctx,
+				      struct os_mbuf *buf)
+{
+	u8_t tid;
+	u16_t actual;
+	s64_t now;
+	struct os_mbuf *msg = model->pub->msg;
+	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_tx_addr == ctx->addr &&
+	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+		return;
+	}
+
+	state->is_optional_para_available = 0x01;
+
+	switch (buf->om_len) {
+	case 0x00:	/* No optional fields are available */
+		state->tt = default_tt;
+		state->delay = 0;
+		state->is_optional_para_available = 0x00;
+		break;
+	case 0x02:	/* Optional fields are available */
+		state->tt = net_buf_simple_pull_u8(buf);
+		state->delay = net_buf_simple_pull_u8(buf);
+		break;
+	default:
+		return;
+	}
+
+	enable_transition = DISABLE_TRANSITION;
+
+	state->last_tid = tid;
+	state->last_tx_addr = ctx->addr;
+	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;
+
+	light_lightnes_actual_tt_values(state);
+
+	if (state->tt_counter_actual == 0) {
+		state->actual = actual;
+	}
+
+	light_lightness_actual_handler(state);
+
+	if (model->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
+		int err;
+
+		bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x4E));
+		net_buf_simple_add_le16(msg, state->actual);
+
+		err = bt_mesh_model_publish(model);
+		if (err) {
+			printk("bt_mesh_model_publish err %d\n", err);
+		}
+	}
+}
+
+static void light_lightness_set(struct bt_mesh_model *model,
+				struct bt_mesh_msg_ctx *ctx,
+				struct os_mbuf *buf)
+{
+	light_lightness_set_unack(model, ctx, buf);
+	light_lightness_get(model, ctx, buf);
+}
+
+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_OP_2(0x82, 0x52));
+	net_buf_simple_add_le16(msg, state->linear);
+
+	if (state->is_optional_para_available) {
+		net_buf_simple_add_le16(msg, state->target_linear);
+		net_buf_simple_add_u8(msg, state->tt);
+	}
+
+	state->is_optional_para_available = 0x00;
+
+	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+		printk("Unable to send LightLightnessLin Status response\n");
+	}
+}
+
+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;
+	u16_t linear;
+	s64_t now;
+	struct os_mbuf *msg = model->pub->msg;
+	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_tx_addr == ctx->addr &&
+	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+		return;
+	}
+
+	state->is_optional_para_available = 0x01;
+
+	switch (buf->om_len) {
+	case 0x00:	/* No optional fields are available */
+		state->tt = default_tt;
+		state->delay = 0;
+		state->is_optional_para_available = 0x00;
+		break;
+	case 0x02:	/* Optional fields are available */
+		state->tt = net_buf_simple_pull_u8(buf);
+		state->delay = net_buf_simple_pull_u8(buf);
+		break;
+	default:
+		return;
+	}
+
+	enable_transition = DISABLE_TRANSITION;
+
+	state->last_tid = tid;
+	state->last_tx_addr = ctx->addr;
+	state->last_msg_timestamp = now;
+	state->target_linear = linear;
+
+	light_lightnes_linear_tt_values(state);
+
+	if (state->tt_counter_linear == 0) {
+		state->linear = linear;
+	}
+
+	light_lightness_linear_handler(state);
+
+	if (model->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
+		int err;
+
+		bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x52));
+		net_buf_simple_add_le16(msg, state->linear);
+
+		err = bt_mesh_model_publish(model);
+		if (err) {
+			printk("bt_mesh_model_publish err %d\n", err);
+		}
+	}
+}
+
+static void light_lightness_linear_set(struct bt_mesh_model *model,
+				       struct bt_mesh_msg_ctx *ctx,
+				       struct os_mbuf *buf)
+{
+	light_lightness_linear_set_unack(model, ctx, buf);
+	light_lightness_linear_get(model, ctx, buf);
+}
+
+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_OP_2(0x82, 0x54));
+
+	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");
+	}
+}
+
+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_OP_2(0x82, 0x56));
+
+	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");
+	}
+}
+
+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;
+
+	bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x58));
+
+	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");
+	}
+
+	state->status_code = RANGE_SUCCESSFULLY_UPDATED;
+}
+
+/* Light Lightness Setup Server message handlers */
+static void light_lightness_default_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 light_lightness_state *state = model->user_data;
+
+	state->def = net_buf_simple_pull_le16(buf);
+
+	/* Here, Model specification is silent about tid implementation */
+
+	/* Do some work here to save value of state->def on SoC flash */
+
+	if (model->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
+		int err;
+
+		bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x56));
+		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(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);
+}
+
+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 os_mbuf *msg = model->pub->msg;
+	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;
+
+			state->light_range_min = min;
+			state->light_range_max = max;
+
+			/* Do some work here to save values of
+			 * state->light_range_min &
+			 * state->light_range_max
+			 * on SoC flash
+			 */
+		} else {
+			/* The provided value for Range Max cannot be set */
+			state->status_code = CANNOT_SET_RANGE_MAX;
+			return false;
+		}
+	}
+
+	if (model->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
+		int err;
+
+		bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x58));
+		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);
+		}
+	}
+
+	return true;
+}
+
+static void light_lightness_range_set_unack(struct bt_mesh_model *model,
+					    struct bt_mesh_msg_ctx *ctx,
+					    struct os_mbuf *buf)
+{
+	light_lightness_range_setunack(model, ctx, buf);
+}
+
+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 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_OP_2(0x82, 0x60));
+	net_buf_simple_add_le16(msg, state->lightness);
+	net_buf_simple_add_le16(msg, state->temp);
+
+	if (state->is_optional_para_available) {
+		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->tt);
+	}
+
+	state->is_optional_para_available = 0x00;
+
+	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+		printk("Unable to send LightCTL Status response\n");
+	}
+}
+
+static bool light_ctl_setunack(struct bt_mesh_model *model,
+			       struct bt_mesh_msg_ctx *ctx,
+			       struct os_mbuf *buf)
+{
+	u8_t tid;
+	s16_t delta_uv;
+	u16_t lightness, temp;
+	s64_t now;
+	struct os_mbuf *msg = model->pub->msg;
+	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 false;
+	}
+
+	now = k_uptime_get();
+	if (state->last_tid == tid && state->last_tx_addr == ctx->addr &&
+	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+		return true;
+	}
+
+	state->is_optional_para_available = 0x01;
+
+	switch (buf->om_len) {
+	case 0x00:	/* No optional fields are available */
+		state->tt = default_tt;
+		state->delay = 0;
+		state->is_optional_para_available = 0x00;
+		break;
+	case 0x02:	/* Optional fields are available */
+		state->tt = net_buf_simple_pull_u8(buf);
+		state->delay = net_buf_simple_pull_u8(buf);
+		break;
+	default:
+		return false;
+	}
+
+	enable_transition = DISABLE_TRANSITION;
+
+	state->last_tid = tid;
+	state->last_tx_addr = ctx->addr;
+	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;
+
+	light_ctl_tt_values(state);
+
+	if (state->tt_counter == 0) {
+		state->lightness = lightness;
+		state->temp = temp;
+		state->delta_uv = delta_uv;
+	}
+
+	light_ctl_handler(state);
+
+	if (model->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
+		int err;
+
+		bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x60));
+
+		/* 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);
+
+		err = bt_mesh_model_publish(model);
+		if (err) {
+			printk("bt_mesh_model_publish err %d\n", err);
+		}
+	}
+
+	return true;
+}
+
+static void light_ctl_set_unack(struct bt_mesh_model *model,
+				struct bt_mesh_msg_ctx *ctx,
+				struct os_mbuf *buf)
+{
+	light_ctl_setunack(model, ctx, buf);
+}
+
+static void light_ctl_set(struct bt_mesh_model *model,
+			  struct bt_mesh_msg_ctx *ctx,
+			  struct os_mbuf *buf)
+{
+	if (light_ctl_setunack(model, ctx, buf) == true) {
+		light_ctl_get(model, ctx, buf);
+	}
+}
+
+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;
+
+	bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x63));
+	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");
+	}
+
+	state->status_code = RANGE_SUCCESSFULLY_UPDATED;
+}
+
+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_OP_2(0x82, 0x68));
+	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");
+	}
+}
+
+/* Light CTL Setup Server message handlers */
+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 os_mbuf *msg = model->pub->msg;
+	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;
+	}
+
+	state->lightness_def = lightness;
+	state->temp_def = temp;
+	state->delta_uv_def = delta_uv;
+
+	/* Do some work here to save values of
+	 * state->lightness_def, state->temp_Def & state->delta_uv_def
+	 * on SoC flash
+	 */
+
+	if (model->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
+		int err;
+
+		bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x68));
+		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);
+		}
+	}
+
+	return true;
+}
+
+static void light_ctl_default_set_unack(struct bt_mesh_model *model,
+					struct bt_mesh_msg_ctx *ctx,
+					struct os_mbuf *buf)
+{
+	light_ctl_default_setunack(model, ctx, buf);
+}
+
+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);
+	}
+}
+
+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 os_mbuf *msg = model->pub->msg;
+	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;
+		state->temp_range_min = min;
+		state->temp_range_max = max;
+
+		/* Do some work here to save values of
+		 * state->temp_range_min & state->temp_range_min
+		 * on SoC flash
+		 */
+	} else {
+		/* The provided value for Range Max cannot be set */
+		state->status_code = CANNOT_SET_RANGE_MAX;
+		return false;
+	}
+
+	if (model->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
+		int err;
+
+		bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x63));
+		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);
+		}
+	}
+
+	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)
+{
+	light_ctl_temp_range_setunack(model, ctx, buf);
+}
+
+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 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_OP_2(0x82, 0x66));
+	net_buf_simple_add_le16(msg, state->temp);
+	net_buf_simple_add_le16(msg, state->delta_uv);
+
+	if (state->is_optional_para_available) {
+		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->tt);
+	}
+
+	state->is_optional_para_available = 0x00;
+
+	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+		printk("Unable to send LightCTL Temp. Status response\n");
+	}
+}
+
+static bool light_ctl_temp_setunack(struct bt_mesh_model *model,
+				    struct bt_mesh_msg_ctx *ctx,
+				    struct os_mbuf *buf)
+{
+	u8_t tid;
+	s16_t delta_uv;
+	u16_t temp;
+	s64_t now;
+
+	struct os_mbuf *msg = model->pub->msg;
+	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 false;
+	}
+
+	now = k_uptime_get();
+	if (state->last_tid == tid && state->last_tx_addr == ctx->addr &&
+	    (now - state->last_msg_timestamp <= K_SECONDS(6))) {
+		return true;
+	}
+
+	state->is_optional_para_available = 0x01;
+
+	switch (buf->om_len) {
+	case 0x00:	/* No optional fields are available */
+		state->tt = default_tt;
+		state->delay = 0;
+		state->is_optional_para_available = 0x00;
+		break;
+	case 0x02:	/* Optional fields are available */
+		state->tt = net_buf_simple_pull_u8(buf);
+		state->delay = net_buf_simple_pull_u8(buf);
+		break;
+	default:
+		return false;
+	}
+
+	enable_transition = DISABLE_TRANSITION;
+
+	state->last_tid = tid;
+	state->last_tx_addr = ctx->addr;
+	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;
+
+	light_ctl_temp_tt_values(state);
+
+	if (state->tt_counter_temp == 0) {
+		state->temp = temp;
+		state->delta_uv = delta_uv;
+	}
+
+	light_ctl_temp_handler(state);
+
+	if (model->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
+		int err;
+
+		bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x66));
+		net_buf_simple_add_le16(msg, state->temp);
+		net_buf_simple_add_le16(msg, state->delta_uv);
+
+		err = bt_mesh_model_publish(model);
+		if (err) {
+			printk("bt_mesh_model_publish err %d\n", err);
+		}
+	}
+
+	return true;
+}
+
+static void light_ctl_temp_set_unack(struct bt_mesh_model *model,
+				     struct bt_mesh_msg_ctx *ctx,
+				     struct os_mbuf *buf)
+{
+	light_ctl_temp_setunack(model, ctx, buf);
+}
+
+static void light_ctl_temp_set(struct bt_mesh_model *model,
+			       struct bt_mesh_msg_ctx *ctx,
+			       struct os_mbuf *buf)
+{
+	if (light_ctl_temp_setunack(model, ctx, buf) == true) {
+		light_ctl_temp_get(model, ctx, buf);
+	}
+}
+
+/* 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), 5, gen_move_set },
+	{ BT_MESH_MODEL_OP_2(0x82, 0x0C), 5, 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), 5, light_ctl_set },
+	{ BT_MESH_MODEL_OP_2(0x82, 0x5F), 5, 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(0x00, CID_RUNTIME), 0, vnd_get },
+	{ BT_MESH_MODEL_OP_3(0x01, CID_RUNTIME), 3, vnd_set },
+	{ BT_MESH_MODEL_OP_3(0x02, CID_RUNTIME), 3, vnd_set_unack },
+	{ BT_MESH_MODEL_OP_3(0x03, 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_ONOFF_SRV,
+		      gen_onoff_srv_op, &gen_onoff_srv_pub_s0,
+		      &gen_onoff_srv_s0_user_data),
+	BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI,
+		      gen_onoff_cli_op, &gen_onoff_cli_pub_s0,
+		      NULL),
+
+	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),
+};

+ 182 - 0
apps/blemesh_models_example_2/src/device_composition.h

@@ -0,0 +1,182 @@
+/* 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
+
+enum lightness {
+	ONPOWERUP = 0x01,
+	ONOFF,
+	LEVEL,
+	DELTA_LEVEL,
+	ACTUAL,
+	LINEAR,
+	CTL,
+	IGNORE
+};
+
+enum temperature {
+	ONOFF_TEMP = 0x01,
+	LEVEL_TEMP,
+	CTL_TEMP,
+	IGNORE_TEMP
+};
+
+struct generic_onoff_state {
+	u8_t onoff;
+	u8_t target_onoff;
+
+	u8_t last_tid;
+	u16_t last_tx_addr;
+	s64_t last_msg_timestamp;
+
+	u8_t tt;
+	u32_t cal_tt;
+	u8_t delay;
+	u32_t tt_counter;
+
+	u8_t is_optional_para_available;
+};
+
+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_tx_addr;
+	s64_t last_msg_timestamp;
+
+	s32_t tt_delta;
+	u8_t tt;
+	u32_t cal_tt;
+	u8_t delay;
+	u32_t tt_counter;
+	u32_t tt_counter_delta;
+	u32_t tt_counter_move;
+
+	u8_t is_optional_para_available;
+};
+
+struct generic_onpowerup_state {
+	u8_t onpowerup;
+	u8_t last_tid;
+	u16_t last_tx_addr;
+};
+
+struct gen_def_trans_time_state {
+	u8_t tt;
+};
+
+struct vendor_state {
+	int current;
+	u32_t response;
+	u8_t last_tid;
+	u16_t last_tx_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;
+
+	u8_t last_tid;
+	u16_t last_tx_addr;
+	s64_t last_msg_timestamp;
+
+	s32_t tt_delta_actual;
+	s32_t tt_delta_linear;
+	u8_t tt;
+	u32_t cal_tt;
+	u8_t delay;
+	u32_t tt_counter_actual;
+	u32_t tt_counter_linear;
+
+	u8_t is_optional_para_available;
+};
+
+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;
+
+	u16_t lightness_def;
+	u16_t temp_def;
+	s16_t delta_uv_def;
+
+	u16_t temp_last;
+
+	u8_t last_tid;
+	u16_t last_tx_addr;
+	s64_t last_msg_timestamp;
+
+	s32_t tt_lightness_delta;
+	s32_t tt_temp_delta;
+	s32_t tt_duv_delta;
+	u8_t tt;
+	u32_t cal_tt;
+	u8_t delay;
+	u32_t tt_counter;
+	u32_t tt_counter_temp;
+
+	u8_t is_optional_para_available;
+};
+
+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;
+
+#endif

+ 198 - 0
apps/blemesh_models_example_2/src/main.c

@@ -0,0 +1,198 @@
+/* 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 "common.h"
+#include "ble_mesh.h"
+#include "device_composition.h"
+#include "publisher.h"
+#include "state_binding.h"
+#include "transition.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 ble_npl_callout button_work;
+
+static void button_pressed(struct os_event *ev)
+{
+	k_work_submit(&button_work);
+}
+
+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);
+}
+
+static void 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 */
+
+	k_work_init(&button_work, publish);
+
+	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]);
+}
+
+void light_default_status_init(void)
+{
+	/* Assume vaules are retrived from Persistence Storage (Start).
+	 * These had saved by respective Setup Servers.
+	 */
+	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.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;
+	/* (End) */
+
+	/* Assume following values are retrived from Persistence
+	 * Storage (Start).
+	 * These values had saved before power down.
+	 */
+	light_lightness_srv_user_data.last = LIGHTNESS_MAX;
+	light_ctl_srv_user_data.temp_last = TEMP_MIN;
+	/* (End) */
+
+	light_ctl_srv_user_data.temp = light_ctl_srv_user_data.temp_def;
+
+	if (gen_power_onoff_srv_user_data.onpowerup == STATE_OFF) {
+		gen_onoff_srv_root_user_data.onoff = STATE_OFF;
+		state_binding(ONOFF, ONOFF_TEMP);
+	} else if (gen_power_onoff_srv_user_data.onpowerup == STATE_DEFAULT) {
+		gen_onoff_srv_root_user_data.onoff = STATE_ON;
+		state_binding(ONOFF, ONOFF_TEMP);
+	} else if (gen_power_onoff_srv_user_data.onpowerup == STATE_RESTORE) {
+		/* Assume following values is retrived from Persistence
+		 * Storage (Start).
+		 * This value had saved before power down.
+		 */
+		gen_onoff_srv_root_user_data.onoff = STATE_ON;
+		/* (End) */
+
+		light_ctl_srv_user_data.temp =
+			light_ctl_srv_user_data.temp_last;
+
+		state_binding(ONPOWERUP, ONOFF_TEMP);
+	}
+}
+
+void update_light_state(void)
+{
+	u8_t power, color;
+
+	power = 100 * ((float) light_lightness_srv_user_data.actual / 65535);
+	color = 100 * ((float) (gen_level_srv_s0_user_data.level + 32768)
+		       / 65535);
+
+	printk("power-> %d, color-> %d\n", power, color);
+
+	if (gen_onoff_srv_root_user_data.onoff == STATE_ON) {
+		/* 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);
+	}
+}
+
+int main(void)
+{
+#ifdef ARCH_sim
+	mcu_sim_parse_args(argc, argv);
+#endif
+
+	/* Initialize OS */
+	sysinit();
+
+	light_default_status_init();
+
+	transition_timers_init();
+
+	gpio_init();
+
+	update_light_state();
+
+	init_pub();
+
+	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;
+
+	randomize_publishers_TID();
+
+	while (1) {
+		os_eventq_run(os_eventq_dflt_get());
+	}
+
+	return 0;
+}

+ 241 - 0
apps/blemesh_models_example_2/src/publisher.c

@@ -0,0 +1,241 @@
+/* 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 "common.h"
+#include "ble_mesh.h"
+#include "publisher.h"
+#include "device_composition.h"
+
+#define ONOFF
+#define GENERIC_LEVEL
+/* #define LIGHT_CTL */
+/* #define LIGHT_CTL_TEMP */
+
+static bool is_randomization_of_TIDs_done;
+
+#if defined(ONOFF)
+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)
+	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 ble_npl_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(0x02, 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(0x02, 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(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);
+	}
+}

+ 27 - 0
apps/blemesh_models_example_2/src/publisher.h

@@ -0,0 +1,27 @@
+/* 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 ble_npl_event *work);
+
+#endif

+ 183 - 0
apps/blemesh_models_example_2/src/state_binding.c

@@ -0,0 +1,183 @@
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * Copyright (c) 2018 Vikrant More
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <math.h>
+
+#include "common.h"
+#include "ble_mesh.h"
+#include "device_composition.h"
+
+static s32_t ceiling(float num)
+{
+	s32_t inum;
+
+	inum = (s32_t) num;
+	if (num == (float) inum) {
+		return inum;
+	}
+
+	return inum + 1;
+}
+
+static bool constrain_light_actual_state(u16_t var)
+{
+	bool is_value_within_range;
+
+	is_value_within_range = false;
+
+	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;
+	} else {
+		is_value_within_range = true;
+	}
+
+	light_lightness_srv_user_data.actual = var;
+
+	return is_value_within_range;
+}
+
+static void update_gen_onoff_state(void)
+{
+	if (light_lightness_srv_user_data.actual == 0) {
+		gen_onoff_srv_root_user_data.onoff = STATE_OFF;
+	} else {
+		gen_onoff_srv_root_user_data.onoff = STATE_ON;
+	}
+}
+
+void state_binding(u8_t lightness, u8_t temperature)
+{
+	bool is_value_within_range;
+	u16_t tmp16;
+	float tmp;
+
+	switch (temperature) {
+	case ONOFF_TEMP:/* Temp. update as per Light CTL temp. default state */
+	case CTL_TEMP:	/* Temp. update as per Light CTL temp. state */
+		/* Mesh Model Specification 6.1.3.1.1 2nd formula start */
+		tmp = (light_ctl_srv_user_data.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);
+		gen_level_srv_s0_user_data.level = tmp - 32768;
+		/* 6.1.3.1.1 2nd formula end */
+		break;
+	case LEVEL_TEMP:/* Temp. update as per Generic Level (s0) state */
+		/* Mesh Model Specification 6.1.3.1.1 1st formula start */
+		tmp = (float) (light_ctl_srv_user_data.temp_range_max -
+			       light_ctl_srv_user_data.temp_range_min) / 65535;
+		tmp = (gen_level_srv_s0_user_data.level + 32768) * tmp;
+		light_ctl_srv_user_data.temp =
+			light_ctl_srv_user_data.temp_range_min + tmp;
+		/* 6.1.3.1.1 1st formula end */
+		break;
+	default:
+		break;
+	}
+
+	tmp16 = 0;
+
+	switch (lightness) {
+	case ONPOWERUP: /* Lightness update as per Generic OnPowerUp state */
+		if (gen_onoff_srv_root_user_data.onoff == STATE_OFF) {
+			light_lightness_srv_user_data.actual = 0;
+			light_lightness_srv_user_data.linear = 0;
+			gen_level_srv_root_user_data.level = -32768;
+			light_ctl_srv_user_data.lightness = 0;
+			return;
+		} else if (gen_onoff_srv_root_user_data.onoff == STATE_ON) {
+			light_lightness_srv_user_data.actual =
+				light_lightness_srv_user_data.last;
+		}
+
+		break;
+	case ONOFF: /* Lightness update as per Generic OnOff (root) state */
+		if (gen_onoff_srv_root_user_data.onoff == STATE_OFF) {
+			light_lightness_srv_user_data.actual = 0;
+			light_lightness_srv_user_data.linear = 0;
+			gen_level_srv_root_user_data.level = -32768;
+			light_ctl_srv_user_data.lightness = 0;
+			return;
+		} else if (gen_onoff_srv_root_user_data.onoff == STATE_ON) {
+			if (light_lightness_srv_user_data.def == 0) {
+				light_lightness_srv_user_data.actual =
+					light_lightness_srv_user_data.last;
+			} else {
+				light_lightness_srv_user_data.actual =
+					light_lightness_srv_user_data.def;
+			}
+		}
+
+		break;
+	case LEVEL: /* Lightness update as per Generic Level (root) state */
+		tmp16 = gen_level_srv_root_user_data.level + 32768;
+		constrain_light_actual_state(tmp16);
+		update_gen_onoff_state();
+		break;
+	case DELTA_LEVEL: /* Lightness update as per Gen. Level (root) state */
+		/* This is as per Mesh Model Specification 3.3.2.2.3 */
+		tmp16 = gen_level_srv_root_user_data.level + 32768;
+		if (tmp16 > 0 &&
+		    tmp16 < light_lightness_srv_user_data.light_range_min) {
+			if (gen_level_srv_root_user_data.last_delta < 0) {
+				tmp16 = 0;
+			} else {
+				tmp16 =
+				light_lightness_srv_user_data.light_range_min;
+			}
+		} else if (tmp16 >
+			   light_lightness_srv_user_data.light_range_max) {
+			tmp16 =
+			light_lightness_srv_user_data.light_range_max;
+		}
+
+		light_lightness_srv_user_data.actual = tmp16;
+
+		update_gen_onoff_state();
+		break;
+	case ACTUAL: /* Lightness update as per Light Lightness Actual state */
+		update_gen_onoff_state();
+		break;
+	case LINEAR: /* Lightness update as per Light Lightness Linear state */
+		tmp16 = (u16_t) 65535 *
+			sqrt(((float) light_lightness_srv_user_data.linear /
+			      65535));
+
+		is_value_within_range = constrain_light_actual_state(tmp16);
+		update_gen_onoff_state();
+
+		if (is_value_within_range) {
+			goto ignore_linear_update_others;
+		}
+
+		break;
+	case CTL: /* Lightness update as per Light CTL Lightness state */
+		constrain_light_actual_state(light_ctl_srv_user_data.lightness);
+		update_gen_onoff_state();
+		break;
+	default:
+		return;
+	}
+
+	tmp = ((float) light_lightness_srv_user_data.actual / 65535);
+	light_lightness_srv_user_data.linear = ceiling(65535 * tmp * tmp);
+
+ignore_linear_update_others:
+	if (light_lightness_srv_user_data.actual != 0) {
+		light_lightness_srv_user_data.last =
+			light_lightness_srv_user_data.actual;
+	}
+
+	gen_level_srv_root_user_data.level =
+		light_lightness_srv_user_data.actual - 32768;
+
+	light_ctl_srv_user_data.lightness =
+		light_lightness_srv_user_data.actual;
+}

+ 13 - 0
apps/blemesh_models_example_2/src/state_binding.h

@@ -0,0 +1,13 @@
+/* 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
+
+void state_binding(u8_t lightness, u8_t temperature);
+
+#endif

+ 678 - 0
apps/blemesh_models_example_2/src/transition.c

@@ -0,0 +1,678 @@
+/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
+ *
+ * Copyright (c) 2018 Vikrant More
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "common.h"
+#include "ble_mesh.h"
+#include "device_composition.h"
+#include "state_binding.h"
+#include "transition.h"
+
+u8_t enable_transition;
+u8_t default_tt;
+
+struct ble_npl_callout onoff_work;
+struct ble_npl_callout level_lightness_work;
+struct ble_npl_callout level_temp_work;
+struct ble_npl_callout light_lightness_actual_work;
+struct ble_npl_callout light_lightness_linear_work;
+struct ble_npl_callout light_ctl_work;
+struct ble_npl_callout light_ctl_temp_work;
+
+struct ble_npl_callout onoff_transition_timer;
+struct ble_npl_callout level_lightness_transition_timer;
+struct ble_npl_callout level_temp_transition_timer;
+struct ble_npl_callout light_lightness_actual_transition_timer;
+struct ble_npl_callout light_lightness_linear_transition_timer;
+struct ble_npl_callout light_ctl_transition_timer;
+struct ble_npl_callout light_ctl_temp_transition_timer;
+
+static u32_t tt_counter_calculator(u8_t *tt, u32_t *cal_tt)
+{
+	u8_t steps_multiplier, resolution;
+	u32_t tt_counter;
+
+	resolution = ((*tt) >> 6);
+	steps_multiplier = (*tt) & 0x3F;
+
+	switch (resolution) {
+	case 0:	/* 100ms */
+		*cal_tt = steps_multiplier * 100;
+		break;
+	case 1:	/* 1 second */
+		*cal_tt = steps_multiplier * 1000;
+		break;
+	case 2:	/* 10 seconds */
+		*cal_tt = steps_multiplier * 10000;
+		break;
+	case 3:	/* 10 minutes */
+		*cal_tt = steps_multiplier * 600000;
+		break;
+	}
+
+	tt_counter = ((float) *cal_tt / 100);
+
+	if (tt_counter > DEVICE_SPECIFIC_RESOLUTION) {
+		tt_counter = DEVICE_SPECIFIC_RESOLUTION;
+	}
+
+	if (tt_counter != 0) {
+		*cal_tt = *cal_tt / tt_counter;
+	}
+
+	return tt_counter;
+}
+
+void onoff_tt_values(struct generic_onoff_state *state)
+{
+	state->tt_counter = tt_counter_calculator(&state->tt, &state->cal_tt);
+}
+
+void level_tt_values(struct generic_level_state *state)
+{
+	u32_t tt_counter;
+
+	tt_counter = tt_counter_calculator(&state->tt, &state->cal_tt);
+	state->tt_counter = tt_counter;
+
+	if (tt_counter == 0) {
+		tt_counter = 1;
+	}
+
+	state->tt_delta = ((float) (state->level - state->target_level) /
+			   tt_counter);
+}
+
+void delta_level_tt_values(struct generic_level_state *state)
+{
+	u32_t tt_counter;
+
+	tt_counter = tt_counter_calculator(&state->tt, &state->cal_tt);
+	state->tt_counter_delta = tt_counter;
+
+	if (tt_counter == 0) {
+		tt_counter = 1;
+	}
+
+	state->tt_delta = ((float) state->last_delta / tt_counter);
+
+	state->tt_delta *= -1;
+}
+
+void move_level_tt_values(struct generic_level_state *state)
+{
+	u32_t tt_counter;
+
+	tt_counter = tt_counter_calculator(&state->tt, &state->cal_tt);
+	state->tt_counter_move = tt_counter;
+
+	if (tt_counter == 0) {
+		tt_counter = 1;
+	}
+
+	state->tt_delta = ((float) state->last_delta / tt_counter);
+
+	state->tt_delta *= -1;
+}
+
+void light_lightnes_actual_tt_values(struct light_lightness_state *state)
+{
+	u32_t tt_counter;
+
+	tt_counter = tt_counter_calculator(&state->tt, &state->cal_tt);
+	state->tt_counter_actual = tt_counter;
+
+	if (tt_counter == 0) {
+		tt_counter = 1;
+	}
+
+	state->tt_delta_actual =
+		((float) (state->actual - state->target_actual) /
+		 tt_counter);
+}
+
+void light_lightnes_linear_tt_values(struct light_lightness_state *state)
+{
+	u32_t tt_counter;
+
+	tt_counter = tt_counter_calculator(&state->tt, &state->cal_tt);
+	state->tt_counter_linear = tt_counter;
+
+	if (tt_counter == 0) {
+		tt_counter = 1;
+	}
+
+	state->tt_delta_linear =
+		((float) (state->linear - state->target_linear) /
+		 tt_counter);
+}
+
+void light_ctl_tt_values(struct light_ctl_state *state)
+{
+	u32_t tt_counter;
+
+	tt_counter = tt_counter_calculator(&state->tt, &state->cal_tt);
+	state->tt_counter = tt_counter;
+
+	if (tt_counter == 0) {
+		tt_counter = 1;
+	}
+
+	state->tt_lightness_delta =
+		((float) (state->lightness - state->target_lightness) /
+		 tt_counter);
+
+	state->tt_temp_delta =
+		((float) (state->temp - state->target_temp) /
+		 tt_counter);
+
+	state->tt_duv_delta =
+		((float) (state->delta_uv - state->target_delta_uv) /
+		 tt_counter);
+}
+
+void light_ctl_temp_tt_values(struct light_ctl_state *state)
+{
+	u32_t tt_counter;
+
+	tt_counter = tt_counter_calculator(&state->tt, &state->cal_tt);
+	state->tt_counter_temp = tt_counter;
+
+	if (tt_counter == 0) {
+		tt_counter = 1;
+	}
+
+	state->tt_temp_delta = ((float) (state->temp - state->target_temp) /
+				tt_counter);
+
+	state->tt_duv_delta =
+		((float) (state->delta_uv - state->target_delta_uv) /
+		 tt_counter);
+}
+
+/* Timers related handlers & threads (Start) */
+static void onoff_work_handler(struct ble_npl_event *work)
+{
+	struct generic_onoff_state *state = &gen_onoff_srv_root_user_data;
+
+	if (enable_transition != ONOFF_TT) {
+		ble_npl_callout_stop(&onoff_transition_timer);
+		return;
+	}
+
+	if (state->tt_counter != 0) {
+		state->tt_counter--;
+
+		if (state->target_onoff == STATE_ON) {
+			state->onoff = STATE_ON;
+
+			state_binding(ONOFF, IGNORE_TEMP);
+			update_light_state();
+
+			enable_transition = DISABLE_TRANSITION;
+		}
+	}
+
+	if (state->tt_counter == 0) {
+		state->onoff = state->target_onoff;
+
+		state_binding(ONOFF, IGNORE_TEMP);
+		update_light_state();
+
+		ble_npl_callout_stop(&onoff_transition_timer);
+	}
+}
+
+static void level_lightness_work_handler(struct ble_npl_event *work)
+{
+	u32_t *tt_counter;
+	struct generic_level_state *state = &gen_level_srv_root_user_data;
+
+	tt_counter = NULL;
+
+	switch (enable_transition) {
+	case LEVEL_TT:
+		tt_counter = &state->tt_counter;
+		break;
+	case LEVEL_TT_DELTA:
+		tt_counter = &state->tt_counter_delta;
+		break;
+	case LEVEL_TT_MOVE:
+		tt_counter = &state->tt_counter_move;
+		break;
+	default:
+		ble_npl_callout_stop(&level_lightness_transition_timer);
+		return;
+	}
+
+	if (*tt_counter != 0) {
+		s32_t lightness;
+
+		(*tt_counter)--;
+
+		lightness = state->level - state->tt_delta;
+
+		if (lightness > INT16_MAX) {
+			lightness = INT16_MAX;
+		} else if (lightness < INT16_MIN) {
+			lightness = INT16_MIN;
+		}
+
+		if (state->level != lightness) {
+			state->level = lightness;
+
+			state_binding(LEVEL, IGNORE_TEMP);
+			update_light_state();
+		} else {
+			enable_transition = DISABLE_TRANSITION;
+		}
+	}
+
+	if (*tt_counter == 0) {
+		state->level = state->target_level;
+
+		state_binding(LEVEL, IGNORE_TEMP);
+		update_light_state();
+
+		ble_npl_callout_stop(&level_lightness_transition_timer);
+	}
+}
+
+static void level_temp_work_handler(struct ble_npl_event *work)
+{
+	u32_t *tt_counter;
+	struct generic_level_state *state = &gen_level_srv_s0_user_data;
+
+	tt_counter = NULL;
+
+	switch (enable_transition) {
+	case LEVEL_TT:
+		tt_counter = &state->tt_counter;
+		break;
+	case LEVEL_TT_DELTA:
+		tt_counter = &state->tt_counter_delta;
+		break;
+	case LEVEL_TT_MOVE:
+		tt_counter = &state->tt_counter_move;
+		break;
+	default:
+		ble_npl_callout_stop(&level_temp_transition_timer);
+		return;
+	}
+
+	if (*tt_counter != 0) {
+		s32_t temp;
+
+		(*tt_counter)--;
+
+		temp = state->level - state->tt_delta;
+
+		if (temp > INT16_MAX) {
+			temp = INT16_MAX;
+		} else if (temp < INT16_MIN) {
+			temp = INT16_MIN;
+		}
+
+		if (state->level != temp) {
+			state->level = temp;
+
+			state_binding(IGNORE, LEVEL_TEMP);
+			update_light_state();
+		} else {
+			enable_transition = DISABLE_TRANSITION;
+		}
+	}
+
+	if (*tt_counter == 0) {
+		state->level = state->target_level;
+
+		state_binding(IGNORE, LEVEL_TEMP);
+		update_light_state();
+
+		ble_npl_callout_stop(&level_temp_transition_timer);
+	}
+}
+
+static void light_lightness_actual_work_handler(struct ble_npl_event *work)
+{
+	struct light_lightness_state *state = &light_lightness_srv_user_data;
+
+	if (enable_transition != LIGTH_LIGHTNESS_ACTUAL_TT) {
+		ble_npl_callout_stop(&light_lightness_actual_transition_timer);
+		return;
+	}
+
+	if (state->tt_counter_actual != 0) {
+		u32_t actual;
+
+		state->tt_counter_actual--;
+
+		actual = state->actual - state->tt_delta_actual;
+
+		if (state->actual != actual) {
+			state->actual = actual;
+
+			state_binding(ACTUAL, IGNORE_TEMP);
+			update_light_state();
+		} else {
+			enable_transition = DISABLE_TRANSITION;
+		}
+	}
+
+	if (state->tt_counter_actual == 0) {
+		state->actual = state->target_actual;
+
+		state_binding(ACTUAL, IGNORE_TEMP);
+		update_light_state();
+
+		ble_npl_callout_stop(&light_lightness_actual_transition_timer);
+	}
+}
+
+static void light_lightness_linear_work_handler(struct ble_npl_event *work)
+{
+	struct light_lightness_state *state = &light_lightness_srv_user_data;
+
+	if (enable_transition != LIGTH_LIGHTNESS_LINEAR_TT) {
+		ble_npl_callout_stop(&light_lightness_linear_transition_timer);
+		return;
+	}
+
+	if (state->tt_counter_linear != 0) {
+		u32_t linear;
+
+		state->tt_counter_linear--;
+
+		linear = state->linear - state->tt_delta_linear;
+
+		if (state->linear != linear) {
+			state->linear = linear;
+
+			state_binding(LINEAR, IGNORE_TEMP);
+			update_light_state();
+		} else {
+			enable_transition = DISABLE_TRANSITION;
+		}
+	}
+
+	if (state->tt_counter_linear == 0) {
+		state->linear = state->target_linear;
+
+		state_binding(LINEAR, IGNORE_TEMP);
+		update_light_state();
+
+		ble_npl_callout_stop(&light_lightness_linear_transition_timer);
+	}
+}
+
+static void light_ctl_work_handler(struct ble_npl_event *work)
+{
+	struct light_ctl_state *state = &light_ctl_srv_user_data;
+
+	if (enable_transition != LIGTH_CTL_TT) {
+		ble_npl_callout_stop(&light_ctl_transition_timer);
+		return;
+	}
+
+	if (state->tt_counter != 0) {
+		u32_t lightness, temp;
+		s32_t delta_uv;
+
+		state->tt_counter--;
+
+		/* Lightness */
+		lightness = state->lightness - state->tt_lightness_delta;
+
+		/* Temperature */
+		temp = state->temp - state->tt_temp_delta;
+
+		/* Delta_UV */
+		delta_uv = state->delta_uv - state->tt_duv_delta;
+
+		if (delta_uv > INT16_MAX) {
+			delta_uv = INT16_MAX;
+		} else if (delta_uv < INT16_MIN) {
+			delta_uv = INT16_MIN;
+		}
+
+		if (state->lightness != lightness || state->temp != temp ||
+		    state->delta_uv != delta_uv) {
+			state->lightness = lightness;
+			state->temp = temp;
+			state->delta_uv = delta_uv;
+
+			state_binding(CTL, CTL_TEMP);
+			update_light_state();
+		} else {
+			enable_transition = DISABLE_TRANSITION;
+		}
+	}
+
+	if (state->tt_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();
+
+		ble_npl_callout_stop(&light_ctl_transition_timer);
+	}
+}
+
+static void light_ctl_temp_work_handler(struct ble_npl_event *work)
+{
+	struct light_ctl_state *state = &light_ctl_srv_user_data;
+
+	if (enable_transition != LIGHT_CTL_TEMP_TT) {
+		ble_npl_callout_stop(&light_ctl_temp_transition_timer);
+		return;
+	}
+
+	if (state->tt_counter_temp != 0) {
+		s32_t delta_uv;
+		u32_t temp;
+
+		state->tt_counter_temp--;
+
+		/* Temperature */
+		temp = state->temp - state->tt_temp_delta;
+
+		/* Delta UV */
+		delta_uv = state->delta_uv - state->tt_duv_delta;
+
+		if (delta_uv > INT16_MAX) {
+			delta_uv = INT16_MAX;
+		} else if (delta_uv < INT16_MIN) {
+			delta_uv = INT16_MIN;
+		}
+
+		if (state->temp != temp || state->delta_uv != delta_uv) {
+			state->temp = temp;
+			state->delta_uv = delta_uv;
+
+			state_binding(IGNORE, CTL_TEMP);
+			update_light_state();
+		} else {
+			enable_transition = DISABLE_TRANSITION;
+		}
+	}
+
+	if (state->tt_counter_temp == 0) {
+		state->temp = state->target_temp;
+		state->delta_uv = state->target_delta_uv;
+
+		state_binding(IGNORE, CTL_TEMP);
+		update_light_state();
+
+		ble_npl_callout_stop(&light_ctl_temp_transition_timer);
+	}
+}
+
+static void onoff_tt_handler(struct ble_npl_event *ev)
+{
+	struct generic_onoff_state *state = ble_npl_event_get_arg(ev);
+
+	assert(state != NULL);
+	ble_npl_callout_reset(&onoff_work, 0);
+	ble_npl_callout_reset(&onoff_transition_timer, K_MSEC(state->cal_tt));
+}
+
+static void level_lightness_tt_handler(struct ble_npl_event *ev)
+{
+	struct generic_level_state *state = ble_npl_event_get_arg(ev);
+
+	assert(state != NULL);
+	ble_npl_callout_reset(&level_lightness_work, 0);
+	ble_npl_callout_reset(&level_lightness_transition_timer, K_MSEC(state->cal_tt));
+}
+
+static void level_temp_tt_handler(struct ble_npl_event *ev)
+{
+	struct generic_level_state *state = ble_npl_event_get_arg(ev);
+
+	assert(state != NULL);
+	ble_npl_callout_reset(&level_temp_work, 0);
+	ble_npl_callout_reset(&level_temp_transition_timer, K_MSEC(state->cal_tt));
+}
+
+static void light_lightness_actual_tt_handler(struct ble_npl_event *ev)
+{
+	struct light_lightness_state *state = ble_npl_event_get_arg(ev);
+
+	assert(state != NULL);
+	ble_npl_callout_reset(&light_lightness_actual_work, 0);
+	ble_npl_callout_reset(&light_lightness_actual_transition_timer, K_MSEC(state->cal_tt));
+}
+
+static void light_lightness_linear_tt_handler(struct ble_npl_event *ev)
+{
+	struct light_lightness_state *state = ble_npl_event_get_arg(ev);
+
+	assert(state != NULL);
+	ble_npl_callout_reset(&light_lightness_linear_work, 0);
+	ble_npl_callout_reset(&light_lightness_linear_transition_timer, K_MSEC(state->cal_tt));
+}
+
+static void light_ctl_tt_handler(struct ble_npl_event *ev)
+{
+	struct light_ctl_state *state = ble_npl_event_get_arg(ev);
+
+	assert(state != NULL);
+	ble_npl_callout_reset(&light_ctl_work, 0);
+	ble_npl_callout_reset(&light_ctl_transition_timer, K_MSEC(state->cal_tt));
+}
+
+static void light_ctl_temp_tt_handler(struct ble_npl_event *ev)
+{
+	struct light_ctl_state *state = ble_npl_event_get_arg(ev);
+
+	assert(state != NULL);
+	ble_npl_callout_reset(&light_ctl_temp_work, 0);
+	ble_npl_callout_reset(&light_ctl_temp_transition_timer, K_MSEC(state->cal_tt));
+}
+/* Timers related handlers & threads (End) */
+
+/* Messages handlers (Start) */
+void onoff_handler(struct generic_onoff_state *state)
+{
+	enable_transition = ONOFF_TT;
+
+	ble_npl_callout_set_arg(&onoff_transition_timer, state);
+	ble_npl_callout_reset(&onoff_transition_timer, K_MSEC(5 * state->delay));
+}
+
+void level_lightness_handler(struct generic_level_state *state)
+{
+	ble_npl_callout_set_arg(&level_lightness_transition_timer, state);
+	ble_npl_callout_reset(&level_lightness_transition_timer,
+		      K_MSEC(5 * state->delay));
+}
+
+void level_temp_handler(struct generic_level_state *state)
+{
+	ble_npl_callout_set_arg(&level_temp_transition_timer, state);
+	ble_npl_callout_reset(&level_temp_transition_timer, K_MSEC(5 * state->delay));
+}
+
+void light_lightness_actual_handler(struct light_lightness_state *state)
+{
+	enable_transition = LIGTH_LIGHTNESS_ACTUAL_TT;
+
+	ble_npl_callout_set_arg(&light_lightness_actual_transition_timer, state);
+	ble_npl_callout_reset(&light_lightness_actual_transition_timer,
+		      K_MSEC(5 * state->delay));
+}
+
+void light_lightness_linear_handler(struct light_lightness_state *state)
+{
+	enable_transition = LIGTH_LIGHTNESS_LINEAR_TT;
+
+	ble_npl_callout_set_arg(&light_lightness_linear_transition_timer, state);
+	ble_npl_callout_reset(&light_lightness_linear_transition_timer,
+		      K_MSEC(5 * state->delay));
+}
+
+void light_ctl_handler(struct light_ctl_state *state)
+{
+	enable_transition = LIGTH_CTL_TT;
+
+	ble_npl_callout_set_arg(&light_ctl_transition_timer, state);
+	ble_npl_callout_reset(&light_ctl_transition_timer, K_MSEC(5 * state->delay));
+}
+
+void light_ctl_temp_handler(struct light_ctl_state *state)
+{
+	enable_transition = LIGHT_CTL_TEMP_TT;
+
+	ble_npl_callout_set_arg(&light_ctl_temp_transition_timer, state);
+	ble_npl_callout_reset(&light_ctl_temp_transition_timer,
+		      K_MSEC(5 * state->delay));
+}
+/* Messages handlers (End) */
+
+void transition_timers_init(void)
+{
+	ble_npl_callout_init(&onoff_work, ble_npl_eventq_dflt_get(),
+			     onoff_work_handler, NULL);
+
+	ble_npl_callout_init(&level_lightness_work, ble_npl_eventq_dflt_get(),
+			     level_lightness_work_handler, NULL);
+	ble_npl_callout_init(&level_temp_work, ble_npl_eventq_dflt_get(),
+			     level_temp_work_handler, NULL);
+
+	ble_npl_callout_init(&light_lightness_actual_work, ble_npl_eventq_dflt_get(),
+			     light_lightness_actual_work_handler, NULL);
+	ble_npl_callout_init(&light_lightness_linear_work, ble_npl_eventq_dflt_get(),
+			     light_lightness_linear_work_handler, NULL);
+
+	ble_npl_callout_init(&light_ctl_work, ble_npl_eventq_dflt_get(),
+			     light_ctl_work_handler, NULL);
+	ble_npl_callout_init(&light_ctl_temp_work, ble_npl_eventq_dflt_get(),
+			     light_ctl_temp_work_handler, NULL);
+
+	ble_npl_callout_init(&onoff_transition_timer, ble_npl_eventq_dflt_get(),
+			     onoff_tt_handler, NULL);
+
+	ble_npl_callout_init(&level_lightness_transition_timer, ble_npl_eventq_dflt_get(),
+			     level_lightness_tt_handler, NULL);
+	ble_npl_callout_init(&level_temp_transition_timer, ble_npl_eventq_dflt_get(),
+			     level_temp_tt_handler, NULL);
+
+	ble_npl_callout_init(&light_lightness_actual_transition_timer, ble_npl_eventq_dflt_get(),
+			     light_lightness_actual_tt_handler, NULL);
+	ble_npl_callout_init(&light_lightness_linear_transition_timer, ble_npl_eventq_dflt_get(),
+			     light_lightness_linear_tt_handler, NULL);
+
+	ble_npl_callout_init(&light_ctl_transition_timer, ble_npl_eventq_dflt_get(),
+			     light_ctl_tt_handler, NULL);
+	ble_npl_callout_init(&light_ctl_temp_transition_timer, ble_npl_eventq_dflt_get(),
+			     light_ctl_temp_tt_handler, NULL);
+
+	ble_npl_callout_init(&light_ctl_temp_transition_timer, ble_npl_eventq_dflt_get(),
+			     light_ctl_temp_tt_handler, NULL);
+
+}

+ 58 - 0
apps/blemesh_models_example_2/src/transition.h

@@ -0,0 +1,58 @@
+/* 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 DEVICE_SPECIFIC_RESOLUTION 10
+
+enum transition_time {
+	DISABLE_TRANSITION = 0x00,
+	ONOFF_TT = 0x01,
+	LEVEL_TT,
+	LEVEL_TT_DELTA,
+	LEVEL_TT_MOVE,
+	LIGTH_LIGHTNESS_ACTUAL_TT,
+	LIGTH_LIGHTNESS_LINEAR_TT,
+	LIGTH_CTL_TT,
+	LEVEL_TEMP_TT,
+	LEVEL_TEMP_TT_DELTA,
+	LEVEL_TEMP_TT_MOVE,
+	LIGHT_CTL_TEMP_TT
+};
+
+extern u8_t enable_transition;
+extern u8_t default_tt;
+
+extern struct ble_npl_callout onoff_transition_timer;
+extern struct ble_npl_callout level_lightness_transition_timer;
+extern struct ble_npl_callout level_temp_transition_timer;
+extern struct ble_npl_callout light_lightness_actual_transition_timer;
+extern struct ble_npl_callout light_lightness_linear_transition_timer;
+extern struct ble_npl_callout light_ctl_transition_timer;
+extern struct ble_npl_callout light_ctl_temp_transition_timer;
+
+void onoff_tt_values(struct generic_onoff_state *state);
+void level_tt_values(struct generic_level_state *state);
+void delta_level_tt_values(struct generic_level_state *state);
+void move_level_tt_values(struct generic_level_state *state);
+void light_lightnes_actual_tt_values(struct light_lightness_state *state);
+void light_lightnes_linear_tt_values(struct light_lightness_state *state);
+void light_ctl_tt_values(struct light_ctl_state *state);
+void light_ctl_temp_tt_values(struct light_ctl_state *state);
+
+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

+ 54 - 0
apps/blemesh_models_example_2/syscfg.yml

@@ -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.
+#
+
+syscfg.vals:
+    # Set log level to info (disable debug logging).
+    LOG_LEVEL: 1
+
+    # Default task settings
+    OS_MAIN_STACK_SIZE: 768
+
+    # Newtmgr is not supported in this app, so disable newtmgr-over-shell.
+    SHELL_NEWTMGR: 0
+
+    MSYS_1_BLOCK_COUNT: 48
+
+    FLOAT_USER: 1
+    HARD_FLOAT: 1
+
+    BLE_MESH_ADV_BUF_COUNT: 20
+    BLE_MESH_TX_SEG_MAX: 9
+
+    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_DEBUG: 0
+    BLE_MESH_DEBUG_NET: 0
+    BLE_MESH_DEBUG_TRANS: 0
+    BLE_MESH_DEBUG_BEACON: 0
+    BLE_MESH_DEBUG_CRYPTO: 0
+    BLE_MESH_DEBUG_PROV: 0
+    BLE_MESH_DEBUG_ACCESS: 0
+    BLE_MESH_DEBUG_MODEL: 0
+    BLE_MESH_DEBUG_ADV: 0
+    BLE_MESH_DEBUG_LOW_POWER: 0
+    BLE_MESH_DEBUG_FRIEND: 0
+    BLE_MESH_DEBUG_PROXY: 0

+ 37 - 0
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 <michal.narajowski@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/shell"
+    - nimble/controller
+    - nimble/host
+    - nimble/host/services/gap
+    - nimble/host/services/gatt
+    - nimble/host/store/ram
+    - nimble/transport/ram

+ 112 - 0
apps/blemesh_shell/src/main.c

@@ -0,0 +1,112 @@
+/*
+ * 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 <assert.h>
+#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;
+}

+ 68 - 0
apps/blemesh_shell/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.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
+
+    # Newtmgr is not supported in this app, so disable newtmgr-over-shell.
+    SHELL_NEWTMGR: 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_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_SETTINGS: 0
+    CONFIG_NFFS: 0
+
+    BLE_MESH_DEBUG: 0
+    BLE_MESH_DEBUG_NET: 0
+    BLE_MESH_DEBUG_TRANS: 0
+    BLE_MESH_DEBUG_BEACON: 0
+    BLE_MESH_DEBUG_CRYPTO: 0
+    BLE_MESH_DEBUG_PROV: 0
+    BLE_MESH_DEBUG_ACCESS: 0
+    BLE_MESH_DEBUG_MODEL: 0
+    BLE_MESH_DEBUG_ADV: 0
+    BLE_MESH_DEBUG_LOW_POWER: 0
+    BLE_MESH_DEBUG_FRIEND: 0
+    BLE_MESH_DEBUG_PROXY: 0

+ 44 - 0
apps/bleprph/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/bleprph
+pkg.type: app
+pkg.description: Simple BLE peripheral application.
+pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps:
+    - "@apache-mynewt-core/boot/split"
+    - "@apache-mynewt-core/boot/bootutil"
+    - "@apache-mynewt-core/kernel/os"
+    - "@apache-mynewt-core/mgmt/imgmgr"
+    - "@apache-mynewt-core/mgmt/newtmgr"
+    - "@apache-mynewt-core/mgmt/newtmgr/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/gap
+    - nimble/host/services/gatt
+    - nimble/host/store/config
+    - nimble/host/util
+    - nimble/transport

+ 61 - 0
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 <stdbool.h>
+#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

+ 204 - 0
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 <assert.h>
+#include <stdio.h>
+#include <string.h>
+#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;
+}

+ 347 - 0
apps/bleprph/src/main.c

@@ -0,0 +1,347 @@
+/*
+ * 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 <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#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"
+
+/* 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->connect.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)
+{
+    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);
+
+    /* Set the default device name. */
+    rc = ble_svc_gap_device_name_set("nimble-bleprph");
+    assert(rc == 0);
+
+#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;
+}

+ 43 - 0
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]);
+}

+ 128 - 0
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

+ 63 - 0
apps/bleprph/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:
+    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
+
+    # Log reboot messages to a flash circular buffer.
+    REBOOT_LOG_FCB: 1
+    LOG_FCB: 1
+    CONFIG_FCB: 1
+
+    # Enable newtmgr commands.
+    STATS_NEWTMGR: 1
+    LOG_NEWTMGR: 1
+    CONFIG_NEWTMGR: 1
+
+    # OS main/default task
+    OS_MAIN_STACK_SIZE: 468
+
+    # Lots of smaller mbufs are required for newtmgr using typical BLE ATT MTU
+    # values.
+    MSYS_1_BLOCK_COUNT: 22
+    MSYS_1_BLOCK_SIZE: 110

+ 38 - 0
apps/bletest/pkg.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.
+#
+
+pkg.name: apps/bletest
+pkg.type: app
+pkg.description: Test project for the nimble BLE controller.
+pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps:
+    - net/nimble/controller
+    - net/nimble/host
+    - net/nimble/transport/ram
+    - kernel/os
+    - sys/console/full
+    - sys/shell
+    - sys/config
+    - sys/log/full
+    - sys/log/modlog
+    - sys/stats/full
+pkg.cflags: -DBLETEST

+ 782 - 0
apps/bletest/src/bletest_hci.c

@@ -0,0 +1,782 @@
+/*
+ * 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 <assert.h>
+#include <string.h>
+#include "os/mynewt.h"
+#include "bsp/bsp.h"
+
+/* BLE */
+#include "nimble/ble.h"
+#include "nimble/ble_hci_trans.h"
+#include "nimble/hci_common.h"
+#include "host/ble_hs.h"
+#include "controller/ble_ll.h"
+#include "controller/ble_ll_hci.h"
+#include "controller/ble_ll_conn.h"
+#include "controller/ble_ll_scan.h"
+#include "controller/ble_ll_adv.h"
+
+/* XXX: An app should not include private headers from a library.  The bletest
+ * app uses some of nimble's internal details for logging.
+ */
+#include "../src/ble_hs_priv.h"
+#include "bletest_priv.h"
+
+extern uint16_t g_bletest_ltk_reply_handle;
+
+void
+bletest_send_conn_update(uint16_t handle)
+{
+    int rc;
+    struct hci_conn_update hcu;
+
+    hcu.conn_latency = 4;
+    hcu.supervision_timeout = 2000;
+    hcu.conn_itvl_min = 1000;
+    hcu.conn_itvl_max = 1000;
+    hcu.handle = handle;
+    hcu.min_ce_len = 4;
+    hcu.max_ce_len = 4;
+
+    rc = ble_hs_hci_cmd_le_conn_update(&hcu);
+    assert(rc == 0);
+}
+
+#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1)
+void
+bletest_ltk_req_reply(uint16_t handle)
+{
+    g_bletest_ltk_reply_handle |= (1 << (handle-1));
+}
+
+int
+bletest_send_ltk_req_neg_reply(uint16_t handle)
+{
+    int rc;
+    uint8_t *dst;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + sizeof(uint16_t)];
+    uint16_t ack_conn_handle;
+    uint8_t rsplen;
+
+    dst = buf;
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_LT_KEY_REQ_NEG_REPLY,
+                       sizeof(uint16_t), dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    put_le16(dst, handle);
+    rc = ble_hs_hci_cmd_tx(buf, &ack_conn_handle, 2, &rsplen);
+    if (rc == 0) {
+        if (rsplen != 2) {
+            rc = -1;
+        }
+    }
+
+    return rc;
+}
+
+int
+bletest_send_ltk_req_reply(uint16_t handle)
+{
+    struct hci_lt_key_req_reply hkr;
+    uint16_t ack_conn_handle;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_LT_KEY_REQ_REPLY_LEN];
+    uint8_t ack_params_len;
+    int rc;
+
+    hkr.conn_handle = handle;
+    swap_buf(hkr.long_term_key, (uint8_t *)g_bletest_LTK, 16);
+
+    ble_hs_hci_cmd_build_le_lt_key_req_reply(&hkr, buf, sizeof buf);
+    rc = ble_hs_hci_cmd_tx(buf, &ack_conn_handle, sizeof ack_conn_handle,
+                        &ack_params_len);
+    if (rc != 0) {
+        return rc;
+    }
+    if (ack_params_len != BLE_HCI_LT_KEY_REQ_REPLY_ACK_PARAM_LEN - 1) {
+        return -1;
+    }
+
+    if (le16toh(ack_conn_handle) != handle) {
+        return -1;
+    }
+    return 0;
+}
+#endif
+
+int
+bletest_hci_reset_ctlr(void)
+{
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN];
+
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_CTLR_BASEBAND, BLE_HCI_OCF_CB_RESET,
+                             0, buf);
+    return ble_hs_hci_cmd_tx(buf, NULL, 0, NULL);
+}
+
+int
+bletest_hci_rd_bd_addr(void)
+{
+    int rc;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN];
+    uint8_t rspbuf[BLE_DEV_ADDR_LEN];
+    uint8_t rsplen;
+
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_INFO_PARAMS, BLE_HCI_OCF_IP_RD_BD_ADDR, 0,
+                       buf);
+    rc = ble_hs_hci_cmd_tx(buf, rspbuf, BLE_DEV_ADDR_LEN, &rsplen);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if (rsplen != BLE_DEV_ADDR_LEN) {
+        return BLE_HS_ECONTROLLER;
+    }
+
+    return rc;
+}
+
+#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1)
+int
+bletest_hci_le_encrypt(uint8_t *key, uint8_t *pt)
+{
+    int rc;
+    uint8_t *dst;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_LE_ENCRYPT_LEN];
+    uint8_t rspbuf[16];
+    uint8_t rsplen;
+
+    dst = buf;
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_ENCRYPT,
+                       BLE_HCI_LE_ENCRYPT_LEN, dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    swap_buf(dst, key, BLE_ENC_BLOCK_SIZE);
+    swap_buf(dst + BLE_ENC_BLOCK_SIZE, pt, BLE_ENC_BLOCK_SIZE);
+    rc = ble_hs_hci_cmd_tx(buf, rspbuf, 16, &rsplen);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if (rsplen != 16) {
+        return BLE_HS_ECONTROLLER;
+    }
+    return rc;
+}
+#endif
+
+int
+bletest_hci_le_set_datalen(uint16_t handle, uint16_t txoctets, uint16_t txtime)
+{
+    int rc;
+    uint8_t *dst;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_DATALEN_LEN];
+    uint8_t rspbuf[2];
+    uint8_t rsplen;
+
+    dst = buf;
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_DATA_LEN,
+                       BLE_HCI_SET_DATALEN_LEN, dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    put_le16(dst, handle);
+    put_le16(dst + 2, txoctets);
+    put_le16(dst + 4, txtime);
+    rc = ble_hs_hci_cmd_tx(buf, rspbuf, 2, &rsplen);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if (rsplen != 2) {
+        return BLE_HS_ECONTROLLER;
+    }
+
+    return rc;
+}
+
+int
+bletest_hci_le_write_sugg_datalen(uint16_t txoctets, uint16_t txtime)
+{
+    uint8_t *dst;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_WR_SUGG_DATALEN_LEN];
+
+    dst = buf;
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_WR_SUGG_DEF_DATA_LEN,
+                       BLE_HCI_WR_SUGG_DATALEN_LEN, dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    put_le16(dst, txoctets);
+    put_le16(dst + 2, txtime);
+    return ble_hs_hci_cmd_tx(buf, NULL, 0, NULL);
+}
+
+int
+bletest_hci_le_rd_sugg_datalen(void)
+{
+    int rc;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN];
+    uint8_t rspbuf[BLE_HCI_RD_SUGG_DATALEN_RSPLEN];
+    uint8_t rsplen;
+
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RD_SUGG_DEF_DATA_LEN, 0,
+                       buf);
+
+    rc = ble_hs_hci_cmd_tx(buf, rspbuf, BLE_HCI_RD_SUGG_DATALEN_RSPLEN, &rsplen);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if (rsplen != BLE_HCI_RD_SUGG_DATALEN_RSPLEN) {
+        return BLE_HS_ECONTROLLER;
+    }
+
+    return 0;
+}
+
+int
+bletest_hci_rd_local_version(void)
+{
+    int rc;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN];
+    uint8_t rspbuf[BLE_HCI_RD_LOC_VER_INFO_RSPLEN];
+    uint8_t rsplen;
+
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_INFO_PARAMS, BLE_HCI_OCF_IP_RD_LOCAL_VER, 0,
+                       buf);
+    rc = ble_hs_hci_cmd_tx(buf, rspbuf, BLE_HCI_RD_LOC_VER_INFO_RSPLEN, &rsplen);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if (rsplen != BLE_HCI_RD_LOC_VER_INFO_RSPLEN) {
+        return BLE_HS_ECONTROLLER;
+    }
+    return rc;
+}
+
+int
+bletest_hci_rd_local_feat(void)
+{
+    int rc;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN];
+    uint8_t rspbuf[BLE_HCI_RD_LOC_SUPP_FEAT_RSPLEN];
+    uint8_t rsplen;
+
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_INFO_PARAMS, BLE_HCI_OCF_IP_RD_LOC_SUPP_FEAT,
+                       0, buf);
+    rc = ble_hs_hci_cmd_tx(buf, rspbuf, BLE_HCI_RD_LOC_SUPP_FEAT_RSPLEN, &rsplen);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if (rsplen != BLE_HCI_RD_LOC_SUPP_FEAT_RSPLEN) {
+        return BLE_HS_ECONTROLLER;
+    }
+    return rc;
+}
+
+int
+bletest_hci_rd_local_supp_cmd(void)
+{
+    int rc;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN];
+    uint8_t rspbuf[BLE_HCI_RD_LOC_SUPP_CMD_RSPLEN];
+    uint8_t rsplen;
+
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_INFO_PARAMS, BLE_HCI_OCF_IP_RD_LOC_SUPP_CMD,
+                       0, buf);
+    rc = ble_hs_hci_cmd_tx(buf, rspbuf, BLE_HCI_RD_LOC_SUPP_CMD_RSPLEN, &rsplen);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if (rsplen != BLE_HCI_RD_LOC_SUPP_CMD_RSPLEN) {
+        return BLE_HS_ECONTROLLER;
+    }
+    return rc;
+}
+
+/**
+ * Read supported states
+ *
+ * OGF = 0x08 (LE)
+ * OCF = 0x001C
+ *
+ * @return int
+ */
+int
+bletest_hci_le_read_supp_states(void)
+{
+    int rc;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN];
+    uint8_t rspbuf[BLE_HCI_RD_SUPP_STATES_RSPLEN];
+    uint8_t rsplen;
+
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RD_SUPP_STATES, 0, buf);
+    rc = ble_hs_hci_cmd_tx(buf, rspbuf, BLE_HCI_RD_SUPP_STATES_RSPLEN, &rsplen);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if (rsplen != BLE_HCI_RD_SUPP_STATES_RSPLEN) {
+        return BLE_HS_ECONTROLLER;
+    }
+    return rc;
+}
+
+int
+bletest_hci_le_rd_max_datalen(void)
+{
+    int rc;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN];
+    uint8_t rspbuf[BLE_HCI_RD_MAX_DATALEN_RSPLEN];
+    uint8_t rsplen;
+
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RD_MAX_DATA_LEN, 0, buf);
+    rc = ble_hs_hci_cmd_tx(buf, rspbuf, BLE_HCI_RD_MAX_DATALEN_RSPLEN, &rsplen);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if (rsplen != BLE_HCI_RD_MAX_DATALEN_RSPLEN) {
+        return BLE_HS_ECONTROLLER;
+    }
+    return rc;
+}
+
+#if MYNEWT_VAL(BLE_ANDROID_MULTI_ADV_SUPPORT)
+int
+bletest_hci_le_set_multi_adv_data(uint8_t *data, uint8_t len, uint8_t instance)
+{
+    uint8_t *dst;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_MULTI_ADV_DATA_LEN];
+
+    if (instance >= BLE_LL_ADV_INSTANCES) {
+        return -1;
+    }
+
+    dst = buf;
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_VENDOR, BLE_HCI_OCF_MULTI_ADV,
+                             BLE_HCI_MULTI_ADV_DATA_LEN, dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    if (((data == NULL) && (len != 0)) || (len > BLE_HCI_MAX_ADV_DATA_LEN)) {
+        return BLE_ERR_INV_HCI_CMD_PARMS;
+    }
+
+    memset(dst, 0, BLE_HCI_MULTI_ADV_DATA_LEN);
+    dst[0] = BLE_HCI_MULTI_ADV_DATA;
+    dst[1] = len;
+    memcpy(dst + 2, data, len);
+    dst[33] = instance;
+
+    return ble_hs_hci_cmd_tx_empty_ack(buf);
+}
+#else
+int
+bletest_hci_le_set_adv_data(uint8_t *data, uint8_t len)
+{
+    int rc;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_ADV_DATA_LEN];
+
+    rc = ble_hs_hci_cmd_build_le_set_adv_data(data, len, buf, sizeof buf);
+    assert(rc == 0);
+    return ble_hs_hci_cmd_tx_empty_ack(buf);
+}
+#endif
+
+#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1)
+int
+bletest_hci_le_start_encrypt(struct hci_start_encrypt *cmd)
+{
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_LE_START_ENCRYPT_LEN];
+
+    ble_hs_hci_cmd_build_le_start_encrypt(cmd, buf, sizeof buf);
+    return ble_hs_hci_cmd_tx_empty_ack(buf);
+}
+#endif
+
+int
+bletest_hci_le_read_rem_used_feat(uint16_t handle)
+{
+    uint8_t *dst;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_CONN_RD_REM_FEAT_LEN];
+
+    dst = buf;
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RD_REM_FEAT,
+                       BLE_HCI_CONN_RD_REM_FEAT_LEN, dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    put_le16(dst, handle);
+    return ble_hs_hci_cmd_tx(buf, NULL, 0, NULL);
+}
+
+#if MYNEWT_VAL(BLE_ANDROID_MULTI_ADV_SUPPORT)
+int
+bletest_hci_le_set_multi_adv_params(struct hci_multi_adv_params *adv,
+                                    uint8_t instance)
+{
+    uint8_t *dst;
+    uint16_t itvl;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_MULTI_ADV_PARAMS_LEN];
+
+    if (instance >= BLE_LL_ADV_INSTANCES) {
+        return -1;
+    }
+
+    dst = buf;
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_VENDOR, BLE_HCI_OCF_MULTI_ADV,
+                             BLE_HCI_MULTI_ADV_PARAMS_LEN, dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    /* Make sure parameters are valid */
+    if ((adv->adv_itvl_min > adv->adv_itvl_max) ||
+        (adv->own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) ||
+        (adv->peer_addr_type > BLE_HCI_ADV_PEER_ADDR_MAX) ||
+        (adv->adv_filter_policy > BLE_HCI_ADV_FILT_MAX) ||
+        (adv->adv_type > BLE_HCI_ADV_TYPE_MAX) ||
+        (adv->adv_channel_map == 0) ||
+        ((adv->adv_channel_map & 0xF8) != 0)) {
+        /* These parameters are not valid */
+        return -1;
+    }
+
+    /* Make sure interval is valid for advertising type. */
+    if ((adv->adv_type == BLE_HCI_ADV_TYPE_ADV_NONCONN_IND) ||
+        (adv->adv_type == BLE_HCI_ADV_TYPE_ADV_SCAN_IND)) {
+        itvl = BLE_HCI_ADV_ITVL_NONCONN_MIN;
+    } else {
+        itvl = BLE_HCI_ADV_ITVL_MIN;
+    }
+
+    /* Do not check if high duty-cycle directed */
+    if (adv->adv_type != BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD) {
+        if ((adv->adv_itvl_min < itvl) ||
+            (adv->adv_itvl_min > BLE_HCI_ADV_ITVL_MAX)) {
+            return -1;
+        }
+    }
+
+    dst[0] = BLE_HCI_MULTI_ADV_PARAMS;
+    put_le16(dst + 1, adv->adv_itvl_min);
+    put_le16(dst + 3, adv->adv_itvl_max);
+    dst[5] = adv->adv_type;
+    dst[6] = adv->own_addr_type;
+    memcpy(dst + 7, adv->own_addr, BLE_DEV_ADDR_LEN);
+    dst[13] = adv->peer_addr_type;
+    memcpy(dst + 14, adv->peer_addr, BLE_DEV_ADDR_LEN);
+    dst[20] = adv->adv_channel_map;
+    dst[21] = adv->adv_filter_policy;
+    dst[22] = instance;
+    dst[23] = adv->adv_tx_pwr;
+
+    return ble_hs_hci_cmd_tx_empty_ack(buf);
+}
+#else
+int
+bletest_hci_le_set_adv_params(struct hci_adv_params *adv)
+{
+    int rc;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_ADV_PARAM_LEN];
+
+    rc = ble_hs_hci_cmd_build_le_set_adv_params(adv, buf, sizeof buf);
+    if (!rc) {
+        rc = ble_hs_hci_cmd_tx_empty_ack(buf);
+    }
+    return rc;
+}
+#endif
+
+int
+bletest_hci_le_set_rand_addr(uint8_t *addr)
+{
+    uint8_t *dst;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_RAND_ADDR_LEN];
+
+    dst = buf;
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_RAND_ADDR,
+                       BLE_DEV_ADDR_LEN, dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    memcpy(dst, addr, BLE_DEV_ADDR_LEN);
+    return ble_hs_hci_cmd_tx(buf, NULL, 0, NULL);
+}
+
+#if MYNEWT_VAL(BLE_ANDROID_MULTI_ADV_SUPPORT)
+int
+bletest_hci_le_set_multi_rand_addr(uint8_t *addr, uint8_t instance)
+{
+    uint8_t *dst;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_MULTI_ADV_SET_RAND_ADDR_LEN];
+
+    if (instance >= BLE_LL_ADV_INSTANCES) {
+        return -1;
+    }
+
+    dst = buf;
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_VENDOR, BLE_HCI_OCF_MULTI_ADV,
+                       BLE_HCI_MULTI_ADV_SET_RAND_ADDR_LEN, dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    dst[0] = BLE_HCI_MULTI_ADV_SET_RAND_ADDR;
+    memcpy(dst + 1, addr, BLE_DEV_ADDR_LEN);
+    dst[7] = instance;
+    return ble_hs_hci_cmd_tx(buf, NULL, 0, NULL);
+}
+#endif
+
+int
+bletest_hci_rd_rem_version(uint16_t handle)
+{
+    uint8_t *dst;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + sizeof(uint16_t)];
+
+    dst = buf;
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LINK_CTRL, BLE_HCI_OCF_RD_REM_VER_INFO,
+                       sizeof(uint16_t), dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    put_le16(dst, handle);
+    return ble_hs_hci_cmd_tx(buf, NULL, 0, NULL);
+}
+
+int
+bletest_hci_le_set_host_chan_class(uint8_t *chanmap)
+{
+    uint8_t *dst;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_HOST_CHAN_CLASS_LEN];
+
+    dst = buf;
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_HOST_CHAN_CLASS,
+                       BLE_HCI_SET_HOST_CHAN_CLASS_LEN, dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    memcpy(dst, chanmap, BLE_HCI_SET_HOST_CHAN_CLASS_LEN);
+    return ble_hs_hci_cmd_tx(buf, NULL, 0, NULL);
+}
+
+int
+bletest_hci_le_rd_chanmap(uint16_t handle)
+{
+    int rc;
+    uint8_t *dst;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_RD_CHANMAP_LEN];
+    uint8_t rspbuf[BLE_HCI_RD_CHANMAP_RSP_LEN];
+    uint8_t rsplen;
+
+    dst = buf;
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RD_CHAN_MAP,
+                       BLE_HCI_RD_CHANMAP_LEN, dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    put_le16(dst, handle);
+    rc = ble_hs_hci_cmd_tx(buf, rspbuf, BLE_HCI_RD_CHANMAP_RSP_LEN, &rsplen);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if (rsplen != BLE_HCI_RD_CHANMAP_RSP_LEN) {
+        return BLE_HS_ECONTROLLER;
+    }
+
+    return rc;
+}
+
+#if MYNEWT_VAL(BLE_ANDROID_MULTI_ADV_SUPPORT)
+int
+bletest_hci_le_set_multi_adv_enable(uint8_t enable, uint8_t instance)
+{
+    uint8_t *dst;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_MULTI_ADV_ENABLE_LEN];
+
+    if (instance >= BLE_LL_ADV_INSTANCES) {
+        return -1;
+    }
+
+    dst = buf;
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_VENDOR, BLE_HCI_OCF_MULTI_ADV,
+                             BLE_HCI_MULTI_ADV_ENABLE_LEN, dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    dst[0] = BLE_HCI_MULTI_ADV_ENABLE;
+    dst[1] = enable;
+    dst[2] = instance;
+    return ble_hs_hci_cmd_tx(buf, NULL, 0, NULL);
+}
+#else
+int
+bletest_hci_le_set_adv_enable(uint8_t enable)
+{
+    uint8_t *dst;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_ADV_ENABLE_LEN];
+
+    dst = buf;
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_ADV_ENABLE,
+                       BLE_HCI_SET_ADV_ENABLE_LEN, dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    dst[0] = enable;
+    return ble_hs_hci_cmd_tx(buf, NULL, 0, NULL);
+}
+#endif
+
+int
+bletest_hci_le_set_event_mask(uint64_t event_mask)
+{
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_LE_EVENT_MASK_LEN];
+
+    ble_hs_hci_cmd_build_le_set_event_mask(event_mask, buf, sizeof buf);
+    return ble_hs_hci_cmd_tx_empty_ack(buf);
+}
+
+int
+bletest_hci_set_event_mask(uint64_t event_mask)
+{
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_EVENT_MASK_LEN];
+
+    ble_hs_hci_cmd_build_set_event_mask(event_mask, buf, sizeof buf);
+    return ble_hs_hci_cmd_tx_empty_ack(buf);
+}
+
+#if MYNEWT_VAL(BLE_ANDROID_MULTI_ADV_SUPPORT)
+int
+bletest_hci_le_set_multi_scan_rsp_data(uint8_t *data, uint8_t len,
+                                       uint8_t instance)
+{
+    uint8_t *dst;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_MULTI_ADV_SCAN_RSP_DATA_LEN];
+
+    if (instance >= BLE_LL_ADV_INSTANCES) {
+        return -1;
+    }
+
+    dst = buf;
+    ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_VENDOR, BLE_HCI_OCF_MULTI_ADV,
+                             BLE_HCI_MULTI_ADV_SCAN_RSP_DATA_LEN, dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    if (((data == NULL) && (len != 0)) || (len>BLE_HCI_MAX_SCAN_RSP_DATA_LEN)) {
+        return BLE_ERR_INV_HCI_CMD_PARMS;
+    }
+
+    memset(dst, 0, BLE_HCI_MULTI_ADV_SCAN_RSP_DATA_LEN);
+    dst[0] = BLE_HCI_MULTI_ADV_SCAN_RSP_DATA;
+    dst[1] = len;
+    memcpy(dst + 2, data, len);
+    dst[33] = instance;
+    return ble_hs_hci_cmd_tx_empty_ack(buf);
+}
+#else
+int
+bletest_hci_le_set_scan_rsp_data(uint8_t *data, uint8_t len)
+{
+    int rc;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_SCAN_RSP_DATA_LEN];
+
+    rc = ble_hs_hci_cmd_build_le_set_scan_rsp_data(data, len, buf, sizeof buf);
+    assert(rc == 0);
+    return ble_hs_hci_cmd_tx_empty_ack(buf);
+}
+#endif
+
+int
+bletest_hci_cmd_le_set_scan_params(uint8_t scan_type, uint16_t scan_itvl,
+                                   uint16_t scan_window, uint8_t own_addr_type,
+                                   uint8_t filter_policy) {
+    int rc;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_SCAN_PARAM_LEN];
+
+    rc = ble_hs_hci_cmd_build_le_set_scan_params(scan_type, scan_itvl,
+                                               scan_window, own_addr_type,
+                                               filter_policy, buf, sizeof buf);
+    if (!rc) {
+        rc = ble_hs_hci_cmd_tx_empty_ack(buf);
+    }
+    return rc;
+}
+
+int
+bletest_hci_le_add_to_whitelist(uint8_t *addr, uint8_t addr_type)
+{
+    int rc;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_SCAN_PARAM_LEN];
+
+    rc = ble_hs_hci_cmd_build_le_add_to_whitelist(addr, addr_type, buf,
+                                                sizeof buf);
+    if (!rc) {
+        rc = ble_hs_hci_cmd_tx_empty_ack(buf);
+    }
+    return rc;
+}
+
+int
+bletest_hci_le_set_scan_enable(uint8_t enable, uint8_t filter_dups)
+{
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_SCAN_ENABLE_LEN];
+
+    ble_hs_hci_cmd_build_le_set_scan_enable(enable, filter_dups, buf, sizeof buf);
+    return ble_hs_hci_cmd_tx_empty_ack(buf);
+}
+
+int
+bletest_hci_le_create_connection(struct hci_create_conn *hcc)
+{
+    int rc;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_CREATE_CONN_LEN];
+
+    rc = ble_hs_hci_cmd_build_le_create_connection(hcc, buf, sizeof buf);
+    if (!rc) {
+        rc = ble_hs_hci_cmd_tx_empty_ack(buf);
+    }
+    return rc;
+}
+
+int
+bletest_hci_le_add_resolv_list(uint8_t *local_irk, uint8_t *peer_irk,
+                               uint8_t *peer_ident_addr, uint8_t addr_type)
+{
+    int rc;
+    struct hci_add_dev_to_resolving_list padd;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_ADD_TO_RESOLV_LIST_LEN];
+
+    padd.addr_type = addr_type;
+    memcpy(padd.addr, peer_ident_addr, BLE_DEV_ADDR_LEN);
+    swap_buf(padd.local_irk, local_irk, 16);
+    swap_buf(padd.peer_irk, peer_irk, 16);
+    rc = ble_hs_hci_cmd_build_add_to_resolv_list(&padd, buf, sizeof buf);
+    if (!rc) {
+        rc = ble_hs_hci_cmd_tx_empty_ack(buf);
+    }
+    return rc;
+}
+
+int
+bletest_hci_le_enable_resolv_list(uint8_t enable)
+{
+    int rc;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_ADDR_RESOL_ENA_LEN];
+
+
+    rc = ble_hs_hci_cmd_build_set_addr_res_en(enable, buf, sizeof buf);
+    if (!rc) {
+        rc = ble_hs_hci_cmd_tx_empty_ack(buf);
+    }
+    return rc;
+}
+

+ 86 - 0
apps/bletest/src/bletest_priv.h

@@ -0,0 +1,86 @@
+/*
+ * 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_BLETEST_PRIV_
+#define H_BLETEST_PRIV_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void bletest_send_conn_update(uint16_t handle);
+
+#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1)
+void bletest_ltk_req_reply(uint16_t handle);
+int bletest_send_ltk_req_neg_reply(uint16_t handle);
+int bletest_send_ltk_req_reply(uint16_t handle);
+int bletest_hci_le_start_encrypt(struct hci_start_encrypt *cmd);
+int bletest_hci_le_encrypt(uint8_t *key, uint8_t *pt);
+#endif
+
+int bletest_hci_reset_ctlr(void);
+int bletest_hci_rd_bd_addr(void);
+int bletest_hci_le_set_datalen(uint16_t handle, uint16_t txoctets,
+                               uint16_t txtime);
+int bletest_hci_le_write_sugg_datalen(uint16_t txoctets, uint16_t txtime);
+int bletest_hci_le_rd_sugg_datalen(void);
+int bletest_hci_rd_local_version(void);
+int bletest_hci_rd_local_feat(void);
+int bletest_hci_rd_local_supp_cmd(void);
+int bletest_hci_le_read_supp_states(void);
+int bletest_hci_le_rd_max_datalen(void);
+int bletest_hci_le_read_rem_used_feat(uint16_t handle);
+int bletest_hci_le_set_rand_addr(uint8_t *addr);
+int bletest_hci_rd_rem_version(uint16_t handle);
+int bletest_hci_le_set_host_chan_class(uint8_t *chanmap);
+int bletest_hci_le_rd_chanmap(uint16_t handle);
+int bletest_hci_le_set_event_mask(uint64_t event_mask);
+int bletest_hci_set_event_mask(uint64_t event_mask);
+int bletest_hci_le_add_to_whitelist(uint8_t *addr, uint8_t addr_type);
+int bletest_hci_le_set_scan_enable(uint8_t enable, uint8_t filter_dups);
+int bletest_hci_le_create_connection(struct hci_create_conn *hcc);
+int bletest_hci_le_set_scan_params(uint8_t scan_type, uint16_t scan_itvl,
+                                   uint16_t scan_window, uint8_t own_addr_type,
+                                   uint8_t filter_policy);
+int bletest_hci_le_add_resolv_list(uint8_t *local_irk, uint8_t *peer_irk,
+                                   uint8_t *peer_ident_addr, uint8_t addr_type);
+int bletest_hci_le_enable_resolv_list(uint8_t enable);
+
+#if MYNEWT_VAL(BLE_ANDROID_MULTI_ADV_SUPPORT)
+int bletest_hci_le_set_multi_rand_addr(uint8_t *addr, uint8_t instance);
+int bletest_hci_le_set_multi_adv_data(uint8_t *data, uint8_t len,
+                                      uint8_t instance);
+int bletest_hci_le_set_multi_adv_params(struct hci_multi_adv_params *adv,
+                                        uint8_t instance);
+int bletest_hci_le_set_multi_adv_enable(uint8_t enable, uint8_t instance);
+int bletest_hci_le_set_multi_scan_rsp_data(uint8_t *data, uint8_t len,
+                                           uint8_t instance);
+#else
+int bletest_hci_le_set_adv_data(uint8_t *data, uint8_t len);
+int bletest_hci_le_set_adv_params(struct hci_adv_params *adv);
+int bletest_hci_le_set_adv_enable(uint8_t enable);
+int bletest_hci_le_set_scan_rsp_data(uint8_t *data, uint8_t len);
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* H_BLETEST_PRIV_*/

+ 1323 - 0
apps/bletest/src/main.c

@@ -0,0 +1,1323 @@
+/*
+ * 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 <assert.h>
+#include <string.h>
+#include "os/mynewt.h"
+#include "bsp/bsp.h"
+#include "hal/hal_bsp.h"
+#include "hal/hal_gpio.h"
+#include "hal/hal_flash.h"
+#include "console/console.h"
+#include "shell/shell.h"
+#include "stats/stats.h"
+#include "flash_map/flash_map.h"
+
+/* BLE */
+#include "nimble/ble.h"
+#include "nimble/ble_hci_trans.h"
+#include "nimble/hci_common.h"
+#include "host/ble_hs.h"
+#include "controller/ble_ll.h"
+#include "controller/ble_ll_hci.h"
+#include "controller/ble_ll_conn.h"
+#include "controller/ble_ll_scan.h"
+#include "controller/ble_ll_adv.h"
+
+/* RAM HCI transport. */
+#include "transport/ram/ble_hci_ram.h"
+
+/* XXX: An app should not include private headers from a library.  The bletest
+ * app uses some of nimble's internal details for logging.
+ */
+#include "../src/ble_hs_priv.h"
+#include "bletest_priv.h"
+
+#define BLETEST_TASK_PRIO               5
+
+/* For LED toggling */
+int g_led_pin;
+
+/* A buffer for host advertising data */
+uint8_t g_host_adv_data[BLE_HCI_MAX_ADV_DATA_LEN];
+uint8_t g_host_adv_len;
+
+/* Some application configurations */
+#define BLETEST_ROLE_NONE               (0)
+#define BLETEST_ROLE_ADVERTISER         (1)
+#define BLETEST_ROLE_SCANNER            (2)
+#define BLETEST_ROLE_INITIATOR          (3)
+
+#if MYNEWT_VAL(BLETEST_ROLE) == BLETEST_ROLE_ADVERTISER
+#define BLETEST_CFG_ROLE                BLETEST_ROLE_ADVERTISER
+#endif
+#if MYNEWT_VAL(BLETEST_ROLE) == BLETEST_ROLE_SCANNER
+#define BLETEST_CFG_ROLE                BLETEST_ROLE_SCANNER
+#endif
+#if MYNEWT_VAL(BLETEST_ROLE) == BLETEST_ROLE_INITIATOR
+#define BLETEST_CFG_ROLE                BLETEST_ROLE_INITIATOR
+#endif
+
+#ifndef BLETEST_CFG_ROLE
+#error "No role defined! Must define a valid role in syscfg.yml in apps/bletest"
+#endif
+
+/* Advertiser config */
+#define BLETEST_CFG_ADV_OWN_ADDR_TYPE   (BLE_HCI_ADV_OWN_ADDR_PUBLIC)
+#define BLETEST_CFG_ADV_PEER_ADDR_TYPE  (BLE_HCI_ADV_PEER_ADDR_PUBLIC)
+#define BLETEST_CFG_ADV_ITVL            (60000 / BLE_HCI_ADV_ITVL)
+#define BLETEST_CFG_ADV_TYPE            BLE_HCI_ADV_TYPE_ADV_IND
+#define BLETEST_CFG_ADV_FILT_POLICY     (BLE_HCI_ADV_FILT_NONE)
+#define BLETEST_CFG_ADV_ADDR_RES_EN     (0)
+
+/* Multi-adv config */
+/*
+ * Number of advertising instances to start up, not including the default
+ * instance. The default instance is used to connect. If this number is greater
+ * than the number of available advertising instances, we only use the number
+ * of available advertising instances (defined by the configuration setting:
+ * BLE_MULTI_ADV_INSTANCES.
+ */
+#define BLETEST_CFG_ADV_TEST_INSTANCES     (8)
+
+struct bletest_multi_adv_interval
+{
+    uint8_t adv_type;
+    /*
+     * Note: if own addr type greater than 1, we use own addr field; otherwise
+     * we use the set multi random address call to set the random address
+     */
+    uint8_t adv_own_addr_type;
+    uint16_t adv_itvl;
+};
+
+/*
+ * NOTE: currently, these are all NONCONN_IND. Thus, must be 100 msecs or
+ * greater
+ */
+const struct bletest_multi_adv_interval
+bletest_multi_adv_instances[BLETEST_CFG_ADV_TEST_INSTANCES] = {
+    {BLE_HCI_ADV_TYPE_ADV_NONCONN_IND,
+     BLE_HCI_ADV_OWN_ADDR_PUBLIC,
+     (100000 / BLE_HCI_ADV_ITVL)},
+
+    {BLE_HCI_ADV_TYPE_ADV_SCAN_IND,
+     BLE_HCI_ADV_OWN_ADDR_RANDOM,
+     (110000 / BLE_HCI_ADV_ITVL)},
+
+    {BLE_HCI_ADV_TYPE_ADV_NONCONN_IND,
+     BLE_HCI_ADV_OWN_ADDR_RANDOM,
+     (120000 / BLE_HCI_ADV_ITVL)},
+
+    {BLE_HCI_ADV_TYPE_ADV_NONCONN_IND,
+     BLE_HCI_ADV_OWN_ADDR_PUBLIC,
+     (130000 / BLE_HCI_ADV_ITVL)},
+
+    {BLE_HCI_ADV_TYPE_ADV_SCAN_IND,
+     BLE_HCI_ADV_OWN_ADDR_MAX + 1,
+     (140000 / BLE_HCI_ADV_ITVL)},
+
+    {BLE_HCI_ADV_TYPE_ADV_NONCONN_IND,
+     BLE_HCI_ADV_OWN_ADDR_MAX + 1,
+     (150000 / BLE_HCI_ADV_ITVL)},
+
+    {BLE_HCI_ADV_TYPE_ADV_NONCONN_IND,
+     BLE_HCI_ADV_OWN_ADDR_PUBLIC,
+     (160000 / BLE_HCI_ADV_ITVL)},
+
+    {BLE_HCI_ADV_TYPE_ADV_SCAN_IND,
+     BLE_HCI_ADV_OWN_ADDR_PUBLIC,
+     (170000 / BLE_HCI_ADV_ITVL)}
+};
+
+/*
+ * Determines if own address contains random address or set through the
+ * multi-adv set random address command
+ */
+#define BLETEST_CFG_MULTI_ADV_RANDOM_OWN    (0)
+
+/* Scan config */
+#define BLETEST_CFG_SCAN_ITVL           (700000 / BLE_HCI_SCAN_ITVL)
+#define BLETEST_CFG_SCAN_WINDOW         (700000 / BLE_HCI_SCAN_ITVL)
+#define BLETEST_CFG_SCAN_TYPE           (BLE_HCI_SCAN_TYPE_PASSIVE)
+#define BLETEST_CFG_SCAN_OWN_ADDR_TYPE  (BLE_HCI_ADV_OWN_ADDR_PUBLIC)
+#define BLETEST_CFG_SCAN_FILT_POLICY    (BLE_HCI_SCAN_FILT_NO_WL)
+#define BLETEST_CFG_FILT_DUP_ADV        (1)
+
+/* Connection config */
+#define BLETEST_CFG_CONN_ITVL           (128)  /* in 1.25 msec increments */
+#define BLETEST_CFG_SLAVE_LATENCY       (0)
+#define BLETEST_CFG_INIT_FILTER_POLICY  (BLE_HCI_CONN_FILT_NO_WL)
+#define BLETEST_CFG_CONN_SPVN_TMO       (1000)  /* 10 msec increments */
+#define BLETEST_CFG_MIN_CE_LEN          (6)
+#define BLETEST_CFG_MAX_CE_LEN          (BLETEST_CFG_CONN_ITVL)
+#define BLETEST_CFG_CONN_PEER_ADDR_TYPE (BLE_HCI_CONN_PEER_ADDR_PUBLIC)
+#define BLETEST_CFG_CONN_OWN_ADDR_TYPE  (BLE_HCI_ADV_OWN_ADDR_PUBLIC)
+#define BLETEST_CFG_CONCURRENT_CONNS    (1)
+
+/* Test packet config */
+#define BLETEST_CFG_RAND_PKT_SIZE       (1)
+#define BLETEST_CFG_SUGG_DEF_TXOCTETS   (251)
+#define BLETEST_CFG_SUGG_DEF_TXTIME     \
+    ble_ll_pdu_tx_time_get(BLETEST_CFG_SUGG_DEF_TXOCTETS + 4, BLE_PHY_1M)
+
+/* Test configurations. One of these should be set to 1 */
+#if !defined(BLETEST_CONCURRENT_CONN_TEST) && !defined(BLETEST_THROUGHPUT_TEST)
+    #define BLETEST_CONCURRENT_CONN_TEST    (1)
+#endif
+
+/* BLETEST variables */
+#undef BLETEST_ADV_PKT_NUM
+#define BLETEST_MAX_PKT_SIZE            (247)
+#define BLETEST_PKT_SIZE                (247)
+#define BLETEST_STACK_SIZE              (256)
+uint32_t g_next_os_time;
+int g_bletest_state;
+struct os_eventq g_bletest_evq;
+struct os_callout g_bletest_timer;
+struct os_task bletest_task;
+bssnz_t os_stack_t bletest_stack[BLETEST_STACK_SIZE];
+uint32_t g_bletest_conn_end;
+int g_bletest_start_update;
+uint32_t g_bletest_conn_upd_time;
+uint8_t g_bletest_current_conns;
+uint8_t g_bletest_cur_peer_addr[BLE_DEV_ADDR_LEN];
+uint8_t g_last_handle_used;
+uint8_t g_bletest_led_state;
+uint32_t g_bletest_led_rate;
+uint32_t g_bletest_next_led_time;
+uint16_t g_bletest_handle;
+uint16_t g_bletest_completed_pkts;
+uint16_t g_bletest_outstanding_pkts;
+uint16_t g_bletest_ltk_reply_handle;
+uint32_t g_bletest_hw_id[4];
+struct hci_create_conn g_cc;
+
+/* --- For LE encryption testing --- */
+/* Key: 0x4C68384139F574D836BCF34E9DFB01BF */
+const uint8_t g_ble_ll_encrypt_test_key[16] =
+{
+    0x4c, 0x68, 0x38, 0x41, 0x39, 0xf5, 0x74, 0xd8,
+    0x36, 0xbc, 0xf3, 0x4e, 0x9d, 0xfb, 0x01, 0xbf
+};
+
+/* Plaint text: 0x0213243546576879acbdcedfe0f10213 */
+const uint8_t g_ble_ll_encrypt_test_plain_text[16] =
+{
+    0x02, 0x13, 0x24, 0x35, 0x46, 0x57, 0x68, 0x79,
+    0xac, 0xbd, 0xce, 0xdf, 0xe0, 0xf1, 0x02, 0x13
+};
+
+/* Encrypted data: 0x99ad1b5226a37e3e058e3b8e27c2c666 */
+const uint8_t g_ble_ll_encrypt_test_encrypted_data[16] =
+{
+    0x99, 0xad, 0x1b, 0x52, 0x26, 0xa3, 0x7e, 0x3e,
+    0x05, 0x8e, 0x3b, 0x8e, 0x27, 0xc2, 0xc6, 0x66
+};
+
+#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
+uint8_t g_bletest_adv_irk[16] = {
+    0xec, 0x02, 0x34, 0xa3, 0x57, 0xc8, 0xad, 0x05,
+    0x34, 0x10, 0x10, 0xa6, 0x0a, 0x39, 0x7d, 0x9b
+};
+
+uint8_t g_bletest_init_irk[16] = {
+    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+    0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10
+};
+#endif
+
+#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1)
+/* LTK 0x4C68384139F574D836BCF34E9DFB01BF */
+const uint8_t g_bletest_LTK[16] =
+{
+    0x4C,0x68,0x38,0x41,0x39,0xF5,0x74,0xD8,
+    0x36,0xBC,0xF3,0x4E,0x9D,0xFB,0x01,0xBF
+};
+uint16_t g_bletest_EDIV = 0x2474;
+uint64_t g_bletest_RAND = 0xABCDEF1234567890;
+uint64_t g_bletest_SKDm = 0xACBDCEDFE0F10213;
+uint64_t g_bletest_SKDs = 0x0213243546576879;
+uint32_t g_bletest_IVm = 0xBADCAB24;
+uint32_t g_bletest_IVs = 0xDEAFBABE;
+#endif
+
+#if (BLETEST_THROUGHPUT_TEST == 1)
+void
+bletest_completed_pkt(uint16_t handle)
+{
+    os_sr_t sr;
+
+    OS_ENTER_CRITICAL(sr);
+    if (handle == g_bletest_handle) {
+        ++g_bletest_completed_pkts;
+    }
+    OS_EXIT_CRITICAL(sr);
+}
+#endif
+
+#ifdef BLETEST_ADV_PKT_NUM
+void
+bletest_inc_adv_pkt_num(void)
+{
+    int rc;
+    uint8_t *dptr;
+    uint8_t digit;
+
+    if (g_host_adv_len != 0) {
+        dptr = &g_host_adv_data[18];
+        while (dptr >= &g_host_adv_data[13]) {
+            digit = *dptr;
+            ++digit;
+            if (digit == 58) {
+                digit = 48;
+                *dptr = digit;
+                --dptr;
+            } else {
+                *dptr = digit;
+                break;
+            }
+        }
+
+        rc = bletest_hci_le_set_adv_data(g_host_adv_data, g_host_adv_len);
+        assert(rc == 0);
+    }
+}
+#endif
+
+/**
+ * Sets the advertising data to be sent in advertising pdu's which contain
+ * advertising data.
+ *
+ * @param dptr
+ * @return uint8_t
+ */
+uint8_t
+bletest_set_adv_data(uint8_t *dptr, uint8_t *addr)
+{
+    uint8_t len;
+
+    /* Place flags in first */
+    dptr[0] = 0x02;
+    dptr[1] = 0x01;     /* Flags identifier */
+    dptr[2] = 0x06;
+    dptr += 3;
+    len = 3;
+
+    /*  Add HID service */
+    dptr[0] = 0x03;
+    dptr[1] = 0x03;
+    dptr[2] = 0x12;
+    dptr[3] = 0x18;
+    dptr += 4;
+    len += 4;
+
+    /* Add local name */
+    dptr[0] = 12;   /* Length of this data, not including the length */
+    dptr[1] = 0x09;
+    dptr[2] = 'r';
+    dptr[3] = 'u';
+    dptr[4] = 'n';
+    dptr[5] = 't';
+    dptr[6] = 'i';
+    dptr[7] = 'm';
+    dptr[8] = 'e';
+    dptr[9] = '-';
+    dptr[10] = '0';
+    dptr[11] = '0';
+    dptr[12] = '7';
+    dptr += 13;
+    len += 13;
+
+    /* Add local device address */
+    dptr[0] = 0x08;
+    dptr[1] = 0x1B;
+    dptr[2] = 0x00;
+    memcpy(dptr + 3, addr, BLE_DEV_ADDR_LEN);
+    len += 9;
+
+    g_host_adv_len = len;
+
+    return len;
+}
+
+#if (BLETEST_CFG_ROLE == BLETEST_ROLE_ADVERTISER)
+#if MYNEWT_VAL(BLE_ANDROID_MULTI_ADV_SUPPORT)
+void
+bletest_init_adv_instances(void)
+{
+    uint8_t i;
+    int rc;
+    uint8_t *addr;
+    uint8_t adv_len;
+    uint8_t inst_allowed;
+    uint8_t rand_addr[BLE_DEV_ADDR_LEN];
+    struct hci_multi_adv_params adv;
+
+    inst_allowed = MYNEWT_VAL(BLE_MULTI_ADV_INSTANCES);
+    if (inst_allowed > BLETEST_CFG_ADV_TEST_INSTANCES) {
+        inst_allowed = BLETEST_CFG_ADV_TEST_INSTANCES;
+    }
+
+    /* Start up all the instances */
+    for (i = 1; i <= inst_allowed; ++i) {
+        memset(&adv, 0, sizeof(struct hci_multi_adv_params));
+
+        adv.own_addr_type = bletest_multi_adv_instances[i-1].adv_own_addr_type;
+        if (adv.own_addr_type == BLE_HCI_ADV_OWN_ADDR_PUBLIC) {
+            addr = g_dev_addr;
+        } else {
+            memcpy(rand_addr, g_dev_addr, BLE_DEV_ADDR_LEN);
+            rand_addr[5] |= 0xc0;
+            rand_addr[0] = i;
+            /*
+             * NOTE: we overload own address type with a special case
+             * to denote if we use own address or call to set multi random
+             * address.
+             */
+            if (adv.own_addr_type == BLE_HCI_ADV_OWN_ADDR_RANDOM) {
+                rc = bletest_hci_le_set_multi_rand_addr(rand_addr, i);
+                assert(rc == 0);
+                addr = rand_addr;
+            } else {
+                adv.own_addr_type = BLE_HCI_ADV_OWN_ADDR_RANDOM;
+                addr = rand_addr;
+                memcpy(adv.own_addr, addr, BLE_DEV_ADDR_LEN);
+            }
+        }
+
+        adv.adv_type = bletest_multi_adv_instances[i - 1].adv_type;
+        adv.adv_channel_map = 0x07;
+        adv.adv_filter_policy = BLE_HCI_ADV_FILT_NONE;
+        adv.peer_addr_type = BLE_HCI_ADV_PEER_ADDR_PUBLIC;
+        adv_len = bletest_set_adv_data(&g_host_adv_data[0], addr);
+
+        adv.adv_itvl_min = bletest_multi_adv_instances[i - 1].adv_itvl;
+        adv.adv_itvl_max = bletest_multi_adv_instances[i - 1].adv_itvl;
+        adv.adv_tx_pwr = -1 * i;
+
+        /* Set the advertising parameters */
+        rc = bletest_hci_le_set_multi_adv_params(&adv, i);
+        assert(rc == 0);
+
+        /* Set advertising data */
+        if (adv_len != 0) {
+            rc = bletest_hci_le_set_multi_adv_data(&g_host_adv_data[0], adv_len,
+                                                   i);
+            assert(rc == 0);
+
+            /* Set scan response data */
+            rc = bletest_hci_le_set_multi_scan_rsp_data(&g_host_adv_data[0],
+                                                        adv_len, i);
+            assert(rc == 0);
+        }
+
+        /* Set the advertising parameters */
+        rc = bletest_hci_le_set_multi_adv_enable(1, i);
+        assert(rc == 0);
+    }
+}
+
+void
+bletest_init_advertising(uint8_t instance, int8_t txpwr)
+{
+    int rc;
+    int set_peer_addr;
+    uint8_t adv_len;
+    uint8_t *addr;
+    uint8_t rand_addr[BLE_DEV_ADDR_LEN];
+    struct hci_multi_adv_params adv;
+
+    /* Make sure it is a valid instance */
+    assert(instance < BLE_LL_ADV_INSTANCES);
+
+    /* Just zero out advertising */
+    set_peer_addr = 0;
+    memset(&adv, 0, sizeof(struct hci_multi_adv_params));
+
+    /* If we are using a random address, we need to set it */
+    adv.own_addr_type = BLETEST_CFG_ADV_OWN_ADDR_TYPE;
+    if (adv.own_addr_type & 1) {
+        memcpy(rand_addr, g_dev_addr, BLE_DEV_ADDR_LEN);
+        rand_addr[5] |= 0xc0;
+        if (BLETEST_CFG_MULTI_ADV_RANDOM_OWN == 1) {
+            addr = rand_addr;
+            memcpy(adv.own_addr, addr, BLE_DEV_ADDR_LEN);
+        } else {
+            rc = bletest_hci_le_set_multi_rand_addr(rand_addr, instance);
+            assert(rc == 0);
+            addr = rand_addr;
+        }
+    } else {
+        addr = g_dev_addr;
+    }
+
+    /* Set advertising parameters */
+    adv.adv_type = BLETEST_CFG_ADV_TYPE;
+    adv.adv_channel_map = 0x07;
+    adv.adv_filter_policy = BLETEST_CFG_ADV_FILT_POLICY;
+    if ((adv.adv_filter_policy & 1) || (BLETEST_CFG_ADV_ADDR_RES_EN == 1)) {
+        set_peer_addr = 1;
+    }
+    adv.peer_addr_type = BLETEST_CFG_ADV_PEER_ADDR_TYPE;
+    if ((adv.adv_type == BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD) ||
+        (adv.adv_type == BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD)) {
+        set_peer_addr = 1;
+        adv_len = 0;
+    } else {
+        adv_len = bletest_set_adv_data(&g_host_adv_data[0], addr);
+    }
+
+    /* Not allowed for multi-adv command */
+    if (adv.own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) {
+        assert(0);
+    }
+
+    if (set_peer_addr) {
+        memcpy(adv.peer_addr, g_bletest_cur_peer_addr, BLE_DEV_ADDR_LEN);
+        if (adv.peer_addr_type == BLE_HCI_ADV_PEER_ADDR_RANDOM) {
+            adv.peer_addr[5] |= 0xc0;
+        }
+    }
+
+    console_printf("Trying to connect to %x.%x.%x.%x.%x.%x\n",
+                   adv.peer_addr[0], adv.peer_addr[1], adv.peer_addr[2],
+                   adv.peer_addr[3], adv.peer_addr[4], adv.peer_addr[5]);
+
+    if (adv.adv_type == BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD) {
+        adv.adv_itvl_min = 0;
+        adv.adv_itvl_max = 0;
+    } else {
+        adv.adv_itvl_min = BLETEST_CFG_ADV_ITVL;
+        adv.adv_itvl_max = BLETEST_CFG_ADV_ITVL; /* Advertising interval */
+    }
+
+    adv.adv_tx_pwr = txpwr;
+
+    /* Set the advertising parameters */
+    rc = bletest_hci_le_set_multi_adv_params(&adv, instance);
+    assert(rc == 0);
+
+    /* Set advertising data */
+    if (adv_len != 0) {
+        rc = bletest_hci_le_set_multi_adv_data(&g_host_adv_data[0], adv_len,
+                                               instance);
+        assert(rc == 0);
+
+        /* Set scan response data */
+        rc = bletest_hci_le_set_multi_scan_rsp_data(&g_host_adv_data[0],adv_len,
+                                                    instance);
+        assert(rc == 0);
+    }
+}
+#else
+void
+bletest_init_advertising(void)
+{
+    int rc;
+    int set_peer_addr;
+    uint8_t adv_len;
+    uint8_t *addr;
+    uint8_t rand_addr[BLE_DEV_ADDR_LEN];
+    struct hci_adv_params adv;
+
+    /* Just zero out advertising */
+    set_peer_addr = 0;
+    memset(&adv, 0, sizeof(struct hci_adv_params));
+
+    /* If we are using a random address, we need to set it */
+    adv.own_addr_type = BLETEST_CFG_ADV_OWN_ADDR_TYPE;
+    if (adv.own_addr_type & 1) {
+        memcpy(rand_addr, g_dev_addr, BLE_DEV_ADDR_LEN);
+        rand_addr[5] |= 0xc0;
+        rc = bletest_hci_le_set_rand_addr(rand_addr);
+        assert(rc == 0);
+        addr = rand_addr;
+    } else {
+        addr = g_dev_addr;
+    }
+
+    /* Set advertising parameters */
+    adv.adv_type = BLETEST_CFG_ADV_TYPE;
+    adv.adv_channel_map = 0x07;
+    adv.adv_filter_policy = BLETEST_CFG_ADV_FILT_POLICY;
+    if ((adv.adv_filter_policy & 1) || (BLETEST_CFG_ADV_ADDR_RES_EN == 1)) {
+        set_peer_addr = 1;
+    }
+    adv.peer_addr_type = BLETEST_CFG_ADV_PEER_ADDR_TYPE;
+    if ((adv.adv_type == BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD) ||
+        (adv.adv_type == BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD)) {
+        set_peer_addr = 1;
+        adv_len = 0;
+    } else {
+        adv_len = bletest_set_adv_data(&g_host_adv_data[0], addr);
+    }
+
+    if (adv.own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) {
+        set_peer_addr = 1;
+    }
+
+    if (set_peer_addr) {
+        memcpy(adv.peer_addr, g_bletest_cur_peer_addr, BLE_DEV_ADDR_LEN);
+        if (adv.peer_addr_type == BLE_HCI_ADV_PEER_ADDR_RANDOM) {
+            adv.peer_addr[5] |= 0xc0;
+        }
+    }
+
+    console_printf("Trying to connect to %x.%x.%x.%x.%x.%x\n",
+                   adv.peer_addr[0], adv.peer_addr[1], adv.peer_addr[2],
+                   adv.peer_addr[3], adv.peer_addr[4], adv.peer_addr[5]);
+
+    if (adv.adv_type == BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD) {
+        adv.adv_itvl_min = 0;
+        adv.adv_itvl_max = 0;
+    } else {
+        adv.adv_itvl_min = BLETEST_CFG_ADV_ITVL;
+        adv.adv_itvl_max = BLETEST_CFG_ADV_ITVL; /* Advertising interval */
+    }
+
+    /* Set the advertising parameters */
+    rc = bletest_hci_le_set_adv_params(&adv);
+    assert(rc == 0);
+
+#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
+    if ((adv.own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) ||
+        (BLETEST_CFG_ADV_ADDR_RES_EN == 1)) {
+        rc = bletest_hci_le_add_resolv_list(g_bletest_adv_irk,
+                                            g_bletest_init_irk,
+                                            adv.peer_addr,
+                                            adv.peer_addr_type);
+        assert(rc == 0);
+
+        rc = bletest_hci_le_enable_resolv_list(1);
+        assert(rc == 0);
+    }
+#endif
+
+    /* Set advertising data */
+    if (adv_len != 0) {
+        rc = bletest_hci_le_set_adv_data(&g_host_adv_data[0], adv_len);
+        assert(rc == 0);
+
+        /* Set scan response data */
+        rc = bletest_hci_le_set_scan_rsp_data(&g_host_adv_data[0], adv_len);
+        assert(rc == 0);
+    }
+}
+#endif  /* MULTI_ADV SUPPORT */
+#endif  /* BLETEST_ROLE_ADVERTISER */
+
+#if (BLETEST_CFG_ROLE == BLETEST_ROLE_SCANNER)
+void
+bletest_init_scanner(void)
+{
+    int rc;
+    uint8_t own_addr_type;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_SCAN_PARAM_LEN];
+    uint8_t add_whitelist;
+
+    own_addr_type = BLETEST_CFG_SCAN_OWN_ADDR_TYPE;
+    rc = ble_hs_hci_cmd_build_le_set_scan_params(BLETEST_CFG_SCAN_TYPE,
+                                               BLETEST_CFG_SCAN_ITVL,
+                                               BLETEST_CFG_SCAN_WINDOW,
+                                               own_addr_type,
+                                               BLETEST_CFG_SCAN_FILT_POLICY,
+                                               buf, sizeof buf);
+    assert(rc == 0);
+    rc = ble_hs_hci_cmd_tx_empty_ack(buf);
+    if (rc == 0) {
+        add_whitelist = BLETEST_CFG_SCAN_FILT_POLICY;
+#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
+        if (own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) {
+            rc = bletest_hci_le_add_resolv_list(g_bletest_init_irk,
+                                                g_bletest_adv_irk,
+                                                g_bletest_cur_peer_addr,
+                                                BLETEST_CFG_ADV_PEER_ADDR_TYPE);
+            assert(rc == 0);
+
+            rc = bletest_hci_le_enable_resolv_list(1);
+            assert(rc == 0);
+        }
+#endif
+        if (add_whitelist & 1) {
+            rc = bletest_hci_le_add_to_whitelist(g_bletest_cur_peer_addr,
+                                                 BLE_ADDR_RANDOM);
+            assert(rc == 0);
+        }
+    }
+}
+
+void
+bletest_execute_scanner(void)
+{
+    int rc;
+
+    /* Enable scanning */
+    if ((int32_t)(os_time_get() - g_next_os_time) >= 0) {
+        if (g_bletest_state) {
+            rc = bletest_hci_le_set_scan_enable(0, BLETEST_CFG_FILT_DUP_ADV);
+            assert(rc == 0);
+            g_bletest_state = 0;
+        } else {
+            rc = bletest_hci_le_set_scan_enable(1, BLETEST_CFG_FILT_DUP_ADV);
+            assert(rc == 0);
+            g_bletest_state = 1;
+        }
+        g_next_os_time += (OS_TICKS_PER_SEC * 60);
+    }
+}
+#endif
+
+#if (BLETEST_CFG_ROLE == BLETEST_ROLE_INITIATOR)
+void
+bletest_init_initiator(void)
+{
+    int rc;
+    uint8_t rand_addr[BLE_DEV_ADDR_LEN];
+    struct hci_create_conn *hcc;
+
+    /* Enable initiating */
+    hcc = &g_cc;
+    hcc->conn_itvl_max = BLETEST_CFG_CONN_ITVL;
+    hcc->conn_itvl_min = BLETEST_CFG_CONN_ITVL;
+    hcc->conn_latency = BLETEST_CFG_SLAVE_LATENCY;
+    hcc->filter_policy = BLETEST_CFG_INIT_FILTER_POLICY;
+    hcc->supervision_timeout = BLETEST_CFG_CONN_SPVN_TMO;
+    hcc->scan_itvl = BLETEST_CFG_SCAN_ITVL;
+    hcc->scan_window = BLETEST_CFG_SCAN_WINDOW;
+    hcc->peer_addr_type = BLETEST_CFG_CONN_PEER_ADDR_TYPE;
+    memcpy(hcc->peer_addr, g_bletest_cur_peer_addr, BLE_DEV_ADDR_LEN);
+    if (hcc->peer_addr_type == BLE_HCI_CONN_PEER_ADDR_RANDOM) {
+        hcc->peer_addr[5] |= 0xc0;
+    }
+    hcc->own_addr_type = BLETEST_CFG_CONN_OWN_ADDR_TYPE;
+    hcc->min_ce_len = BLETEST_CFG_MIN_CE_LEN;
+    hcc->max_ce_len = BLETEST_CFG_MAX_CE_LEN;
+
+    console_printf("Trying to connect to %x.%x.%x.%x.%x.%x\n",
+                   hcc->peer_addr[0], hcc->peer_addr[1], hcc->peer_addr[2],
+                   hcc->peer_addr[3], hcc->peer_addr[4], hcc->peer_addr[5]);
+
+    /* If we are using a random address, we need to set it */
+    if (hcc->own_addr_type == BLE_HCI_ADV_OWN_ADDR_RANDOM) {
+        memcpy(rand_addr, g_dev_addr, BLE_DEV_ADDR_LEN);
+        rand_addr[5] |= 0xc0;
+        rc = bletest_hci_le_set_rand_addr(rand_addr);
+        assert(rc == 0);
+    }
+
+#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
+        if ((hcc->peer_addr_type > BLE_HCI_CONN_PEER_ADDR_RANDOM) ||
+            (hcc->own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM)) {
+            rc = bletest_hci_le_add_resolv_list(g_bletest_init_irk,
+                                                g_bletest_adv_irk,
+                                                g_bletest_cur_peer_addr,
+                                                BLETEST_CFG_ADV_PEER_ADDR_TYPE);
+            assert(rc == 0);
+
+            rc = bletest_hci_le_enable_resolv_list(1);
+            assert(rc == 0);
+        }
+#endif
+
+    bletest_hci_le_create_connection(hcc);
+}
+
+void
+bletest_execute_initiator(void)
+{
+    int i;
+    int rc;
+    int8_t rssi;
+    uint16_t handle;
+    uint8_t new_chan_map[5];
+
+    /*
+     * Determine if there is an active connection for the current handle
+     * we are trying to create. If so, start looking for the next one
+     */
+    if (g_bletest_current_conns < BLETEST_CFG_CONCURRENT_CONNS) {
+        handle = g_bletest_current_conns + 1;
+        if (ble_ll_conn_find_active_conn(handle)) {
+            /* Set LED to slower blink rate */
+            g_bletest_led_rate = OS_TICKS_PER_SEC;
+
+            /* Ask for version information */
+            rc = bletest_hci_rd_rem_version(handle);
+
+            /* Ask for remote used features */
+            rc = bletest_hci_le_read_rem_used_feat(handle);
+
+            /* Scanning better be stopped! */
+            assert(ble_ll_scan_enabled() == 0);
+
+            /* Add to current connections */
+            if (!rc) {
+                ++g_bletest_current_conns;
+
+                /* Move to next connection */
+                if (g_bletest_current_conns < BLETEST_CFG_CONCURRENT_CONNS) {
+                    /* restart initiating */
+                    g_bletest_cur_peer_addr[5] += 1;
+                    g_dev_addr[5] += 1;
+                    bletest_init_initiator();
+                }
+            }
+        } else {
+            if (ble_ll_scan_enabled() == 0) {
+                bletest_hci_le_create_connection(&g_cc);
+            }
+        }
+    } else {
+        if ((int32_t)(os_time_get() - g_next_os_time) >= 0) {
+            if ((g_bletest_state == 1) || (g_bletest_state == 3)) {
+                for (i = 0; i < g_bletest_current_conns; ++i) {
+                    if (ble_ll_conn_find_active_conn(i + 1)) {
+                        bletest_hci_le_rd_chanmap(i+1);
+                    }
+                }
+            } else if (g_bletest_state == 2) {
+                new_chan_map[0] = 0;
+                new_chan_map[1] = 0x3;
+                new_chan_map[2] = 0;
+                new_chan_map[3] = 0x1F;
+                new_chan_map[4] = 0;
+                bletest_hci_le_set_host_chan_class(new_chan_map);
+            } else if (g_bletest_state == 4) {
+#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1)
+                struct hci_start_encrypt hsle;
+                for (i = 0; i < g_bletest_current_conns; ++i) {
+                    if (ble_ll_conn_find_active_conn(i + 1)) {
+                        hsle.connection_handle = i + 1;
+                        hsle.encrypted_diversifier = g_bletest_EDIV;
+                        hsle.random_number = g_bletest_RAND;
+                        swap_buf(hsle.long_term_key, (uint8_t *)g_bletest_LTK,
+                                 16);
+                        bletest_hci_le_start_encrypt(&hsle);
+                    }
+                }
+#endif
+            } else if (g_bletest_state == 8) {
+#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1)
+                struct hci_start_encrypt hsle;
+                for (i = 0; i < g_bletest_current_conns; ++i) {
+                    if (ble_ll_conn_find_active_conn(i + 1)) {
+                        hsle.connection_handle = i + 1;
+                        hsle.encrypted_diversifier = g_bletest_EDIV;
+                        hsle.random_number = ~g_bletest_RAND;
+                        swap_buf(hsle.long_term_key, (uint8_t *)g_bletest_LTK,
+                                 16);
+                        bletest_hci_le_start_encrypt(&hsle);
+                    }
+                }
+#endif
+            } else {
+                for (i = 0; i < g_bletest_current_conns; ++i) {
+                    if (ble_ll_conn_find_active_conn(i + 1)) {
+                        ble_hs_hci_util_read_rssi(i+1, &rssi);
+                    }
+                }
+            }
+
+            ++g_bletest_state;
+            if (g_bletest_state > 9) {
+                g_bletest_state = 9;
+            }
+            g_next_os_time = os_time_get() + OS_TICKS_PER_SEC * 3;
+        }
+    }
+}
+#endif
+
+#if (BLETEST_CFG_ROLE == BLETEST_ROLE_ADVERTISER)
+/*
+ * Test wrapper to get packets. Only get a packet if we have more than half
+ * left
+ */
+static struct os_mbuf *
+bletest_get_packet(void)
+{
+    struct os_mbuf *om;
+
+    om = NULL;
+    if (os_msys_num_free() >= 5) {
+        om = os_msys_get_pkthdr(0, sizeof(struct ble_mbuf_hdr));
+    }
+    return om;
+}
+
+static struct os_mbuf *
+bletest_send_packet(uint16_t handle)
+{
+    int j;
+    uint8_t val;
+    struct os_mbuf *om;
+    uint16_t pktlen;
+
+    om = bletest_get_packet();
+    if (om) {
+        /* set payload length */
+#if BLETEST_THROUGHPUT_TEST
+        pktlen = BLETEST_PKT_SIZE;
+#else
+#if (BLETEST_CFG_RAND_PKT_SIZE == 1)
+        pktlen = rand() % (BLETEST_MAX_PKT_SIZE + 1);
+#else
+        pktlen = BLETEST_PKT_SIZE;
+#endif
+#endif
+
+        /* Put the HCI header in the mbuf */
+        put_le16(om->om_data, handle);
+        put_le16(om->om_data + 2, pktlen + 4);
+
+        /* Place L2CAP header in packet */
+        put_le16(om->om_data + 4, pktlen);
+        om->om_data[6] = 0;
+        om->om_data[7] = 0;
+        om->om_len = 8;
+        OS_MBUF_PKTHDR(om)->omp_len = 8;
+
+        /* Fill with incrementing pattern (starting from 1) */
+        for (j = 0; j < pktlen; ++j) {
+            val = j + 1;
+            os_mbuf_append(om, &val, 1);
+        }
+
+        /* Transmit it */
+        ble_hci_trans_hs_acl_tx(om);
+    }
+
+    return om;
+}
+
+static void
+bletest_execute_advertiser(void)
+{
+    int i;
+#if (BLETEST_CONCURRENT_CONN_TEST == 1)
+#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1)
+    uint16_t mask;
+    uint16_t reply_handle;
+#endif
+#endif
+    int rc;
+    uint16_t handle;
+    struct os_mbuf *om;
+#if (BLETEST_THROUGHPUT_TEST == 1)
+    os_sr_t sr;
+    uint16_t completed_pkts;
+#endif
+
+    /* See if we should start advertising again */
+    if (g_bletest_current_conns < BLETEST_CFG_CONCURRENT_CONNS) {
+        handle = g_bletest_current_conns + 1;
+        if (ble_ll_conn_find_active_conn(handle)) {
+            /* Set LED to slower blink rate */
+            g_bletest_led_rate = OS_TICKS_PER_SEC;
+
+#if (BLETEST_THROUGHPUT_TEST == 1)
+            /* Set next os time to 10 seconds after 1st connection */
+            if (g_next_os_time == 0) {
+                g_next_os_time = os_time_get() + (10 * OS_TICKS_PER_SEC);
+                g_bletest_handle = handle;
+            }
+#endif
+
+            /* advertising better be stopped! */
+            assert(ble_ll_adv_enabled() == 0);
+
+            /* Send the remote used features command */
+            rc = bletest_hci_le_read_rem_used_feat(handle);
+            if (rc) {
+                return;
+            }
+
+            /* Send the remote read version command */
+            rc = bletest_hci_rd_rem_version(handle);
+            if (rc) {
+                return;
+            }
+
+            /* set conn update time */
+            g_bletest_conn_upd_time = os_time_get() + (OS_TICKS_PER_SEC * 5);
+            g_bletest_start_update = 1;
+
+            /* Add to current connections */
+            ++g_bletest_current_conns;
+
+            /* Move to next connection */
+            if (g_bletest_current_conns < BLETEST_CFG_CONCURRENT_CONNS) {
+                /* restart initiating */
+                g_bletest_cur_peer_addr[5] += 1;
+                g_dev_addr[5] += 1;
+#if MYNEWT_VAL(BLE_ANDROID_MULTI_ADV_SUPPORT)
+                bletest_init_advertising(0,0);
+                bletest_hci_le_set_multi_adv_enable(1, 0);
+#else
+                bletest_init_advertising();
+                bletest_hci_le_set_adv_enable(1);
+#endif
+            }
+        } else {
+            /* If we failed to start advertising we should keep trying */
+            if (ble_ll_adv_enabled() == 0) {
+#if MYNEWT_VAL(BLE_ANDROID_MULTI_ADV_SUPPORT)
+                bletest_hci_le_set_multi_adv_enable(1, 0);
+#else
+                bletest_hci_le_set_adv_enable(1);
+#endif
+            }
+        }
+    }
+#if 0
+    if (g_bletest_start_update) {
+        if ((int32_t)(os_time_get() - g_bletest_conn_upd_time) >= 0) {
+            bletest_send_conn_update(1);
+            g_bletest_start_update = 0;
+        }
+    }
+#endif
+
+#if (BLETEST_CONCURRENT_CONN_TEST == 1)
+    /* See if it is time to hand a data packet to the connection */
+    if ((int32_t)(os_time_get() - g_next_os_time) >= 0) {
+#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1)
+        /* Do we need to send a LTK reply? */
+        mask = 1;
+        reply_handle = 1;
+        while (g_bletest_ltk_reply_handle && mask) {
+            if (g_bletest_ltk_reply_handle & mask) {
+                bletest_send_ltk_req_reply(reply_handle);
+                //bletest_send_ltk_req_neg_reply(reply_handle);
+                g_bletest_ltk_reply_handle &= ~mask;
+            }
+            ++reply_handle;
+            mask <<= 1;
+        }
+#endif
+        if (g_bletest_current_conns) {
+            for (i = 0; i < g_bletest_current_conns; ++i) {
+                if ((g_last_handle_used == 0) ||
+                    (g_last_handle_used > g_bletest_current_conns)) {
+                    g_last_handle_used = 1;
+                }
+                handle = g_last_handle_used;
+                if (ble_ll_conn_find_active_conn(handle)) {
+                    om = bletest_send_packet(handle);
+                    if (om) {
+                        /* Increment last handle used */
+                        ++g_last_handle_used;
+                    }
+                } else {
+                    ++g_last_handle_used;
+                }
+            }
+        }
+        g_next_os_time = os_time_get() + OS_TICKS_PER_SEC;
+    }
+#endif
+
+#if (BLETEST_THROUGHPUT_TEST == 1)
+    /* Nothing to do if no connections */
+    if (!g_bletest_current_conns) {
+        return;
+    }
+
+    /* See if it is time to start throughput testing */
+    if ((int32_t)(os_time_get() - g_next_os_time) >= 0) {
+        /* Keep window full */
+        OS_ENTER_CRITICAL(sr);
+        completed_pkts = g_bletest_completed_pkts;
+        g_bletest_completed_pkts = 0;
+        OS_EXIT_CRITICAL(sr);
+
+        assert(g_bletest_outstanding_pkts >= completed_pkts);
+        g_bletest_outstanding_pkts -= completed_pkts;
+
+        while (g_bletest_outstanding_pkts < 20) {
+            om = bletest_send_packet(g_bletest_handle);
+            if (om) {
+                ++g_bletest_outstanding_pkts;
+            }
+        }
+    }
+#endif /* XXX: throughput test */
+}
+#endif /* XXX: BLETEST_ROLE_ADVERTISER */
+
+/**
+ * Main bletest function. Called by the task timer every 50 msecs.
+ *
+ */
+void
+bletest_execute(void)
+{
+    /* Toggle LED at set rate */
+    if ((int32_t)(os_time_get() - g_bletest_next_led_time) >= 0) {
+        hal_gpio_toggle(LED_BLINK_PIN);
+        g_bletest_next_led_time = os_time_get() + g_bletest_led_rate;
+    }
+#if (BLETEST_CFG_ROLE == BLETEST_ROLE_ADVERTISER)
+    bletest_execute_advertiser();
+#endif
+#if (BLETEST_CFG_ROLE == BLETEST_ROLE_SCANNER)
+    bletest_execute_scanner();
+#endif
+#if (BLETEST_CFG_ROLE == BLETEST_ROLE_INITIATOR)
+    bletest_execute_initiator();
+#endif
+}
+
+/**
+ * Callback when BLE test timer expires.
+ *
+ * @param arg
+ */
+void
+bletest_timer_cb(struct os_event *ev)
+{
+    /* Call the bletest code */
+    bletest_execute();
+
+    /* Re-start the timer (run every 10 msecs) */
+    os_callout_reset(&g_bletest_timer, OS_TICKS_PER_SEC / 100);
+}
+
+/**
+ * BLE test task
+ *
+ * @param arg
+ */
+void
+bletest_task_handler(void *arg)
+{
+    int rc;
+    uint64_t rand64;
+    uint64_t event_mask;
+
+    /* Set LED blink rate */
+    g_bletest_led_rate = OS_TICKS_PER_SEC / 20;
+
+    /* Wait one second before starting test task */
+    os_time_delay(OS_TICKS_PER_SEC);
+
+    /* Initialize the host timer */
+    os_callout_init(&g_bletest_timer, &g_bletest_evq, bletest_timer_cb,
+                    NULL);
+
+    ble_hs_dbg_set_sync_state(BLE_HS_SYNC_STATE_GOOD);
+
+    /* Send the reset command first */
+    rc = bletest_hci_reset_ctlr();
+    assert(rc == 0);
+
+#if (BLETEST_CFG_ROLE == BLETEST_ROLE_ADVERTISER)
+    /* Initialize the advertiser */
+    console_printf("Starting BLE test task as advertiser\n");
+#if MYNEWT_VAL(BLE_ANDROID_MULTI_ADV_SUPPORT)
+    /* Start up all advertising instances except default one */
+    bletest_init_adv_instances();
+
+    /* Start advertising on instance 0 at 0 dbm */
+    bletest_init_advertising(0, 0);
+#else
+    bletest_init_advertising();
+#endif
+#endif
+
+#if (BLETEST_CFG_ROLE == BLETEST_ROLE_SCANNER)
+    /* Initialize the scanner */
+    console_printf("Starting BLE test task as scanner\n");
+    bletest_init_scanner();
+#endif
+
+#if (BLETEST_CFG_ROLE == BLETEST_ROLE_INITIATOR)
+    /* Initialize the scanner */
+    console_printf("Starting BLE test task as initiator\n");
+    bletest_init_initiator();
+#endif
+
+    /* Read unique HW id */
+    rc = hal_bsp_hw_id((void *)&g_bletest_hw_id[0], sizeof(g_bletest_hw_id));
+    assert(rc == 16);
+    console_printf("HW id=%04x%04x%04x%04x\n",
+                   (unsigned int)g_bletest_hw_id[0],
+                   (unsigned int)g_bletest_hw_id[1],
+                   (unsigned int)g_bletest_hw_id[2],
+                   (unsigned int)g_bletest_hw_id[3]);
+
+    /* Set the event mask we want to display */
+    event_mask = 0x7FF;
+    rc = bletest_hci_le_set_event_mask(event_mask);
+    assert(rc == 0);
+
+    /* Turn on all events */
+    event_mask = 0xffffffffffffffff;
+    rc = bletest_hci_set_event_mask(event_mask);
+    assert(rc == 0);
+
+    /* Read device address */
+    rc = bletest_hci_rd_bd_addr();
+    assert(rc == 0);
+
+    /* Read local features */
+    rc = bletest_hci_rd_local_feat();
+    assert(rc == 0);
+
+    /* Read local commands */
+    rc = bletest_hci_rd_local_supp_cmd();
+    assert(rc == 0);
+
+    /* Read version */
+    rc = bletest_hci_rd_local_version();
+    assert(rc == 0);
+
+    /* Read supported states */
+    rc = bletest_hci_le_read_supp_states();
+    assert(rc == 0);
+
+    /* Read maximum data length */
+    rc = bletest_hci_le_rd_max_datalen();
+    assert(rc == 0);
+
+#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_DATA_LEN_EXT) == 1)
+    /* Read suggested data length */
+    rc = bletest_hci_le_rd_sugg_datalen();
+    assert(rc == 0);
+
+    /* write suggested default data length */
+    rc = bletest_hci_le_write_sugg_datalen(BLETEST_CFG_SUGG_DEF_TXOCTETS,
+                                           BLETEST_CFG_SUGG_DEF_TXTIME);
+    assert(rc == 0);
+
+    /* Read suggested data length */
+    rc = bletest_hci_le_rd_sugg_datalen();
+    assert(rc == 0);
+
+    /* Set data length (note: we know there is no connection; just a test) */
+    rc = bletest_hci_le_set_datalen(0x1234, BLETEST_CFG_SUGG_DEF_TXOCTETS,
+                                    BLETEST_CFG_SUGG_DEF_TXTIME);
+    assert(rc != 0);
+#endif
+
+    /* Encrypt a block */
+#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1)
+    rc = bletest_hci_le_encrypt((uint8_t *)g_ble_ll_encrypt_test_key,
+                                (uint8_t *)g_ble_ll_encrypt_test_plain_text);
+    assert(rc == 0);
+#endif
+
+    /* Get a random number */
+    rc = ble_hs_hci_util_rand(&rand64, 8);
+    assert(rc == 0);
+
+    /* Wait some time before starting */
+    os_time_delay(OS_TICKS_PER_SEC);
+
+    /* Init state */
+    g_bletest_state = 0;
+
+    /* Begin advertising if we are an advertiser */
+#if (BLETEST_CFG_ROLE == BLETEST_ROLE_ADVERTISER)
+#if MYNEWT_VAL(BLE_ANDROID_MULTI_ADV_SUPPORT)
+    rc = bletest_hci_le_set_multi_adv_enable(1, 0);
+    assert(rc == 0);
+#else
+    rc = bletest_hci_le_set_adv_enable(1);
+    assert(rc == 0);
+#endif
+#endif
+
+    bletest_timer_cb(NULL);
+
+    while (1) {
+        os_eventq_run(&g_bletest_evq);
+    }
+}
+
+/**
+ * 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();
+
+    /* Dummy device address */
+#if BLETEST_CFG_ROLE == BLETEST_ROLE_ADVERTISER
+    g_dev_addr[0] = 0x00;
+    g_dev_addr[1] = 0x00;
+    g_dev_addr[2] = 0x00;
+    g_dev_addr[3] = 0x88;
+    g_dev_addr[4] = 0x88;
+    g_dev_addr[5] = 0x08;
+
+    g_bletest_cur_peer_addr[0] = 0x00;
+    g_bletest_cur_peer_addr[1] = 0x00;
+    g_bletest_cur_peer_addr[2] = 0x00;
+    g_bletest_cur_peer_addr[3] = 0x99;
+    g_bletest_cur_peer_addr[4] = 0x99;
+    g_bletest_cur_peer_addr[5] = 0x09;
+#else
+    g_dev_addr[0] = 0x00;
+    g_dev_addr[1] = 0x00;
+    g_dev_addr[2] = 0x00;
+    g_dev_addr[3] = 0x99;
+    g_dev_addr[4] = 0x99;
+    g_dev_addr[5] = 0x09;
+
+    g_bletest_cur_peer_addr[0] = 0x00;
+    g_bletest_cur_peer_addr[1] = 0x00;
+    g_bletest_cur_peer_addr[2] = 0x00;
+    g_bletest_cur_peer_addr[3] = 0x88;
+    g_bletest_cur_peer_addr[4] = 0x88;
+    g_bletest_cur_peer_addr[5] = 0x08;
+#endif
+
+    /* Set the led pin as an output */
+    g_led_pin = LED_BLINK_PIN;
+    hal_gpio_init_out(g_led_pin, 1);
+
+    /* Initialize eventq for bletest task */
+    os_eventq_init(&g_bletest_evq);
+
+    rc = os_task_init(&bletest_task, "bletest", bletest_task_handler, NULL,
+                      BLETEST_TASK_PRIO, OS_WAIT_FOREVER, bletest_stack,
+                      BLETEST_STACK_SIZE);
+    assert(rc == 0);
+
+    while (1) {
+        os_eventq_run(os_eventq_dflt_get());
+    }
+    /* Never returns */
+
+    /* os start should never return. If it does, this should be an error */
+    assert(0);
+
+    return rc;
+}

+ 33 - 0
apps/bletest/syscfg.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.
+#
+
+syscfg.defs:
+    BLETEST_ROLE:
+        description: >
+            The role of the bletest code. Should be set to:
+                1: Advertiser
+                2: Scanner
+                3: Initiator
+        value: 1
+
+syscfg.vals:
+    MSYS_1_BLOCK_COUNT: 16
+    BLE_SM_LEGACY: 0
+
+    # Default task settings
+    OS_MAIN_STACK_SIZE: 256

+ 39 - 0
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 <dev@mynewt.apache.org>"
+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

+ 201 - 0
apps/btshell/src/btshell.h

@@ -0,0 +1,201 @@
+/*
+ * 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 <inttypes.h>
+#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;
+};
+
+SLIST_HEAD(btshell_svc_list, btshell_svc);
+
+struct btshell_l2cap_coc {
+    SLIST_ENTRY(btshell_l2cap_coc) next;
+    struct ble_l2cap_chan *chan;
+};
+
+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;
+};
+
+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_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 *ltk, uint16_t ediv,
+                        uint64_t rand_val, int auth);
+int btshell_tx_start(uint16_t handle, uint16_t len, uint16_t rate,
+                     uint16_t num);
+int btshell_rssi(uint16_t conn_handle, int8_t *out_rssi);
+int btshell_l2cap_create_srv(uint16_t psm, int accept_response);
+int btshell_l2cap_connect(uint16_t conn, uint16_t psm);
+int btshell_l2cap_disconnect(uint16_t conn, uint16_t idx);
+int btshell_l2cap_send(uint16_t conn, uint16_t idx, uint16_t bytes);
+
+/** 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

+ 3934 - 0
apps/btshell/src/cmd.c

@@ -0,0 +1,3934 @@
+/*
+ * 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 <assert.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <string.h>
+#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 }
+};
+
+
+
+/*****************************************************************************
+ * $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 bool adv_instances[BLE_ADV_INSTANCES];
+
+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;
+    }
+
+    if (adv_instances[instance]) {
+        console_printf("instance already configured\n");
+        return rc;
+    }
+
+    memset(&params, 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_arg_mac("peer_addr", params.peer.val);
+    if (rc == 0) {
+        params.directed = 1;
+
+        params.peer.type = parse_arg_kv_dflt("peer_addr_type",
+                                             cmd_peer_addr_types,
+                                             BLE_ADDR_PUBLIC, &rc);
+        if (rc != 0) {
+            console_printf("invalid 'peer_addr_type' parameter\n");
+            return rc;
+        }
+    }
+    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_uint32_dflt("interval_min", 0, &rc);
+    if (rc != 0) {
+        console_printf("invalid 'interval_min' parameter\n");
+        return rc;
+    }
+
+    params.itvl_max = parse_arg_uint32_dflt("interval_max", 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, &params, &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);
+
+    adv_instances[instance] = true;
+
+    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;
+    }
+
+    if (!adv_instances[instance]) {
+        console_printf("instance not configured\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;
+    }
+
+    if (!adv_instances[instance]) {
+        console_printf("instance not configured\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;
+    }
+
+    if (!adv_instances[instance]) {
+        console_printf("instance not configured\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;
+    }
+
+    if (!adv_instances[instance]) {
+        console_printf("instance not configured\n");
+        return rc;
+    }
+
+    rc = ble_gap_ext_adv_remove(instance);
+    if (rc) {
+        console_printf("failed to remove advertising instance\n");
+        return rc;
+    }
+
+    adv_instances[instance] = false;
+
+    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;
+    }
+
+    peer_addr.type = parse_arg_kv_dflt("peer_addr_type", cmd_peer_addr_types,
+                                       BLE_ADDR_PUBLIC, &rc);
+    if (rc != 0) {
+        console_printf("invalid 'peer_addr_type' parameter\n");
+        return rc;
+    }
+
+    rc = parse_arg_mac("peer_addr", peer_addr.val);
+    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_uint16_dflt("interval_min", 0, &rc);
+    if (rc != 0) {
+        console_printf("invalid 'interval_min' parameter\n");
+        return rc;
+    }
+
+    params.itvl_max = parse_arg_uint16_dflt("interval_max", 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,
+                           &params, 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;
+    }
+
+    peer_addr.type = parse_arg_kv_dflt("peer_addr_type", cmd_peer_addr_types,
+                                       BLE_ADDR_PUBLIC, &rc);
+    if (rc != 0) {
+        console_printf("invalid 'peer_addr_type' parameter\n");
+        return rc;
+    }
+
+    rc = parse_arg_mac("peer_addr", peer_addr.val);
+    if (rc == ENOENT) {
+        /* Allow "addr" for backwards compatibility. */
+        rc = parse_arg_mac("addr", peer_addr.val);
+    }
+
+    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_uint16_dflt("scan_interval", 0x0010, &rc);
+    if (rc != 0) {
+        console_printf("invalid 'scan_interval' parameter\n");
+        return rc;
+    }
+
+    phy_1M_params.scan_window = parse_arg_uint16_dflt("scan_window", 0x0010, &rc);
+    if (rc != 0) {
+        console_printf("invalid 'scan_window' parameter\n");
+        return rc;
+    }
+
+    phy_1M_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;
+    }
+
+    phy_1M_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;
+    }
+
+    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_uint16_dflt("timeout", 0x0100, &rc);
+    if (rc != 0) {
+        console_printf("invalid 'timeout' parameter\n");
+        return rc;
+    }
+
+    phy_1M_params.min_ce_len = parse_arg_uint16_dflt("min_conn_event_len",
+                                                     0x0010, &rc);
+    if (rc != 0) {
+        console_printf("invalid 'min_conn_event_len' parameter\n");
+        return rc;
+    }
+
+    phy_1M_params.max_ce_len = parse_arg_uint16_dflt("max_conn_event_len",
+                                                     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);
+        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);
+        console_printf("error connecting; rc=%d\n", rc);
+        return rc;
+    }
+
+    /* Get coded params */
+    phy_coded_params.scan_itvl = parse_arg_uint16_dflt("coded_scan_interval",
+                                                       0x0010, &rc);
+    if (rc != 0) {
+        console_printf("invalid 'coded_scan_interval' parameter\n");
+        return rc;
+    }
+
+    phy_coded_params.scan_window = parse_arg_uint16_dflt("coded_scan_window",
+                                                         0x0010, &rc);
+    if (rc != 0) {
+        console_printf("invalid 'coded_scan_window' parameter\n");
+        return rc;
+    }
+
+    phy_coded_params.itvl_min = parse_arg_uint16_dflt("coded_interval_min",
+                                                      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_uint16_dflt("coded_interval_max",
+                                                      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_uint16_dflt("coded_timeout", 0x0100, &rc);
+
+    if (rc != 0) {
+        console_printf("invalid 'coded_timeout' parameter\n");
+        return rc;
+    }
+
+    phy_coded_params.min_ce_len =
+        parse_arg_uint16_dflt("coded_min_conn_event", 0x0010, &rc);
+    if (rc != 0) {
+        console_printf("invalid 'coded_min_conn_event' parameter\n");
+        return rc;
+    }
+
+    phy_coded_params.max_ce_len = parse_arg_uint16_dflt("coded_max_conn_event",
+                                                        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_uint16_dflt("2M_interval_min",
+                                                   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_uint16_dflt("2M_interval_max",
+                                                   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_uint16_dflt("2M_timeout",
+                                                              0x0100, &rc);
+
+    if (rc != 0) {
+        console_printf("invalid '2M_timeout' parameter\n");
+        return rc;
+    }
+
+    phy_2M_params.min_ce_len = parse_arg_uint16_dflt("2M_min_conn_event", 0x0010,
+                                                     &rc);
+    if (rc != 0) {
+        console_printf("invalid '2M_min_conn_event' parameter\n");
+        return rc;
+    }
+
+    phy_2M_params.max_ce_len = parse_arg_uint16_dflt("2M_max_conn_event",
+                                                     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: =<UINT16>"},
+    {"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;
+    }
+
+    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"},
+    {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_long_bounds_dflt("duration", 1, INT32_MAX,
+                                             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_uint16_dflt("interval", 0, &rc);
+    if (rc != 0) {
+        console_printf("invalid 'interval' parameter\n");
+        return rc;
+    }
+
+    params.window = parse_arg_uint16_dflt("window", 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, &params, &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_uint16_dflt("extended_duration", 0, &rc);
+    if (rc != 0) {
+        console_printf("invalid 'extended_duration' parameter\n");
+        return rc;
+    }
+
+    period = parse_arg_uint16_dflt("extended_period", 0, &rc);
+    if (rc != 0) {
+        console_printf("invalid 'extended_period' parameter\n");
+        return rc;
+    }
+
+    coded.itvl = parse_arg_uint16_dflt("longrange_interval", 0, &rc);
+    if (rc != 0) {
+        console_printf("invalid 'longrange_interval' parameter\n");
+        return rc;
+    }
+
+    coded.window = parse_arg_uint16_dflt("longrange_window", 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:
+        rc = -1;
+        console_printf("invalid 'extended' parameter\n");
+        break;
+    }
+
+    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 const struct kv_pair cmd_set_addr_types[] = {
+    { "public",         BLE_ADDR_PUBLIC },
+    { "random",         BLE_ADDR_RANDOM },
+    { NULL }
+};
+
+static int
+cmd_set_addr(void)
+{
+    uint8_t addr[6];
+    int addr_type;
+    int rc;
+
+    addr_type = parse_arg_kv_dflt("addr_type", cmd_set_addr_types,
+                                  BLE_ADDR_PUBLIC, &rc);
+    if (rc != 0) {
+        console_printf("invalid 'addr_type' parameter\n");
+        return rc;
+    }
+
+    rc = parse_arg_mac("addr", addr);
+    if (rc != 0) {
+        console_printf("invalid 'addr' parameter\n");
+        return rc;
+    }
+
+    switch (addr_type) {
+#if MYNEWT_VAL(BLE_DEVICE)
+    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, 6);
+        ble_hs_id_set_pub(g_dev_addr);
+        break;
+#endif
+
+    case BLE_ADDR_RANDOM:
+        rc = ble_hs_id_set_rnd(addr);
+        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)
+{
+    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
+
+    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;
+    }
+
+    if (!adv_instances[instance]) {
+        console_printf("instance not configured\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) {
+            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);
+
+                os_mbuf_append(adv_data, extra_data,
+                               min(extra_data_len - counter, 10));
+
+                counter += 10;
+            }
+        }
+
+        if (scan_rsp) {
+            rc = ble_gap_ext_adv_rsp_set_data(instance, adv_data);
+        } 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);
+}
+
+static int
+cmd_set_scan_rsp(int argc, char **argv)
+{
+    return cmd_set_adv_data_or_scan_rsp(argc, argv, true);
+}
+
+#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;
+    }
+
+    addr.type = parse_arg_kv_dflt("addr_type", cmd_set_addr_types,
+                                  BLE_ADDR_PUBLIC, &rc);
+    if (rc != 0) {
+        console_printf("invalid 'addr_type' parameter\n");
+        return rc;
+    }
+
+    rc = parse_arg_mac("addr", addr.val);
+    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_arg_mac("addr", addrs[addrs_cnt].val);
+        if (rc == ENOENT) {
+            break;
+        } else if (rc != 0) {
+            console_printf("invalid 'addr' parameter\n");
+            return rc;
+        }
+
+        addrs[addrs_cnt].type = parse_arg_kv("addr_type", cmd_addr_type, &rc);
+        if (rc != 0) {
+            console_printf("invalid 'addr' parameter\n");
+            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: =<UINT16>"},
+    {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_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.latency = parse_arg_uint16_dflt("latency", 0, &rc);
+    if (rc != 0) {
+        console_printf("invalid 'latency' parameter\n");
+        return rc;
+    }
+
+    params.supervision_timeout = parse_arg_uint16_dflt("timeout", 0x0100, &rc);
+    if (rc != 0) {
+        console_printf("invalid 'timeout' parameter\n");
+        return rc;
+    }
+
+    params.min_ce_len = parse_arg_uint16_dflt("min_conn_event_len",
+                                              0x0010, &rc);
+    if (rc != 0) {
+        console_printf("invalid 'min_conn_event_len' parameter\n");
+        return rc;
+    }
+
+    params.max_ce_len = parse_arg_uint16_dflt("max_conn_event_len",
+                                              0x0300, &rc);
+    if (rc != 0) {
+        console_printf("invalid 'max_conn_event_len' parameter\n");
+        return rc;
+    }
+
+    rc = btshell_update_conn(conn_handle, &params);
+    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: =<UINT16>"},
+    {"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", "conn_datalen handle, usage: =<UINT16>"},
+    {"octets", "usage: =<UINT16>"},
+    {"time", "usage: =<UINT16>"},
+    {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:
+        out->sec.peer_addr.type = parse_arg_kv("addr_type",
+                                               cmd_addr_type, &rc);
+        if (rc != 0) {
+            console_printf("invalid 'addr_type' parameter\n");
+            return rc;
+        }
+
+        rc = parse_arg_mac("addr", out->sec.peer_addr.val);
+        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: =<msec|ssec|cccd>"},
+    {"addr_type", "usage: =<public|random>"},
+    {"addr", "usage: =<XX:XX:XX:XX:XX:XX>"},
+    {"ediv", "usage: =<UINT16>"},
+    {"rand", "usage: =<UINT64>"},
+    {"ltk", "usage: =<XX:XX:...>, len=16 octets"},
+    {"irk", "usage: =<XX:XX:...>, len=16 octets"},
+    {"csrk", "usage: =<XX:XX:...>, 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: =<msec|ssec|cccd>"},
+    {"addr_type", "usage: =<public|random>"},
+    {"addr", "usage: =<XX:XX:XX:XX:XX:XX>"},
+    {"ediv", "usage: =<UINT16>"},
+    {"rand", "usage: =<UINT64>"},
+    {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;
+    }
+    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: =<msec|ssec|cccd>"},
+    {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
+/*****************************************************************************
+ * $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;
+
+       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: =<UINT16>"},
+    {"action", "auth action type, usage: =<UINT16>"},
+    {"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: =<UINT16>"},
+    {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;
+
+    rc = parse_arg_all(argc - 1, argv + 1);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = parse_arg_mac("peer_addr", peer.val);
+    if (rc == 0) {
+
+        peer.type = parse_arg_kv_dflt("peer_addr_type",
+                                      cmd_peer_addr_types,
+                                      BLE_ADDR_PUBLIC, &rc);
+        if (rc != 0) {
+            console_printf("invalid 'peer_addr_type' parameter\n");
+            return rc;
+        }
+    } else {
+        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[] = {
+    {"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: =<UINT16>"},
+    {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];
+    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, 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;
+        }
+
+        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, 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: =<UINT16>"},
+    {"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 rate;
+    uint16_t len;
+    uint16_t handle;
+    uint16_t num;
+
+    rc = parse_arg_all(argc - 1, argv + 1);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rate = parse_arg_uint16("rate", &rc);
+    if (rc != 0) {
+        console_printf("invalid 'rate' 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");
+    }
+
+    num = parse_arg_uint16("num", &rc);
+    if (rc != 0) {
+        console_printf("invalid 'num' parameter\n");
+        return rc;
+    }
+
+    handle = parse_arg_uint16("handle", &rc);
+    if (rc != 0) {
+        console_printf("invalid 'handle' parameter\n");
+        return rc;
+    }
+
+    rc = btshell_tx_start(handle, len, rate, num);
+    return rc;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_param test_tx_params[] = {
+    {"num", "number of packets, usage: =<UINT16>"},
+    {"length", "size of packet, usage: =<UINT16>"},
+    {"rate", "rate of tx, usage: =<UINT16>"},
+    {"handle", "handle to tx to, usage: =<UINT16>"},
+    {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: =<UINT16>"},
+    {"tx_phys_mask", "usage: =<UINT8>"},
+    {"rx_phys_mask", "usage: =<UINT8>"},
+    {"phy_opts", "usage: =<UINT16>"},
+    {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: =<UINT8>"},
+    {"rx_phys_mask", "usage: =<UINT8>"},
+    {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: =<UINT16>"},
+    {NULL, NULL}
+};
+
+static const struct shell_cmd_help phy_read_help = {
+    .summary = "read PHYs",
+    .usage = NULL,
+    .params = phy_read_params,
+};
+
+/*****************************************************************************
+ * $host-enable                                                              *
+ *****************************************************************************/
+
+static int
+cmd_host_enable(int argc, char **argv)
+{
+    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);
+    return rc;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+static const struct shell_cmd_help host_disable_help = {
+    .summary = "stop the NimBLE host",
+    .usage = NULL,
+    .params = NULL,
+};
+#endif
+
+/*****************************************************************************
+ * $gatt-discover                                                            *
+ *****************************************************************************/
+
+static const struct shell_param gatt_discover_characteristic_params[] = {
+    {"conn", "connection handle, usage: =<UINT16>"},
+    {"uuid", "discover by uuid, usage: =[UUID]"},
+    {"start", "start handle, usage: =<UINT16>"},
+    {"end", "end handle, usage: =<UINT16>"},
+    {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: =<UINT16>"},
+    {"start", "start handle, usage: =<UINT16>"},
+    {"end", "end handle, usage: =<UINT16>"},
+    {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: =<UINT16>"},
+    {"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: =<UINT16>"},
+    {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: =<UINT16>"},
+    {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: =<UINT16>"},
+    {"start", "start handle, usage: =<UINT16>"},
+    {"end", "end handle, usage: =<UINT16>"},
+    {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: =<UINT16>"},
+    {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: =<UINT16>"},
+    {"long", "is read long, usage: =[0-1], default=0"},
+    {"attr", "attribute handle, usage: =<UINT16>"},
+    {"offset", "offset value, usage: =<UINT16>"},
+    {"uuid", "read by uuid, usage: =[UUID]"},
+    {"start", "start handle, usage: =<UINT16>"},
+    {"end", "end handle, usage: =<UINT16>"},
+    {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: =<UINT16>"},
+    {"end", "end handle, usage: =<UINT16>"},
+    {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: =<UINT16>"},
+    {"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: =<UINT16>"},
+    {"no_rsp", "write without response, usage: =[0-1], default=0"},
+    {"long", "is write long, usage: =[0-1], default=0"},
+    {"attr", "attribute handle, usage: =<UINT16>"},
+    {"offset", "attribute handle, usage: =<UINT16>"},
+    {"value", "usage: =<octets>"},
+    {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: =<UINT16>"},
+    {"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: =<UINT16>"},
+    {"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: =<UINT16>"},
+    {"psm", "usage: =<UINT16>"},
+    {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: =<UINT16>"},
+    {"idx", "usage: =<UINT16>"},
+    {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-send                                                               *
+ *****************************************************************************/
+
+static const struct shell_param l2cap_send_params[] = {
+    {"conn", "connection handle, usage: =<UINT16>"},
+    {"idx", "usage: =<UINT16>"},
+    {"bytes", "number of bytes to send, usage: =<UINT16>"},
+    {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
+
+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-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 = "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
+    },
+    { NULL, NULL, NULL },
+};
+
+
+void
+cmd_init(void)
+{
+    shell_register(BTSHELL_MODULE, btshell_commands);
+    shell_register_default_module(BTSHELL_MODULE);
+}

+ 64 - 0
apps/btshell/src/cmd.h

@@ -0,0 +1,64 @@
+/*
+ * 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 <inttypes.h>
+#include "host/ble_uuid.h"
+
+struct kv_pair {
+    char *key;
+    int val;
+};
+
+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_byte_stream_exact_length(char *name, uint8_t *dst, int len);
+int parse_arg_mac(char *name, uint8_t *dst);
+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

+ 587 - 0
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 <inttypes.h>
+#include <errno.h>
+
+#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;
+}

+ 39 - 0
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

+ 261 - 0
apps/btshell/src/cmd_l2cap.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 <inttypes.h>
+#include <errno.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, &params);
+    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;
+    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;
+    }
+
+    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, accept_response);
+    if (rc) {
+        console_printf("Server create error: 0x%02x", rc);
+    }
+
+    return 0;
+}
+
+/*****************************************************************************
+ * $l2cap-connect                                                            *
+ *****************************************************************************/
+
+int
+cmd_l2cap_connect(int argc, char **argv)
+{
+    uint16_t conn = 0;
+    uint16_t psm = 0;
+    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;
+    }
+
+    return btshell_l2cap_connect(conn, psm);
+}
+
+/*****************************************************************************
+ * $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;
+}

+ 32 - 0
apps/btshell/src/cmd_l2cap.h

@@ -0,0 +1,32 @@
+/*
+ * 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);
+
+#endif

+ 595 - 0
apps/btshell/src/gatt_svr.c

@@ -0,0 +1,595 @@
+/*
+ * 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 <assert.h>
+#include <string.h>
+#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
+
+/**
+ * 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 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;
+        }
+
+    case PTS_CHR_READ_WRITE:
+    case PTS_CHR_READ_WRITE_ENC:
+    case PTS_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_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;
+        }
+
+    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;
+        }
+
+    default:
+        assert(0);
+        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;
+    }
+
+    return 0;
+}

+ 2251 - 0
apps/btshell/src/main.c

@@ -0,0 +1,2251 @@
+/*
+ * 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 <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#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_hci_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_rate;
+    uint16_t tx_handle;
+    uint16_t tx_len;
+};
+static struct btshell_tx_data_s btshell_tx_data;
+int btshell_full_disc_prev_chr_val;
+
+#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)
+{
+    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_is_empty(svc) && SLIST_EMPTY(&svc->chrs)) {
+            rc = btshell_disc_all_chrs(conn_handle, svc->svc.start_handle,
+                                       svc->svc.end_handle);
+            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_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(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(" length_data=%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);
+    console_printf("\n");
+}
+
+#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;
+    }
+
+    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,"
+                   " addr_type=%d addr=",
+                   desc->rssi, desc->tx_power, desc->prim_phy, desc->sec_phy,
+                   desc->sid, 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_gap_event(struct ble_gap_event *event, void *arg);
+
+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;
+}
+
+static int
+btshell_gap_event(struct ble_gap_event *event, void *arg)
+{
+    struct ble_gap_conn_desc desc;
+    int conn_idx;
+    int rc;
+
+    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;
+
+    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)
+{
+    int i;
+    uint8_t len;
+    int32_t timeout;
+    uint8_t *dptr;
+    struct os_mbuf *om;
+
+    if ((btshell_tx_data.tx_num == 0) || (btshell_tx_data.tx_len == 0)) {
+        return;
+    }
+
+    len = btshell_tx_data.tx_len;
+
+    om = NULL;
+    if (os_msys_num_free() >= 4) {
+        om = os_msys_get_pkthdr(len + 4, sizeof(struct ble_mbuf_hdr));
+    }
+
+    if (om) {
+        /* Put the HCI header in the mbuf */
+        om->om_len = len + 4;
+        put_le16(om->om_data, btshell_tx_data.tx_handle);
+        put_le16(om->om_data + 2, len);
+        dptr = om->om_data + 4;
+
+        /*
+         * NOTE: first byte gets 0xff so not confused with l2cap channel.
+         * The rest of the data gets filled with incrementing pattern starting
+         * from 0.
+         */
+        put_le16(dptr, len - 4);
+        dptr[2] = 0xff;
+        dptr[3] = 0xff;
+        dptr += 4;
+        len -= 4;
+
+        for (i = 0; i < len; ++i) {
+            *dptr = i;
+            ++dptr;
+        }
+
+        /* Set packet header length */
+        OS_MBUF_PKTHDR(om)->omp_len = om->om_len;
+        ble_hci_trans_hs_acl_tx(om);
+
+        --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_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 *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;
+        ediv = value_sec.ediv;
+        rand_val = value_sec.rand_num;
+        auth = value_sec.authenticated;
+    }
+
+    rc = ble_gap_encryption_initiate(conn_handle, 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 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_rate = rate;
+    btshell_tx_data.tx_len = len;
+    btshell_tx_data.tx_handle = handle;
+
+    os_callout_reset(&btshell_tx_timer, 0);
+
+    return 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)
+{
+    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);
+
+    ble_l2cap_recv_ready(chan, sdu);
+}
+
+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;
+    }
+
+    ble_l2cap_recv_ready(chan, sdu_rx);
+
+    return 0;
+}
+
+static int
+btshell_l2cap_event(struct ble_l2cap_event *event, void *arg)
+{
+    int accept_response;
+
+    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;
+            }
+
+            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,
+                           ble_l2cap_get_scid(event->connect.chan),
+                           ble_l2cap_get_dcid(event->connect.chan),
+                           ble_l2cap_get_our_mtu(event->connect.chan),
+                           ble_l2cap_get_peer_mtu(event->connect.chan));
+
+            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: 0x%08lx\n",
+                           (uint32_t) 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;
+        default:
+            return 0;
+    }
+}
+#endif
+
+int
+btshell_l2cap_create_srv(uint16_t psm, 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
+
+    return ble_l2cap_create_server(psm, BTSHELL_COC_MTU, btshell_l2cap_event,
+                                   INT_TO_PTR(accept_response));
+#endif
+}
+
+int
+btshell_l2cap_connect(uint16_t conn_handle, uint16_t psm)
+{
+#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;
+
+    sdu_rx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0);
+    assert(sdu_rx != NULL);
+
+    return ble_l2cap_connect(conn_handle, psm, BTSHELL_COC_MTU, 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_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;
+    }
+
+    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) {
+        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;
+}

+ 162 - 0
apps/btshell/src/misc.c

@@ -0,0 +1,162 @@
+/*
+ * 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 "
+                   "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);
+}
+
+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);
+    }
+}

+ 587 - 0
apps/btshell/src/parse.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 <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <assert.h>
+#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;
+}
+
+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_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_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;
+}

+ 42 - 0
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
+
+    # Newtmgr is not supported in this app, so disable newtmgr-over-shell.
+    SHELL_NEWTMGR: 0
+
+syscfg.vals.BLE_MESH:
+    MSYS_1_BLOCK_COUNT: 16

+ 41 - 0
apps/ext_advertiser/pkg.yml

@@ -0,0 +1,41 @@
+# 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/boot/bootutil"
+    - "@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"

+ 464 - 0
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 <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#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 (&params, 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, &params, 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 (&params, 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, &params, 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 (&params, 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, &params, 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 (&params, 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, &params, 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 (&params, 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, &params, 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;
+}

+ 186 - 0
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
+};

+ 45 - 0
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

+ 71 - 0
apps/ibeacon/src/ibeacon.c

@@ -0,0 +1,71 @@
+
+#include "os/os.h"
+#include "nimble/hci_common.h"
+#include "host/ble_hs_hci.h"
+#include "ble_hs_priv.h"
+
+#include <rtthread.h>
+
+static void ble_app_set_addr(void)
+{
+    ble_addr_t addr;
+    int rc;
+
+    rc = ble_hs_id_gen_rnd(1, &addr);
+    RT_ASSERT(rc == 0);
+
+    rc = ble_hs_id_set_rnd(addr.val);
+    RT_ASSERT(rc == 0);
+}
+
+static void ble_app_advertise(void)
+{
+    struct ble_gap_adv_params adv_params;
+    uint8_t uuid128[16];
+    int rc;
+    int8_t tx_pwr;
+
+    /* Arbitrarily set the UUID to a string of 0x11 bytes. */
+    memset(uuid128, 0x11, sizeof uuid128);
+
+    rc = ble_hs_hci_util_read_adv_tx_pwr(&tx_pwr);
+    RT_ASSERT(rc == 0);
+
+    /* Major version=2; minor version=10. */
+    rc = ble_ibeacon_set_adv_data(uuid128, 2, 10, tx_pwr);
+    RT_ASSERT(rc == 0);
+
+    /* Begin advertising. */
+    adv_params = (struct ble_gap_adv_params){ 0 };
+    rc = ble_gap_adv_start(BLE_OWN_ADDR_RANDOM, NULL, BLE_HS_FOREVER,
+                           &adv_params, NULL, NULL);
+    RT_ASSERT(rc == 0);
+}
+
+static void ble_app_on_sync(void)
+{
+    /* Generate a non-resolvable private address. */
+    ble_app_set_addr();
+
+    /* Advertise indefinitely. */
+    ble_app_advertise();
+}
+
+extern int nimble_ble_enable(void);
+
+static int ble_ibeacon(void)
+{
+    static int init_flag = 0;
+
+    if (init_flag)
+        return 0;
+    init_flag = 1;
+
+    ble_hs_cfg.sync_cb = ble_app_on_sync;
+
+    /* Enable bluetooth stack */
+    nimble_ble_enable();
+
+    return 0;
+}
+MSH_CMD_EXPORT_ALIAS(ble_ibeacon, ble_ibeacon, "bluetoooth ibeacon");

+ 5 - 0
docs/.gitignore

@@ -0,0 +1,5 @@
+xml
+node_modules
+_build
+doxygen_*
+*.pyc

+ 25 - 0
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

+ 33 - 0
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/

+ 22 - 0
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:

+ 14 - 0
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:

+ 15 - 0
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:

+ 15 - 0
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:

+ 27 - 0
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 <ble_hs_return_codes>
+   GAP <ble_gap>
+   GATT Client <ble_gattc>
+   GATT Server <ble_gatts>
+   Identity <ble_hs_id>
+   ATT <ble_att>

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä