Browse Source

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

EvalZero 7 years ago
parent
commit
ebf4924934
100 changed files with 23097 additions and 4 deletions
  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.
 
       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
       the brackets!)  The text should be enclosed in the appropriate
       comment syntax for the file format. We also recommend that a
@@ -186,7 +186,7 @@
       same "printed page" as the copyright notice for easier
       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");
    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.
    See the License for the specific language governing permissions and
    limitations under the License.
+
+This product bundles queue.h 8.5, which is available under the "3-clause BSD"
+license.  For details, see porting/nimble/include/os/queue.h
+
+This product partly derives from FreeBSD, which is available under the
+"3-clause BSD" license.  For details, see:
+    * porting/nimble/src/os_mbuf.c
+
+This product bundles Gary S. Brown's CRC32 implementation, which is available
+under the following license:
+    COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
+    code or tables extracted from it, as desired without restriction.
+
+This product bundles tinycrypt, which is available under the "3-clause BSD"
+license. For details, and bundled files see:
+    * ext/tinycrypt/LICENSE

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

Some files were not shown because too many files changed in this diff